/* * Author: Jon Trulson * 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 #include #include #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= '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); // cmdarg<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> 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" // (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; }