hm11: Initial implementation

This module implements support for the Grove BLE (Bluetooth Low
Energy) device.  It is implemented as a UART device accepting an "AT"
command set.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Zion Orent <zorent@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
This commit is contained in:
Jon Trulson 2015-04-15 14:09:54 -06:00 committed by Mihai Tudor Panu
parent 4ca399845f
commit d776edbab2
9 changed files with 699 additions and 0 deletions

View File

@ -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})

123
examples/c++/hm11.cxx Normal file
View File

@ -0,0 +1,123 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* 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 <unistd.h>
#include <iostream>
#include <signal.h>
#include <stdio.h>
#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;
}

136
examples/javascript/hm11.js Normal file
View File

@ -0,0 +1,136 @@
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */
/*
* Author: Zion Orent <zorent@ics.com>
* 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);
});

110
examples/python/hm11.py Normal file
View File

@ -0,0 +1,110 @@
#!/usr/bin/python
# Author: Zion Orent <zorent@ics.com>
# 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

5
src/hm11/CMakeLists.txt Normal file
View File

@ -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()

154
src/hm11/hm11.cxx Normal file
View File

@ -0,0 +1,154 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* 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 <iostream>
#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;
}

143
src/hm11/hm11.h Normal file
View File

@ -0,0 +1,143 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* 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 <string>
#include <iostream>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <mraa/uart.h>
#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;
};
}

12
src/hm11/jsupm_hm11.i Normal file
View File

@ -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);

13
src/hm11/pyupm_hm11.i Normal file
View File

@ -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);