upm/src/rn2903/rn2903.c
Jon Trulson 21a1860ec2 rn2903: Initial implementation; C; C++ wraps C; examples
This requires a new MRAA with UART sendbreak support.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
2017-05-09 13:44:12 -06:00

1164 lines
31 KiB
C

/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2017 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 <string.h>
#include <ctype.h>
#include <assert.h>
#include "rn2903.h"
#include "upm_utilities.h"
#include "upm_platform.h"
// we use small buffers of this size to build certain compound
// commands
#define RN2903_CMD_BUFFER_32B (32) // 32 bytes
// maximum number of autobaud retries
#define RN2903_AUTOBAUD_RETRIES (10)
// some useful macros to save on typing and text wrapping
#undef _SHIFT
#define _SHIFT(x) (_RN2903_##x##_SHIFT)
#undef _MASK
#define _MASK(x) (_RN2903_##x##_MASK)
#undef _SHIFTMASK
#define _SHIFTMASK(x) (_MASK(x) << _SHIFT(x))
// disable printf to stdout if on Zephyr, and stdout isn't available.
#if defined(UPM_PLATFORM_ZEPHYR) && !defined(CONFIG_STDOUT_CONSOLE)
# define printf printk
#endif
static bool validate_hex_str(const char *hex)
{
assert(hex != NULL);
int len = strlen(hex);
if ((len % 2) != 0)
{
printf("%s: strlen(hex) must be a multiple of 2\n",
__FUNCTION__);
return false;
}
for (int i=0; i<len; i++)
{
if ( !((hex[i] >= '0' && hex[i] <= '9') ||
(tolower(hex[i]) >= 'a' && tolower(hex[i]) <= 'f')) )
{
printf("%s: invalid hex character at position %d\n",
__FUNCTION__, i);
return false;
}
}
return true;
}
static rn2903_context _rn2903_preinit()
{
// make sure MRAA is initialized
int mraa_rv;
if ((mraa_rv = mraa_init()) != MRAA_SUCCESS)
{
printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv);
return NULL;
}
rn2903_context dev =
(rn2903_context)malloc(sizeof(struct _rn2903_context));
if (!dev)
return NULL;
// zero out context
memset((void *)dev, 0, sizeof(struct _rn2903_context));
// first response wait time
dev->cmd_resp_wait_ms = RN2903_DEFAULT_RESP_DELAY;
// optional second response wait time
dev->cmd_resp2_wait_ms = RN2903_DEFAULT_RESP2_DELAY;
// init stored baudrate to RN2903_DEFAULT_BAUDRATE
dev->baudrate = RN2903_DEFAULT_BAUDRATE;
// uncomment for "early" debugging
// dev->debug = true;
return dev;
}
static rn2903_context _rn2903_postinit(rn2903_context dev,
unsigned int baudrate)
{
assert(dev != NULL);
if (rn2903_set_baudrate(dev, baudrate))
{
printf("%s: rn2903_set_baudrate() failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
if (rn2903_set_flow_control(dev, RN2903_FLOW_CONTROL_NONE))
{
printf("%s: rn2903_set_flow_control() failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
// turn off debugging
rn2903_set_debug(dev, false);
// reset the device
if (rn2903_reset(dev))
{
printf("%s: rn2903_reset() failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
// now get and store our hardware EUI
if (rn2903_command(dev, "sys get hweui"))
{
printf("%s: rn2903_command(sys get hweui) failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
strncpy(dev->hardware_eui, dev->resp_data, RN2903_MAX_HEX_EUI64);
return dev;
}
// uart init
rn2903_context rn2903_init(unsigned int uart, unsigned int baudrate)
{
rn2903_context dev;
if (!(dev = _rn2903_preinit()))
return NULL;
// initialize the MRAA context
// uart, default should be 8N1
if (!(dev->uart = mraa_uart_init(uart)))
{
printf("%s: mraa_uart_init() failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
return _rn2903_postinit(dev, baudrate);
}
// uart tty init
rn2903_context rn2903_init_tty(const char *uart_tty, unsigned int baudrate)
{
rn2903_context dev;
if (!(dev = _rn2903_preinit()))
return NULL;
// initialize the MRAA context
// uart, default should be 8N1
if (!(dev->uart = mraa_uart_init_raw(uart_tty)))
{
printf("%s: mraa_uart_init_raw() failed.\n", __FUNCTION__);
rn2903_close(dev);
return NULL;
}
return _rn2903_postinit(dev, baudrate);
}
void rn2903_close(rn2903_context dev)
{
assert(dev != NULL);
if (dev->to_hex_buf)
free(dev->to_hex_buf);
if (dev->from_hex_buf)
free(dev->from_hex_buf);
if (dev->uart)
mraa_uart_stop(dev->uart);
free(dev);
}
int rn2903_read(const rn2903_context dev, char *buffer, size_t len)
{
assert(dev != NULL);
// uart
return mraa_uart_read(dev->uart, buffer, len);
}
int rn2903_write(const rn2903_context dev, const char *buffer, size_t len)
{
assert(dev != NULL);
int rv = mraa_uart_write(dev->uart, buffer, len);
mraa_uart_flush(dev->uart);
return rv;
}
bool rn2903_data_available(const rn2903_context dev, unsigned int millis)
{
assert(dev != NULL);
if (mraa_uart_data_available(dev->uart, millis))
return true;
else
return false;
}
upm_result_t rn2903_set_baudrate(const rn2903_context dev,
unsigned int baudrate)
{
assert(dev != NULL);
if (dev->debug)
printf("%s: Setting baudrate to %d\n", __FUNCTION__,
baudrate);
if (mraa_uart_set_baudrate(dev->uart, baudrate))
{
printf("%s: mraa_uart_set_baudrate() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
dev->baudrate = baudrate;
if (!rn2903_autobaud(dev, RN2903_AUTOBAUD_RETRIES))
{
printf("%s: rn2903_autobaud detection failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
return UPM_SUCCESS;
}
void rn2903_set_debug(const rn2903_context dev, bool enable)
{
assert(dev != NULL);
dev->debug = enable;
}
void rn2903_set_response_wait_time(const rn2903_context dev,
unsigned int wait_time)
{
assert(dev != NULL);
dev->cmd_resp_wait_ms = wait_time;
}
void rn2903_set_response2_wait_time(const rn2903_context dev,
unsigned int wait_time)
{
assert(dev != NULL);
dev->cmd_resp2_wait_ms = wait_time;
}
void rn2903_drain(const rn2903_context dev)
{
assert(dev != NULL);
char resp[RN2903_MAX_BUFFER];
int rv;
while (rn2903_data_available(dev, 0))
{
rv = rn2903_read(dev, resp, RN2903_MAX_BUFFER);
if (rv < 0)
{
printf("%s: read failed\n", __FUNCTION__);
return;
}
// printf("%s: Tossed %d bytes\n", __FUNCTION__, rv);
}
return;
}
RN2903_RESPONSE_T rn2903_waitfor_response(const rn2903_context dev,
int wait_ms)
{
assert(dev != NULL);
memset(dev->resp_data, 0, RN2903_MAX_BUFFER);
dev->resp_len = 0;
upm_clock_t clock;
upm_clock_init(&clock);
uint32_t elapsed = 0;
do
{
if (rn2903_data_available(dev, 1))
{
int rv = rn2903_read(dev, &(dev->resp_data[dev->resp_len]), 1);
if (rv < 0)
return RN2903_RESPONSE_UPM_ERROR;
// discard CR's
if (dev->resp_data[dev->resp_len] == '\r')
continue;
// got a LF, we are done - discard and finish
if (dev->resp_data[dev->resp_len] == '\n')
{
dev->resp_data[dev->resp_len] = 0;
break;
}
// too much data?
if (dev->resp_len >= RN2903_MAX_BUFFER - 1)
break;
dev->resp_len++;
}
} while ( (elapsed = upm_elapsed_ms(&clock)) < wait_ms);
if (dev->debug)
printf("\tRESP (%d): '%s'\n", (int)dev->resp_len,
(dev->resp_len) ? dev->resp_data : "");
// check for and return obvious errors
if (elapsed >= wait_ms)
return RN2903_RESPONSE_TIMEOUT;
else if (rn2903_find(dev, RN2903_PHRASE_INV_PARAM))
return RN2903_RESPONSE_INVALID_PARAM;
else
return RN2903_RESPONSE_OK; // either data or "ok"
}
RN2903_RESPONSE_T rn2903_command(const rn2903_context dev, const char *cmd)
{
assert(dev != NULL);
assert(cmd != NULL);
rn2903_drain(dev);
if (dev->debug)
printf("CMD: '%s'\n", cmd);
if (rn2903_write(dev, cmd, strlen(cmd)) < 0)
{
printf("%s: rn2903_write(cmd) failed\n", __FUNCTION__);
return RN2903_RESPONSE_UPM_ERROR;
}
// now write the termination string (CR/LF)
if (rn2903_write(dev, RN2903_PHRASE_TERM, RN2903_PHRASE_TERM_LEN) < 0)
{
printf("%s: rn2903_write(TERM) failed\n", __FUNCTION__);
return RN2903_RESPONSE_UPM_ERROR;
}
return rn2903_waitfor_response(dev, dev->cmd_resp_wait_ms);
}
RN2903_RESPONSE_T rn2903_command_with_arg(const rn2903_context dev,
const char *cmd, const char *arg)
{
assert(dev != NULL);
assert(cmd != NULL);
assert(arg != NULL);
// cmd<space>arg<0-terminator>
int buflen = strlen(cmd) + 1 + strlen(arg) + 1;
char buf[buflen];
memset(buf, 0, buflen);
snprintf(buf, buflen, "%s %s", cmd, arg);
return rn2903_command(dev, buf);
}
const char *rn2903_get_response(const rn2903_context dev)
{
assert(dev != NULL);
return dev->resp_data;
}
size_t rn2903_get_response_len(const rn2903_context dev)
{
assert(dev != NULL);
return dev->resp_len;
}
const char *rn2903_to_hex(const rn2903_context dev, const char *src, int len)
{
assert(dev != NULL);
assert(src != NULL);
static const char hdigits[16] = "0123456789ABCDEF";
// first free previous destination hex buffer if allocated
if (dev->to_hex_buf)
{
free(dev->to_hex_buf);
dev->to_hex_buf = NULL;
}
if (len == 0)
return NULL;
int dlen = (len * 2) + 1;
if (!(dev->to_hex_buf = malloc(dlen)))
{
printf("%s: malloc(%d) failed\n", __FUNCTION__, dlen);
return NULL;
}
memset(dev->to_hex_buf, 0, dlen);
char *dptr = dev->to_hex_buf;
char *sptr = (char *)src;
for (int i=0; i<len; i++)
{
*dptr++ = hdigits[(sptr[i] >> 4) & 0x0f];
*dptr++ = hdigits[(sptr[i] & 0x0f)];
}
// the memset() will have ensured the last byte is 0
return dev->to_hex_buf;
}
const char *rn2903_from_hex(const rn2903_context dev,
const char *src)
{
assert(dev != NULL);
assert(src != NULL);
// first free previous destination hex buffer if allocated
if (dev->from_hex_buf)
{
free(dev->from_hex_buf);
dev->from_hex_buf = NULL;
}
int len = strlen(src);
if (len == 0)
return NULL;
if (!validate_hex_str(src))
return NULL;
// add a byte for 0 termination, just in case we're decoding a
// string
int dlen = (len / 2) + 1;
if (!(dev->from_hex_buf = malloc(dlen)))
{
printf("%s: malloc(%d) failed\n", __FUNCTION__, dlen);
return NULL;
}
memset(dev->from_hex_buf, 0, dlen);
char *dptr = dev->from_hex_buf;
for (int i=0; i<(dlen - 1); i++)
{
char tbuf[3] = { src[i*2], src[(i*2)+1], 0 };
*dptr++ = (char)strtol(tbuf, NULL, 16);
}
return dev->from_hex_buf;
}
const char *rn2903_get_hardware_eui(const rn2903_context dev)
{
assert(dev != NULL);
return dev->hardware_eui;
}
upm_result_t rn2903_update_mac_status(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac get status"))
{
printf("%s: rn2903_command(mac get status) failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
// make sure we actually got a hex value of 4 bytes
if (!validate_hex_str(dev->resp_data) || dev->resp_len != 4)
{
printf("%s: invalid mac status.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
// convert it
const char *statPtr = rn2903_from_hex(dev, dev->resp_data);
if (!statPtr)
{
printf("%s: from_hex conversion failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
// now play pointer games. We should have 2 bytes which we'll
// turn into a uint16_t (LE), which we will then stuff into our
// mac_status_word field in the context.
uint16_t status16 = (statPtr[0] << 8) | statPtr[1];
// store the mac_status_word, then decode the actual mac_status
// (state) enumeration
dev->mac_status_word = status16;
dev->mac_mac_status =
(RN2903_MAC_STATUS_T)((status16 & _SHIFTMASK(MAC_STATUS_MAC_STATUS))
>> _SHIFT(MAC_STATUS_MAC_STATUS));
return UPM_SUCCESS;
}
uint16_t rn2903_get_mac_status_word(const rn2903_context dev)
{
assert(dev != NULL);
return dev->mac_status_word;
}
RN2903_MAC_STATUS_T rn2903_get_mac_status(const rn2903_context dev)
{
assert(dev != NULL);
return dev->mac_mac_status;
}
upm_result_t rn2903_reset(const rn2903_context dev)
{
assert(dev != NULL);
rn2903_autobaud(dev, RN2903_AUTOBAUD_RETRIES);
if (rn2903_command(dev, "sys reset"))
{
// this command will reset the baudrate back to the default if
// we changed it previously. We do not report an error here,
// if we are not using the default baudrate, since we've now
// switched to a different baudrate than we had, and cannot
// read the response anyway.
if (dev->baudrate == RN2903_DEFAULT_BAUDRATE)
return UPM_ERROR_OPERATION_FAILED;
}
// to be safe, always set the baudrate after a reset
if (rn2903_set_baudrate(dev, dev->baudrate))
return UPM_ERROR_OPERATION_FAILED;
upm_delay_ms(100);
return UPM_SUCCESS;
}
RN2903_JOIN_STATUS_T rn2903_join(const rn2903_context dev,
RN2903_JOIN_TYPE_T type)
{
assert(dev != NULL);
// first, do a couple of initial checks...
// get the mac status and ensure that 1) we are not already
// joined, 2) the mac status is idle, 3) we have not been
// silenced, and 4) MAC has not been paused.
if (rn2903_update_mac_status(dev))
{
printf("%s: rn2903_update_mac_status() failed\n", __FUNCTION__);
return RN2903_JOIN_STATUS_UPM_ERROR;
}
// if the radio is not idle, we aren't going anywhere
RN2903_MAC_STATUS_T mac_status = rn2903_get_mac_status(dev);
if (mac_status != RN2903_MAC_STAT_IDLE)
return RN2903_JOIN_STATUS_BUSY;
// now check the rest of the status bits...
uint16_t status = rn2903_get_mac_status_word(dev);
if (status & RN2903_MAC_STATUS_JOINED)
return RN2903_JOIN_STATUS_ALREADY_JOINED;
else if (status & RN2903_MAC_STATUS_SILENT)
return RN2903_JOIN_STATUS_SILENT;
else if (status & RN2903_MAC_STATUS_PAUSED)
return RN2903_JOIN_STATUS_MAC_PAUSED;
// so far, so good... now build the command
char cmd[RN2903_CMD_BUFFER_32B] = {};
snprintf(cmd, RN2903_CMD_BUFFER_32B, "mac join %s",
(type == RN2903_JOIN_TYPE_OTAA) ? "otaa" : "abp");
// run the command. We will get two responses back - one
// immediately if there is an error or if the join operation was
// successfully submitted to the radio for transmission, and
// another indicating whether the join was granted, or failed.
// ABP joins will always succeed immediately.
RN2903_RESPONSE_T rv;
if ((rv = rn2903_command(dev, cmd)))
{
// a failure of some sort. We've already screened for most of
// them, but there are a couple that we can't detect until we
// try (UPM).
printf("%s: join command failed (%d).\n", __FUNCTION__, rv);
return RN2903_JOIN_STATUS_UPM_ERROR;
}
// if we are here, then we either got an "ok" or another error we
// couldn't screen for. Check for them.
if (rn2903_find(dev, "no_free_ch"))
return RN2903_JOIN_STATUS_NO_CHAN;
else if (rn2903_find(dev, "keys_not_init"))
return RN2903_JOIN_STATUS_BAD_KEYS;
// now we wait awhile for another response indicating whether the
// join request was accepted or not
if ((rv = rn2903_waitfor_response(dev, dev->cmd_resp2_wait_ms)))
{
printf("%s: join second response failed (%d).\n", __FUNCTION__, rv);
return RN2903_JOIN_STATUS_UPM_ERROR;
}
if (rn2903_find(dev, "denied"))
return RN2903_JOIN_STATUS_DENIED;
else if (rn2903_find(dev, "accepted"))
return RN2903_JOIN_STATUS_ACCEPTED;
// if it's anything else, we failed :(
printf("%s: unexpected response to join request.\n", __FUNCTION__);
return RN2903_JOIN_STATUS_UPM_ERROR;
}
upm_result_t rn2903_set_flow_control(const rn2903_context dev,
RN2903_FLOW_CONTROL_T fc)
{
assert(dev != NULL);
mraa_result_t rv = MRAA_SUCCESS;
switch(fc)
{
case RN2903_FLOW_CONTROL_NONE:
rv = mraa_uart_set_flowcontrol(dev->uart, false, false);
break;
case RN2903_FLOW_CONTROL_HARD:
rv = mraa_uart_set_flowcontrol(dev->uart, false, true);
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
if (rv == MRAA_SUCCESS)
return UPM_SUCCESS;
else
return UPM_ERROR_OPERATION_FAILED;
}
bool rn2903_find(const rn2903_context dev, const char *str)
{
assert(dev != NULL);
assert(str != NULL);
return ((strstr(dev->resp_data, str) == dev->resp_data) ? true : false);
}
upm_result_t rn2903_set_device_eui(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (16 bytes for the 8 byte EUI)
if (!validate_hex_str(str) || strlen(str) != 16)
{
printf("%s: invalid 16 byte device EUI hex string.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set deveui", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_get_device_eui(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac get deveui"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_set_application_eui(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (16 bytes for the 8 byte EUI)
if (!validate_hex_str(str) || strlen(str) != 16)
{
printf("%s: invalid 16 byte application EUI hex string.\n",
__FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set appeui", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_get_application_eui(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac get appeui"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_set_application_key(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (32 bytes for the 16 byte key)
if (!validate_hex_str(str) || strlen(str) != 32)
{
printf("%s: invalid 32 byte application key hex string.\n",
__FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set appkey", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_get_application_key(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac get appkey"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_set_device_addr(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (8 bytes for the 4 byte key)
if (!validate_hex_str(str) || strlen(str) != 8)
{
printf("%s: invalid 8 byte device addr hex string.\n",
__FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set devaddr", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_get_device_addr(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac get devaddr"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_set_network_session_key(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (32 bytes for the 16 byte key)
if (!validate_hex_str(str) || strlen(str) != 32)
{
printf("%s: invalid 32 byte network session key hex string.\n",
__FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set nwkskey", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_set_application_session_key(const rn2903_context dev,
const char *str)
{
assert(dev != NULL);
assert(str != NULL);
// first verify that the string is a valid hex string of the right
// size (32 bytes for the 16 byte key)
if (!validate_hex_str(str) || strlen(str) != 32)
{
printf("%s: invalid 32 byte application session key hex string.\n",
__FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
if (rn2903_command_with_arg(dev, "mac set appskey", str))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_mac_save(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac save"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_mac_pause(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac pause"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
upm_result_t rn2903_mac_resume(const rn2903_context dev)
{
assert(dev != NULL);
if (rn2903_command(dev, "mac resume"))
return UPM_ERROR_OPERATION_FAILED;
return UPM_SUCCESS;
}
RN2903_MAC_TX_STATUS_T rn2903_mac_tx(const rn2903_context dev,
RN2903_MAC_MSG_TYPE_T type,
int port, const char *payload)
{
assert(dev != NULL);
assert(payload != NULL);
// check some things
// port can only be between 1 and 223
if (port < 1 || port > 223)
{
printf("%s: port must be between 1 and 223\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
// make sure payload is a valid hex string
if (!validate_hex_str(payload))
{
printf("%s: payload is not a valid hex string\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
// get the mac status and ensure that 1) we are joined to a
// LoRaWAN network, 2) the mac status is idle, 3) we have not been
// silenced, and 4) MAC has not been paused.
if (rn2903_update_mac_status(dev))
{
printf("%s: rn2903_update_mac_status() failed\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
// if the radio is not idle, we aren't going anywhere
RN2903_MAC_STATUS_T mac_status = rn2903_get_mac_status(dev);
if (mac_status != RN2903_MAC_STAT_IDLE)
return RN2903_MAC_TX_STATUS_BUSY;
// now check the rest of the status bits...
uint16_t status = rn2903_get_mac_status_word(dev);
if (!(status & RN2903_MAC_STATUS_JOINED))
return RN2903_MAC_TX_STATUS_NOT_JOINED;
else if (status & RN2903_MAC_STATUS_SILENT)
return RN2903_MAC_TX_STATUS_SILENT;
else if (status & RN2903_MAC_STATUS_PAUSED)
return RN2903_MAC_TX_STATUS_MAC_PAUSED;
// good so far, build and send the command. Then we check for
// more things.
char cmd[RN2903_CMD_BUFFER_32B] = {};
snprintf(cmd, RN2903_CMD_BUFFER_32B, "mac tx %s %d",
(type == RN2903_MAC_MSG_TYPE_CONFIRMED) ? "cnf" : "uncnf",
port);
RN2903_RESPONSE_T rv;
if ((rv = rn2903_command_with_arg(dev, cmd, payload)))
{
printf("%s: mac tx command failed (%d).\n", __FUNCTION__, rv);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
// now check for some other things we couldn't check before
if (rn2903_find(dev, "no_free_ch"))
return RN2903_MAC_TX_STATUS_NO_CHAN;
else if (rn2903_find(dev, "frame_counter_err_rejoin_needed"))
return RN2903_MAC_TX_STATUS_FC_NEED_REJOIN;
else if (rn2903_find(dev, "invalid_data_len"))
return RN2903_MAC_TX_STATUS_BAD_DATA_LEN;
// now we wait for transmission to complete, and a possible
// downlink packet.
if ((rv = rn2903_waitfor_response(dev, dev->cmd_resp2_wait_ms)))
{
printf("%s: mac tx second response failed (%d).\n", __FUNCTION__, rv);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
if (rn2903_find(dev, "mac_tx_ok"))
return RN2903_MAC_TX_STATUS_TX_OK;
else if (rn2903_find(dev, "mac_err"))
return RN2903_MAC_TX_STATUS_MAC_ERR;
else if (rn2903_find(dev, "invalid_data_len"))
return RN2903_MAC_TX_STATUS_BAD_DATA_LEN;
else if (rn2903_find(dev, "mac_rx"))
return RN2903_MAC_TX_STATUS_RX_RECEIVED; // we got a downlink
// packet in the
// response buffer
// if it's anything else, we failed :(
printf("%s: unexpected response to mac tx command\n", __FUNCTION__);
return RN2903_JOIN_STATUS_UPM_ERROR;
}
RN2903_RESPONSE_T rn2903_radio_tx(const rn2903_context dev,
const char *payload)
{
assert(dev != NULL);
assert(payload != NULL);
// check some things
// make sure payload is a valid hex string
if (!validate_hex_str(payload))
{
printf("%s: payload is not a valid hex string\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
// get the mac status and ensure that we are paused
if (rn2903_update_mac_status(dev))
{
printf("%s: rn2903_update_mac_status() failed\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
uint16_t status = rn2903_get_mac_status_word(dev);
if (!(status & RN2903_MAC_STATUS_PAUSED))
{
printf("%s: MAC must be paused first\n", __FUNCTION__);
return RN2903_RESPONSE_UPM_ERROR;
}
// good so far, build and send the command
RN2903_RESPONSE_T rv;
if ((rv = rn2903_command_with_arg(dev, "radio tx", payload)))
{
printf("%s: radio tx command failed (%d).\n", __FUNCTION__, rv);
return rv;
}
// we're done
return RN2903_RESPONSE_OK;
}
RN2903_RESPONSE_T rn2903_radio_rx(const rn2903_context dev,
int window_size)
{
assert(dev != NULL);
// check some things
// get the mac status and ensure that we are paused
if (rn2903_update_mac_status(dev))
{
printf("%s: rn2903_update_mac_status() failed\n", __FUNCTION__);
return RN2903_MAC_TX_STATUS_UPM_ERROR;
}
uint16_t status = rn2903_get_mac_status_word(dev);
if (!(status & RN2903_MAC_STATUS_PAUSED))
{
printf("%s: MAC must be paused first\n", __FUNCTION__);
return RN2903_RESPONSE_UPM_ERROR;
}
// good so far, build and send the command
char cmd[RN2903_CMD_BUFFER_32B] = {};
snprintf(cmd, RN2903_CMD_BUFFER_32B, "radio rx %d", window_size);
RN2903_RESPONSE_T rv;
if ((rv = rn2903_command(dev, cmd)))
{
printf("%s: radio tx command failed (%d).\n", __FUNCTION__, rv);
return rv;
}
// now, wait for and return the second response
return rn2903_waitfor_response(dev, dev->cmd_resp2_wait_ms);
}
upm_result_t rn2903_mac_set_battery(const rn2903_context dev, int level)
{
assert(dev != NULL);
if (level < 0 || level > 255)
{
printf("%s: level must be between 0 and 255\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
char cmd[RN2903_CMD_BUFFER_32B] = {};
snprintf(cmd, RN2903_CMD_BUFFER_32B, "mac set bat %d", level);
RN2903_RESPONSE_T rv;
if ((rv = rn2903_command(dev, cmd)))
{
printf("%s: mac tx command failed (%d).\n", __FUNCTION__, rv);
return UPM_ERROR_OPERATION_FAILED;
}
return UPM_SUCCESS;
}
bool rn2903_autobaud(const rn2903_context dev, int retries)
{
assert(dev != NULL);
do
{
// trigger rn2903 auto-baud detection
// send a break signal, then a 0x55, then try a command
mraa_result_t rv;
if ((rv = mraa_uart_sendbreak(dev->uart, 0)))
{
// we don't want to fail here if break not implemented or
// supported
if (rv != MRAA_ERROR_FEATURE_NOT_IMPLEMENTED &&
rv != MRAA_ERROR_FEATURE_NOT_SUPPORTED)
{
printf("%s: mraa_uart_sendbreak() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
}
upm_delay_ms(100);
// The magic autobaud detection character
char buf = 0x55;
rn2903_write(dev, &buf, 1);
upm_delay_ms(100);
// try a command to verify speed
if (!rn2903_command(dev, "sys get ver"))
break;
if (dev->debug)
printf("%s: RETRIES %d: FAIL!\n", __FUNCTION__, retries);
} while (retries-- > 0);
if (retries <= 0)
return false;
if (dev->debug)
printf("%s: RETRIES %d: success!\n", __FUNCTION__, retries);
return true;
}
const char *rn2903_get_radio_rx_payload(const rn2903_context dev)
{
assert(dev != NULL);
// first make sure we have the right data in the response buffer.
// The response buffer should contain "radio_rx<sp><sp><hex_payload>"
// (note the two spaces between radio_rx and the payload.)
if (rn2903_find(dev, "radio_rx") || dev->resp_len > 10)
return &(dev->resp_data[10]);
return NULL;
}