diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index aab4a2bf..930536ef 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -117,6 +117,7 @@ add_executable (isd1820-example isd1820.cxx) add_executable (sx6119-example sx6119.cxx) add_executable (si114x-example si114x.cxx) add_executable (maxsonarez-example maxsonarez.cxx) +add_executable (hm11-example hm11.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -212,6 +213,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/isd1820) include_directories (${PROJECT_SOURCE_DIR}/src/sx6119) include_directories (${PROJECT_SOURCE_DIR}/src/si114x) include_directories (${PROJECT_SOURCE_DIR}/src/maxsonarez) +include_directories (${PROJECT_SOURCE_DIR}/src/hm11) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -330,3 +332,4 @@ target_link_libraries (isd1820-example isd1820 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (sx6119-example sx6119 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (si114x-example si114x ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (maxsonarez-example maxsonarez ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (hm11-example hm11 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/c++/hm11.cxx b/examples/c++/hm11.cxx new file mode 100644 index 00000000..f266fca6 --- /dev/null +++ b/examples/c++/hm11.cxx @@ -0,0 +1,123 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "hm11.h" + +using namespace std; +using namespace upm; + +void printUsage(char *progname) +{ + cout << "Usage: " << progname << " [AT command]" << endl; + cout << endl; + + cout << "If an argument is supplied on the command line, that argument is" + << endl; + cout << "sent to the module and the response is printed out." << endl; + cout << endl; + cout << "If no argument is used, then the address and PIN of the module" + << endl; + cout << "are queried and the results printed out." << endl; + cout << endl; + cout << endl; +} + +// simple helper function to send a command and wait for a response +void sendCommand(upm::HM11* ble, char *cmd) +{ + char buffer[BUFSIZ]; + ble->writeData(cmd, strlen(cmd)); + + // wait up to 1 second + if (ble->dataAvailable(1000)) + { + memset(buffer, 0, BUFSIZ); + + ble->readData(buffer, BUFSIZ); + cout << "Returned: " << buffer << endl; + } + else + { + cerr << "Timed out waiting for response" << endl; + } +} + + +int main (int argc, char **argv) +{ +//! [Interesting] + char buffer[BUFSIZ]; + // Instantiate a HM11 BLE Module on UART 0 + + upm::HM11* ble = new upm::HM11(0); + + // make sure port is initialized properly. 9600 baud is the default. + if (!ble->setupTty(B9600)) + { + cerr << "Failed to setup tty port parameters" << endl; + return 1; + } + + printUsage(argv[0]); + + if (argc > 1) + { + cout << "Sending command line argument (" << argv[1] << ")..." << endl; + sendCommand(ble, argv[1]); + } + else + { + // query the module address + char addr[] = "AT+ADDR?"; + cout << "Querying module address (" << addr << ")..." << endl; + sendCommand(ble, addr); + + sleep(1); + + // query the module address + char pin[] = "AT+PASS?"; + cout << "Querying module PIN (" << pin << ")..." << endl; + sendCommand(ble, pin); + + // Other potentially useful commands are: + // + // AT+VERS? - query module version + // AT+ROLE0 - set as slave + // AT+ROLE1 - set as master + // AT+CLEAR - clear all previous settings + // AT+RESET - restart the device + // + // A comprehensive list is available from the datasheet at: + // http://www.seeedstudio.com/wiki/images/c/cd/Bluetooth4_en.pdf + } + +//! [Interesting] + + delete ble; + return 0; +} diff --git a/examples/javascript/hm11.js b/examples/javascript/hm11.js new file mode 100644 index 00000000..326f61ea --- /dev/null +++ b/examples/javascript/hm11.js @@ -0,0 +1,136 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* +* Author: Zion Orent +* Copyright (c) 2015 Intel Corporation. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +/************** Variables **************/ +// normal read/write mode +var bufferLength = 256; + +var ble = require('jsupm_hm11'); + + +/************** Functions **************/ +function printUsage(progname) +{ + var outputStr = "Usage: " + progname + " [AT command]\n\n" + + "If an argument is supplied on the command line, that argument is\n" + + "sent to the module and the response is printed out.\n\n" + + "If no argument is used, then the address and PIN of the module\n" + + "are queried and the results printed out.\n\n" + console.log(outputStr); +} + +// simple helper function to send a command and wait for a response +function sendCommand(bleObj, cmd, callback) +{ + var bleBuffer = new ble.charArray(bufferLength); + bleObj.writeData(cmd, cmd.length); + + // wait up to 1 second + if (bleObj.dataAvailable(1000)) + { + bleObj.readData(bleBuffer, bufferLength); + var bleData = ""; + // read only the number of characters + // specified by myGPSSensor.readData + for (var x = 0; x < bufferLength; x++) + { + if (bleBuffer.getitem(x) == '\0') + break; + else + bleData += bleBuffer.getitem(x); + } + console.log(bleData); + } + else + console.log("Timed out waiting for response"); + if (callback) + callback(); +} + +/************** Main code **************/ +// Instantiate a HM11 BLE Module on UART 0 +var my_ble_obj = new ble.HM11(0); + +// make sure port is initialized properly. 9600 baud is the default. +if (!my_ble_obj.setupTty(ble.int_B9600)) +{ + console.log("Failed to setup tty port parameters"); + process.exit(0); +} + +printUsage(process.argv[1]); + +// Note: in nodeJS, command-line argument 0 is "node". +// Command-line argument 1 is "hm11.js" +// If you have a third argument, then it's a command for BLE +if (process.argv.length > 2) +{ + console.log("Sending command line argument (" + process.argv[2] + ")..."); + sendCommand(my_ble_obj, process.argv[2]); +} +else +{ + // query the module address + var addr = "AT+ADDR?"; + console.log("Querying module address (" + addr + ")..."); + + // sending this command as a synchronous callback ensures better timing + var callbackFunc = function() + { + setTimeout(function() + { + // query the module address + var pin = "AT+PASS?"; + console.log("Querying module PIN (" + pin + ")..."); + sendCommand(my_ble_obj, pin); + + // Other potentially useful commands are: + // + // AT+VERS? - query module version + // AT+ROLE0 - set as slave + // AT+ROLE1 - set as master + // AT+CLEAR - clear all previous settings + // AT+RESET - restart the device + // + // A comprehensive list is available from the datasheet at: + // http://www.seeedstudio.com/wiki/images/c/cd/Bluetooth4_en.pdf + }, 1000); + }; + sendCommand(my_ble_obj, addr, callbackFunc); +} + + +/************** Exit code **************/ +process.on('SIGINT', function() +{ + my_ble_obj = null; + ble.cleanUp(); + ble = null; + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/python/hm11.py b/examples/python/hm11.py new file mode 100644 index 00000000..c20a8ead --- /dev/null +++ b/examples/python/hm11.py @@ -0,0 +1,110 @@ +#!/usr/bin/python +# Author: Zion Orent +# Copyright (c) 2015 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time, sys, signal, atexit +import pyupm_hm11 as upmHm11 + +# Instantiate a HM11 BLE Module on UART 0 +my_ble_obj = upmHm11.HM11(0) + + +## Exit handlers ## +# This stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This function lets you run code on exit, +# including functions from my_ble_obj +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + + +bufferLength = 256 + +# make sure port is initialized properly. 9600 baud is the default. +if (not my_ble_obj.setupTty(upmHm11.cvar.int_B9600)): + print "Failed to setup tty port parameters" + sys.exit(0) + + +usageStr = ("Usage:\n" +"Pass a commandline argument (any argument) to this program\n" +"to query the radio configuration and output it. NOTE: the\n" +"radio must be in CONFIG mode for this to work.\n\n" +"Running this program without arguments will simply transmit\n" +"'Hello World!' every second, and output any data received from\n" +"another radio.\n\n") +print usageStr + +# simple helper function to send a command and wait for a response +def sendCommand(bleObj, cmd): + bleBuffer = upmHm11.charArray(bufferLength) + bleObj.writeData(cmd, len(cmd)) + + # wait up to 1 second + if (bleObj.dataAvailable(1000)): + bleObj.readData(bleBuffer, bufferLength) + bleData = "" + # read only the number of characters + # specified by myGPSSensor.readData + for x in range(0, bufferLength): + if (bleBuffer.__getitem__(x) == '\0'): + break + else: + bleData += bleBuffer.__getitem__(x) + print bleData + else: + print "Timed out waiting for response" + + +if (len(sys.argv) > 1): + print "Sending command line argument (" + sys.argv[1] + ")..." + sendCommand(my_ble_obj, sys.argv[1]) +else: + # query the module address + addr = "AT+ADDR?"; + print "Querying module address (" + addr + ")..." + + sendCommand(my_ble_obj, addr) + time.sleep(1) + # query the module address + pin = "AT+PASS?"; + print "Querying module PIN (" + pin + ")..." + sendCommand(my_ble_obj, pin) + + # Other potentially useful commands are: + # + # AT+VERS? - query module version + # AT+ROLE0 - set as slave + # AT+ROLE1 - set as master + # AT+CLEAR - clear all previous settings + # AT+RESET - restart the device + # + # A comprehensive list is available from the datasheet at: + # http://www.seeedstudio.com/wiki/images/c/cd/Bluetooth4_en.pdf + diff --git a/src/hm11/CMakeLists.txt b/src/hm11/CMakeLists.txt new file mode 100644 index 00000000..6e9fb87e --- /dev/null +++ b/src/hm11/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "hm11") +set (libdescription "upm grove hm11 bluetooth low energy module") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/hm11/hm11.cxx b/src/hm11/hm11.cxx new file mode 100644 index 00000000..5569f6f6 --- /dev/null +++ b/src/hm11/hm11.cxx @@ -0,0 +1,154 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "hm11.h" + +using namespace upm; +using namespace std; + +static const int defaultDelay = 100; // max wait time for read + +HM11::HM11(int uart) +{ + m_ttyFd = -1; + + if ( !(m_uart = mraa_uart_init(uart)) ) + { + cerr << __FUNCTION__ << ": mraa_uart_init() failed" << endl; + return; + } + + // This requires a recent MRAA (1/2015) + char *devPath = mraa_uart_get_dev_path(m_uart); + + if (!devPath) + { + cerr << __FUNCTION__ << ": mraa_uart_get_dev_path() failed" << endl; + return; + } + + // now open the tty + if ( (m_ttyFd = open(devPath, O_RDWR)) == -1) + { + cerr << __FUNCTION__ << ": open of " << devPath << " failed: " + << strerror(errno) << endl; + return; + } +} + +HM11::~HM11() +{ + if (m_ttyFd != -1) + close(m_ttyFd); +} + +bool HM11::dataAvailable(unsigned int millis) +{ + if (m_ttyFd == -1) + return false; + + struct timeval timeout; + + // no waiting + timeout.tv_sec = 0; + timeout.tv_usec = millis * 1000; + + int nfds; + fd_set readfds; + + FD_ZERO(&readfds); + + FD_SET(m_ttyFd, &readfds); + + if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0) + return true; // data is ready + else + return false; +} + +int HM11::readData(char *buffer, size_t len) +{ + if (m_ttyFd == -1) + return(-1); + + int rv = read(m_ttyFd, buffer, len); + + if (rv < 0) + cerr << __FUNCTION__ << ": read failed: " << strerror(errno) << endl; + + return rv; +} + +int HM11::writeData(char *buffer, size_t len) +{ + if (m_ttyFd == -1) + return(-1); + + // first, flush any pending but unread input + + tcflush(m_ttyFd, TCIFLUSH); + + int rv = write(m_ttyFd, buffer, len); + + if (rv < 0) + { + cerr << __FUNCTION__ << ": write failed: " << strerror(errno) << endl; + return rv; + } + + tcdrain(m_ttyFd); + + return rv; +} + +bool HM11::setupTty(speed_t baud) +{ + if (m_ttyFd == -1) + return(false); + + struct termios termio; + + // get current modes + tcgetattr(m_ttyFd, &termio); + + // setup for a 'raw' mode. 81N, no echo or special character + // handling, such as flow control. + cfmakeraw(&termio); + + // set our baud rates + cfsetispeed(&termio, baud); + cfsetospeed(&termio, baud); + + // make it so + if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0) + { + cerr << __FUNCTION__ << ": tcsetattr failed: " << strerror(errno) << endl; + return false; + } + + return true; +} + diff --git a/src/hm11/hm11.h b/src/hm11/hm11.h new file mode 100644 index 00000000..30a6a12f --- /dev/null +++ b/src/hm11/hm11.h @@ -0,0 +1,143 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Thanks to Adafruit for supplying a google translated version of the + * Chinese datasheet and some clues in their code. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HM11_DEFAULT_UART 0 + +namespace upm { + /** + * @brief UPM library for the HM-11 Bluetooth 4.0 Low Energy Module + * @defgroup hm11 libupm-hm11 + * @ingroup seeed serial wifi + */ + + /** + * @library hm11 + * @sensor hm11 + * @comname HM-11 Bluetooth 4.0 Low Energy Module + * @altname HM-10, HM-12 + * @category wifi + * @manufacturer seeed + * @connection uart + * @web http://www.seeedstudio.com/wiki/images/c/cd/Bluetooth4_en.pdf + * + * @brief C++ API HM-11 4.0 Bluetooth Low Energy Module + * + * The driver was tested with the Grove BLE module. Its an HM-11 + * BLE 4.0 module based on a TI CC2541 chip. It operates using a + * standard 'AT' command set. See the datasheet for a full list + * of available commands and their possible responses. + * + * http://www.seeedstudio.com/wiki/images/c/cd/Bluetooth4_en.pdf + * + * It is connected via a UART at 9600 baud. + * + * @snippet hm11.cxx Interesting + */ + + class HM11 { + public: + + /** + * HM11 module constructor + * + * @param uart default uart to use (0 or 1) + */ + HM11(int uart); + + /** + * HM11 module Destructor + */ + ~HM11(); + + /** + * check to see if there is data available for reading + * + * @param millis number of milliseconds to wait, 0 means no wait. + * @return true if there is data available to be read + */ + bool dataAvailable(unsigned int millis); + + /** + * read any available data into a user-supplied buffer. Note, the + * call will block until data is available to be read. Use + * dataAvailable() to determine whether there is data available + * beforehand, to avoid blocking. + * + * @param buffer the buffer to hold the data read + * @param len the length of the buffer + * @return the number of bytes read + */ + int readData(char *buffer, size_t len); + + /** + * write the data in buffer to the device + * + * @param buffer the buffer to hold the data read + * @param len the length of the buffer + * @return the number of bytes written + */ + int writeData(char *buffer, size_t len); + + /** + * setup the proper tty i/o modes and the baudrate. The default + * baud rate is 9600 (B9600) for this device. + * + * @param baud the desired baud rate. + * @return true if successful + */ + bool setupTty(speed_t baud=B9600); + + + protected: + int ttyFd() { return m_ttyFd; }; + int setTtyFd(int fd) { m_ttyFd = fd; }; + + private: + mraa_uart_context m_uart; + int m_ttyFd; + }; +} + + diff --git a/src/hm11/jsupm_hm11.i b/src/hm11/jsupm_hm11.i new file mode 100644 index 00000000..4f02b7a2 --- /dev/null +++ b/src/hm11/jsupm_hm11.i @@ -0,0 +1,12 @@ +%module jsupm_hm11 +%include "../upm.i" +%include "carrays.i" + +%{ + #include "hm11.h" + speed_t int_B9600 = B9600; +%} + +%include "hm11.h" +speed_t int_B9600 = B9600; +%array_class(char, charArray); diff --git a/src/hm11/pyupm_hm11.i b/src/hm11/pyupm_hm11.i new file mode 100644 index 00000000..05169e4e --- /dev/null +++ b/src/hm11/pyupm_hm11.i @@ -0,0 +1,13 @@ +%module pyupm_hm11 +%include "../upm.i" +%include "carrays.i" + +%feature("autodoc", "3"); + +%{ + #include "hm11.h" + speed_t int_B9600 = B9600; +%} +%include "hm11.h" +speed_t int_B9600 = B9600; +%array_class(char, charArray);