nmea_gps: add support for ubloc I2c reading of NMEA data

These changes allow NMEA data to be read via I2C on UBLOX compliant
devices that support this capability, such as the LEA-6H based GPS
shield from DFRobot.

It adds a new init() function to the C code, and a new constructor to
the C++ code. It also adds 5 new examples for C, C++, Javascript,
Python, and Java.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
This commit is contained in:
Jon Trulson 2016-08-26 16:34:13 -06:00 committed by Noel Eck
parent d456066277
commit 4a8ddc10ad
13 changed files with 526 additions and 11 deletions

View File

@ -317,3 +317,4 @@ if (OPENZWAVE_FOUND)
add_custom_example (aeotecdsb09104-example aeotecdsb09104.cxx ozw)
add_custom_example (tzemt400-example tzemt400.cxx ozw)
endif()
add_custom_example (nmea_gps_i2c_example-cxx nmea_gps_i2c.cxx nmea_gps)

View File

@ -0,0 +1,69 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 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 "nmea_gps.hpp"
using namespace std;
bool shouldRun = true;
const size_t bufferLength = 128;
void sig_handler(int signo)
{
if (signo == SIGINT)
shouldRun = false;
}
int main()
{
signal(SIGINT, sig_handler);
//! [Interesting]
// Instantiate a NMEA_GPS UBLOX based i2c sensor on i2c bus 0 at
// address 0x42
upm::NMEAGPS *sensor = new upm::NMEAGPS(0, 0x42);
// loop, dumping NMEA data out as fast as it comes in
while (shouldRun)
{
if (sensor->dataAvailable(0))
usleep(100);
else
cout << sensor->readStr(bufferLength);
}
//! [Interesting]
cout << "Exiting" << endl;
delete sensor;
return 0;
}

View File

@ -92,3 +92,6 @@ add_example (mma7361)
add_example (bh1750)
add_example (urm37)
add_example (urm37-uart)
# Custom examples
add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps)

82
examples/c/nmea_gps_i2c.c Normal file
View File

@ -0,0 +1,82 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 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 <signal.h>
#include "nmea_gps.h"
bool shouldRun = true;
const size_t bufferLength = 128;
void sig_handler(int signo)
{
if (signo == SIGINT)
shouldRun = false;
}
int main()
{
signal(SIGINT, sig_handler);
//! [Interesting]
// Instantiate a NMEA_GPS UBLOX based i2c sensor on i2c bus 0 at
// address 0x42
nmea_gps_context sensor = nmea_gps_init_ublox_i2c(0, 0x42);
if (!sensor)
{
printf("nmea_gps_init_ublox_i2c() failed.\n");
return 1;
}
char buffer[bufferLength];
int rv = 0;
// loop, dumping NMEA data out as fast as it comes in
while (shouldRun)
{
if (!nmea_gps_data_available(sensor, 0))
usleep(100);
else
{
if ((rv = nmea_gps_read(sensor, buffer, bufferLength)) >= 0)
{
int i;
for (i=0; i<rv; i++)
printf("%c", buffer[i]);
}
}
}
//! [Interesting]
printf("Exiting\n");
nmea_gps_close(sensor);
return 0;
}

View File

@ -153,4 +153,4 @@ if (OPENZWAVE_FOUND)
add_example_with_path(AeotecDSB09104_Example ozw ozw)
add_example_with_path(TZEMT400_Example ozw ozw)
endif()
add_example_with_path(NMEAGPS_I2C_Example nmea_gps nmea_gps)

View File

@ -0,0 +1,49 @@
/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 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 upm_nmea_gps.NMEAGPS;
public class NMEAGPS_I2C_Example
{
public static void main(String[] args) throws InterruptedException
{
// ! [Interesting]
System.out.println("Initializing...");
// Instantiate a NMEA_GPS UBLOX based i2c sensor on i2c bus 0 at
// address 0x42
NMEAGPS sensor = new NMEAGPS(0, (byte)0x42);
// loop, dumping NMEA data out as fast as it comes in
while (true)
{
if (sensor.dataAvailable(0))
System.out.print(sensor.readStr(256));
else
Thread.sleep(100);
}
// ! [Interesting]
}
}

View File

@ -0,0 +1,60 @@
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */
/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 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.
*/
var sensorObj = require('jsupm_nmea_gps');
function sleepFor(millis)
{
var now = new Date().getTime();
while(new Date().getTime() < now + millis)
{
/* do nothing */
}
}
// Instantiate a NMEA_GPS UBLOX based i2c sensor on i2c bus 0 at
// address 0x42
var sensor = new sensorObj.NMEAGPS(0, 0x42);
// loop, dumping NMEA data out as fast as it comes in
while (true)
{
if (sensor.dataAvailable(0))
process.stdout.write(sensor.readStr(256));
else
sleepFor(10);
}
// exit on ^C
process.on('SIGINT', function()
{
sensor = null;
sensorObj.cleanUp();
sensorObj = null;
console.log("Exiting.");
process.exit(0);
});

View File

@ -0,0 +1,52 @@
#!/usr/bin/python
# Author: Jon Trulson <jtrulson@ics.com>
# Copyright (c) 2016 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_nmea_gps as sensorObj
# Instantiate a NMEA_GPS UBLOX based i2c sensor on i2c bus 0 at
# address 0x42
sensor = sensorObj.NMEAGPS(0, 0x42)
## Exit handlers ##
# This function 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
def exitHandler():
print "Exiting"
sys.exit(0)
# Register exit handlers
atexit.register(exitHandler)
signal.signal(signal.SIGINT, SIGINTHandler)
# Every second, sample the NMEAGPS and output the measured lux value
# loop, dumping NMEA data out as fast as it comes in
while (True):
if (sensor.dataAvailable(0)):
sys.stdout.write(sensor.readStr(256))
else:
time.sleep(.1)

View File

@ -1,6 +1,7 @@
%module javaupm_nmea_gps
%include "../upm.i"
%include "std_string.i"
%include "stdint.i"
%include "typemaps.i"
%include "nmea_gps.hpp"

View File

@ -29,6 +29,45 @@
#include "upm_utilities.h"
// For ublox6 and compatible I2C communications (see the u-blox6
// Receiver Description Protocol Specification datasheet for details).
#define UBLOX6_I2C_BYTES_AVAIL_H 0xfd
#define UBLOX6_I2C_BYTES_AVAIL_L 0xfe
#define UBLOX6_I2C_BYTE_STREAM 0xff
#define UBLOX6_I2C_BYTE_NONE 0xff // read if no data avail
// static helpers for i2c reading
static uint8_t readReg(const nmea_gps_context dev, uint8_t reg)
{
assert(dev != NULL);
assert(dev->i2c != NULL);
int rv;
if ((rv = mraa_i2c_read_byte_data(dev->i2c, reg)) < 0)
printf("%s: mraa_i2c_read_byte_data() failed.\n", __FUNCTION__);
// This will return 0xff on errors, which is invalid NMEA data anyway.
return (uint8_t)rv;
}
static int readRegs(const nmea_gps_context dev, uint8_t reg,
uint8_t *buffer, int len)
{
assert(dev != NULL);
assert(dev->i2c != NULL);
int rv;
if ((rv = mraa_i2c_read_bytes_data(dev->i2c, reg, buffer, len)) < 0)
{
printf("%s: mraa_i2c_read_bytes_data() failed.\n", __FUNCTION__);
}
return rv;
}
// uart init
nmea_gps_context nmea_gps_init(unsigned int uart, unsigned int baudrate,
int enable_pin)
{
@ -42,6 +81,7 @@ nmea_gps_context nmea_gps_init(unsigned int uart, unsigned int baudrate,
memset((void *)dev, 0, sizeof(struct _nmea_gps_context));
dev->uart = NULL;
dev->i2c = NULL;
dev->gpio_en = NULL;
// initialize the MRAA contexts
@ -82,6 +122,51 @@ nmea_gps_context nmea_gps_init(unsigned int uart, unsigned int baudrate,
return dev;
}
// i2c ublox init
nmea_gps_context nmea_gps_init_ublox_i2c(unsigned int bus, uint8_t addr)
{
nmea_gps_context dev =
(nmea_gps_context)malloc(sizeof(struct _nmea_gps_context));
if (!dev)
return NULL;
// zero out context
memset((void *)dev, 0, sizeof(struct _nmea_gps_context));
dev->uart = NULL;
dev->i2c = NULL;
dev->gpio_en = NULL;
// initialize the MRAA contexts
if (!(dev->i2c = mraa_i2c_init(bus)))
{
printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__);
nmea_gps_close(dev);
return NULL;
}
// This device cannot operate at more than 100Khz, so we set that
// here and bail if it fails.
if (mraa_i2c_frequency(dev->i2c, MRAA_I2C_STD))
{
printf("%s: mraa_i2c_frequency(MRAA_I2C_STD) failed.\n", __FUNCTION__);
nmea_gps_close(dev);
return NULL;
}
if (mraa_i2c_address(dev->i2c, addr))
{
printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__);
nmea_gps_close(dev);
return NULL;
}
return dev;
}
void nmea_gps_close(nmea_gps_context dev)
{
assert(dev != NULL);
@ -91,6 +176,8 @@ void nmea_gps_close(nmea_gps_context dev)
if (dev->uart)
mraa_uart_stop(dev->uart);
if (dev->i2c)
mraa_i2c_stop(dev->i2c);
if (dev->gpio_en)
mraa_gpio_close(dev->gpio_en);
@ -116,6 +203,35 @@ int nmea_gps_read(const nmea_gps_context dev, char *buffer, size_t len)
{
assert(dev != NULL);
// i2c ublox
if (dev->i2c)
{
int rv;
if ((rv = readRegs(dev, UBLOX6_I2C_BYTE_STREAM, buffer, len)) < 0)
return rv;
// now we need to go through the bytes returned, and stop
// counting "real" bytes when we hit any character with the high
// bit set. The documentation implies that only a 0xff will be
// sent when no new data is available, but it seems sometimes
// the return contains 0xbf and 0xc3 bytes. So we stop counting
// as soon as we see any "8 bit" character (which isn't allowed
// by NMEA anyway).
int realBytes = 0;
int i;
for (i=0; i<rv; i++)
{
if (buffer[i] & 0x80)
break;
realBytes++;
}
return realBytes;
}
// uart
return mraa_uart_read(dev->uart, buffer, len);
}
@ -123,6 +239,9 @@ int nmea_gps_write(const nmea_gps_context dev, char *buffer, size_t len)
{
assert(dev != NULL);
if (!dev->uart)
return UPM_ERROR_NO_RESOURCES;
return mraa_uart_write(dev->uart, buffer, len);
}
@ -130,6 +249,24 @@ bool nmea_gps_data_available(const nmea_gps_context dev, unsigned int millis)
{
assert(dev != NULL);
// i2c ublox
if (dev->i2c)
{
// here millis is ignored
uint8_t h, l;
h = readReg(dev, UBLOX6_I2C_BYTES_AVAIL_H);
l = readReg(dev, UBLOX6_I2C_BYTES_AVAIL_L);
uint16_t total = (h << 8) | l;
// 0 means no data available, 0xffff means read errors
if (total == 0x0000 || total == 0xffff)
return false;
else
return true;
}
// uart
if (mraa_uart_data_available(dev->uart, millis))
return true;
else
@ -141,6 +278,9 @@ upm_result_t nmea_gps_set_baudrate(const nmea_gps_context dev,
{
assert(dev != NULL);
if (!dev->uart)
return UPM_ERROR_NO_RESOURCES;
if (mraa_uart_set_baudrate(dev->uart, baudrate))
{
printf("%s: mraa_uart_set_baudrate() failed.\n", __FUNCTION__);

View File

@ -31,7 +31,7 @@ using namespace upm;
using namespace std;
NMEAGPS::NMEAGPS(unsigned int uart, unsigned int baudrate,
int enable_pin) :
int enable_pin) :
m_nmea_gps(nmea_gps_init(uart, baudrate, enable_pin))
{
if (!m_nmea_gps)
@ -39,6 +39,14 @@ NMEAGPS::NMEAGPS(unsigned int uart, unsigned int baudrate,
+ ": nmea_gps_init() failed");
}
NMEAGPS::NMEAGPS(unsigned int bus, uint8_t addr) :
m_nmea_gps(nmea_gps_init_ublox_i2c(bus, addr))
{
if (!m_nmea_gps)
throw std::runtime_error(string(__FUNCTION__)
+ ": nmea_gps_init() failed");
}
NMEAGPS::~NMEAGPS()
{
nmea_gps_close(m_nmea_gps);

View File

@ -27,6 +27,7 @@
#include <stdint.h>
#include "upm.h"
#include "mraa/uart.h"
#include "mraa/i2c.h"
#include "mraa/gpio.h"
#ifdef __cplusplus
@ -39,7 +40,16 @@ extern "C" {
* This driver was tested with a number of GPS devices that emit
* NMEA data via a serial interface of some sort (typically a UART).
*
* The I2C capablity was tested with a UBLOX LEA-6H based GPS shield
* from DFRobot. Currently, the I2C capability is only supported
* for UBLOX devices (or compatibles) that conform to the
* specifications outlined in the u-blox6 Receiver Description
* Protocol Specification, Chapter 4, DDC Port.
*
* An example using the UART.
* @snippet nmea_gps.c Interesting
* An example using I2C.
* @snippet nmea_gps_i2c.c Interesting
*/
/**
@ -48,10 +58,11 @@ extern "C" {
typedef struct _nmea_gps_context {
mraa_uart_context uart;
mraa_gpio_context gpio_en;
mraa_i2c_context i2c;
} *nmea_gps_context;
/**
* NMEA_GPS Initializer
* NMEA_GPS Initializer for generic UART operation
*
* @param uart Specify which uart to use.
* @param baudrate Specify the baudrate to use. The device defaults
@ -63,6 +74,16 @@ extern "C" {
nmea_gps_context nmea_gps_init(unsigned int uart, unsigned int baudrate,
int enable_pin);
/**
* NMEA_GPS Initializer for UBLOX I2C operation
*
* @param bus Specify which the I2C bus to use.
* @param addr Specify the I2C address to use. For UBLOX devices,
* this typically defaults to 0x42.
* @return an initialized device context on success, NULL on error.
*/
nmea_gps_context nmea_gps_init_ublox_i2c(unsigned int bus, uint8_t addr);
/**
* NMEA_GPS sensor close function
*/
@ -79,7 +100,8 @@ extern "C" {
int nmea_gps_read(const nmea_gps_context dev, char *buffer, size_t len);
/**
* Write character data to the device.
* Write character data to the device. This is only valid for a
* UART device.
*
* @param dev sensor context
* @param buffer The character buffer containing data to write.
@ -101,7 +123,7 @@ extern "C" {
/**
* Set the baudrate of the device. By default, nmea_gps_init() will
* set the baudrate to 9600.
* set the baudrate to 9600. This is only valid for UART devices.
*
* @param dev sensor context
* @param baudrate The baud rate to set for the device.
@ -111,7 +133,11 @@ extern "C" {
unsigned int baudrate);
/**
* Determine whether there is data available to be read.
* Determine whether there is data available to be read. In the
* case of a UART, this function will wait up to "millis"
* milliseconds for data to become available. In the case of an I2C
* device, the millis argument is ignored and the function will
* return immediately, indicating whether data is available.
*
* @param dev sensor context
* @param millis The number of milliseconds to wait for data to

View File

@ -52,14 +52,23 @@ namespace upm {
* This driver was tested with a number of GPS devices that emit
* NMEA data via a serial interface of some sort (typically a UART).
*
* The I2C capablity was tested with a UBLOX LEA-6H based GPS shield
* from DFRobot. Currently, the I2C capability is only supported
* for UBLOX devices (or compatibles) that conform to the
* specifications outlined in the u-blox6 Receiver Description
* Protocol Specification, Chapter 4, DDC Port.
*
* An example using the UART.
* @snippet nmea_gps.cxx Interesting
* An example using I2C.
* @snippet nmea_gps_i2c.cxx Interesting
*/
class NMEAGPS {
public:
/**
* NMEAGPS object constructor
* NMEAGPS object constructor for a UART
*
* @param uart Specify which uart to use.
* @param baudrate Specify the baudrate to use. The device defaults
@ -68,7 +77,16 @@ namespace upm {
* -1 to not use an enable pin.
*/
NMEAGPS(unsigned int uart, unsigned int baudrate,
int enable_pin);
int enable_pin);
/**
* NMEAGPS object constructor for a UBLOX I2C interface
*
* @param bus Specify which the I2C bus to use.
* @param addr Specify the I2C address to use. For UBLOX devices,
* this typically defaults to 0x42.
*/
NMEAGPS(unsigned int bus, uint8_t addr);
/**
* NMEAGPS object destructor
@ -84,7 +102,8 @@ namespace upm {
std::string readStr(size_t size);
/**
* Write character data to the device.
* Write character data to the device. This is only valid for a
* UART device.
*
* @param buffer The string containing the data to write.
* @return The number of bytes written.
@ -102,14 +121,19 @@ namespace upm {
/**
* Set the baudrate of the device. By default, the constructor
* will set the baudrate to 9600.
* will set the baudrate to 9600. This is only valid for UART
* devices.
*
* @param baudrate The baud rate to set for the device.
*/
void setBaudrate(unsigned int baudrate);
/**
* Determine whether there is data available to be read.
* Determine whether there is data available to be read. In the
* case of a UART, this function will wait up to "millis"
* milliseconds for data to become available. In the case of an I2C
* device, the millis argument is ignored and the function will
* return immediately, indicating whether data is available.
*
* @param millis The number of milliseconds to wait for data to
* become available.