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

@ -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.