/* Copyright (c) 2025 - Jacob Eva (Liberated Embedded Systems) * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include #include #include "librnode.h" #include "libs/framing.h" #include "libs/serial.h" #include "libs/logging/log.h" #include "libs/util.h" #include "libs/slip_enc.h" #include "libs/eeprom.h" #include "libs/zip.h" // flashing #include "libs/flashers/nrf/dfu.h" #include "libs/flashers/nrf/uart_drv.h" #include "libs/flashers/esp32/esputil.h" /* Handles incoming RNode communications from serial * Scope: private * Returns: * > 0 - number of bytes read from port * -1 - generic error */ int rnode_handle_resp(struct RNode* rn, bool* in_frame, uint8_t* cmd, uint8_t* cmd_buf, uint8_t* cmd_buf_l, uint16_t* frame_len) { int len; bool escape = false; uint8_t sbyte; uint8_t resp_buf[RESP_BUF_SIZE] = {0}; len = read_port(rn->fd, resp_buf, RESP_BUF_SIZE); if (len > 0) { log_trace("Read %d bytes", len); for (int i = 0; i < len; i++) { log_trace("Byte: %02x", resp_buf[i]); if (!(*in_frame) && resp_buf[i] == FEND) { *in_frame = true; *cmd = CMD_UNKNOWN; sbyte = 0; *cmd_buf_l = 0; } else if (*in_frame) { if (*frame_len == 0) { *cmd = resp_buf[i]; (*frame_len)++; continue; } else { sbyte = resp_buf[i]; } if (sbyte == FEND && *cmd == CMD_ROM_READ) { //rn->product = cmd_buf[0]; //rn->model = cmd_buf[1]; //rn->hw_rev = cmd_buf[2]; //memcpy(rn->serial, cmd_buf+3, SERIAL_SIZE); //memcpy(rn->made, cmd_buf+3+SERIAL_SIZE, MADE_SIZE); //memcpy(rn->checksum, cmd_buf+3+SERIAL_SIZE+MADE_SIZE, CHECKSUM_SIZE); //memcpy(rn->signature, cmd_buf+3+SERIAL_SIZE+MADE_SIZE, CHECKSUM_SIZE); memcpy(rn->r_eeprom, cmd_buf, EEPROM_SIZE); *in_frame = false; *frame_len = 0; *cmd = CMD_UNKNOWN; continue; } else if (sbyte == FEND) { *in_frame = false; *frame_len = 0; *cmd = CMD_UNKNOWN; *cmd_buf_l = 0; continue; } else if (*cmd == CMD_ROM_READ) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; } } else if (*cmd == CMD_DETECT && sbyte == DETECT_RESP) { rn->connected = true; } else if (*cmd == CMD_PLATFORM) { rn->platform = sbyte; } else if (*cmd == CMD_MCU) { rn->mcu = sbyte; } else if (*cmd == CMD_FW_VERSION) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; if (*cmd_buf_l == 2) { rn->fw_ver = cmd_buf[0] + cmd_buf[1] / 100.0; *cmd_buf_l = 0; } } } else if (*cmd == CMD_INTERFACES) { cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; if (*cmd_buf_l == 2) { rn->interfaces[cmd_buf[0]] = cmd_buf[1]; *cmd_buf_l = 0; } } else if (*cmd == CMD_FREQUENCY) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; if (*cmd_buf_l == 4) { rn->freq = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3]; *cmd_buf_l = 0; } } } else if (*cmd == CMD_BANDWIDTH) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; if (*cmd_buf_l == 4) { rn->bw = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3]; *cmd_buf_l = 0; } } } else if (*cmd == CMD_TXPOWER) { rn->txp = sbyte; } else if (*cmd == CMD_SF) { rn->sf = sbyte; } else if (*cmd == CMD_CR) { rn->cr = sbyte; } else if (*cmd == CMD_ST_ALOCK) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; } if (*cmd_buf_l == 2) { float at = cmd_buf[0] << 8 | cmd_buf[1]; rn->st_alock = at / 100.0; *cmd_buf_l = 0; } } else if (*cmd == CMD_LT_ALOCK) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; } if (*cmd_buf_l == 2) { float at = cmd_buf[0] << 8 | cmd_buf[1]; rn->lt_alock = at / 100.0; *cmd_buf_l = 0; } } else if (*cmd == CMD_RADIO_STATE) { rn->int_state[rn->sel_int] = sbyte; } else if (*cmd == CMD_ROM_READ) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; } } else if (*cmd == CMD_BT_PIN) { if (sbyte == FESC) { escape = true; } else { if (escape) { if (sbyte == TFEND) { sbyte = FEND; } else if (sbyte == TFESC) { sbyte = FESC; } escape = false; } cmd_buf[*cmd_buf_l] = sbyte; (*cmd_buf_l)++; } if (*cmd_buf_l == 4) { rn->bt_pairing_pin = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3]; *cmd_buf_l = 0; } } (*frame_len)++; } } } return len; } /* Detects an RNode * Scope: private * Returns: * 0 - success * -1 - generic error */ int rnode_detect(struct RNode* rn) { int err_code; uint8_t tx_buf[16]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[16]){FEND, CMD_DETECT, DETECT_REQ, FEND, FEND, CMD_FW_VERSION, 0x00, FEND, FEND, CMD_PLATFORM, 0x00, FEND, FEND, CMD_MCU, 0x00, FEND}, 16*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 16); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); return (err_code == -1) ? -1 : rn->connected; } else { return -1; } } /* Establishes communication with an RNode * Params: port (e.g. /dev/ttyACM0), baud rate (e.g. 115200), detect (attempt * to communicate with RNode), force_detect (fail if cannot communicate with * RNode) * Scope: public * Returns: * 0 - success * -1 - generic error */ int rnode_init(struct RNode* rn, char* port, uint32_t baud, bool detect, bool force_detect) { rn->port = port; rn->baud = baud; rn->fd = open_port(rn->port, baud); if (rn->fd > 0) { if (detect) { if (rnode_detect(rn)) { log_info("RNode detected successfully on port %s", port); } else { if (force_detect) { log_error("RNode could not be detected on port %s", port); return -1; } else { log_info("RNode could not be detected on port %s, this isn't a problem.", port); } } } else { return 0; } } else { return -1; } return 0; } /* Resets an RNode * Scope: public * Returns: * 0 - success * -1 - generic error */ int rnode_reset(struct RNode* rn) { uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_RESET, CMD_RESET_BYTE, FEND}, 4*sizeof(uint8_t)); return write_port(rn->fd, tx_buf, 4); } /* Sets an RNode's platform attribute manually * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_platform(struct RNode* rn, uint8_t platform) { rn->platform = platform; return 0; } /* Enable / disable Bluetooth on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_bt(struct RNode* rn, uint8_t val) { int err_code; if (val == BT_ON) { log_info("Enabling Bluetooth..."); } else if (val == BT_PAIRING) { log_info("Starting Bluetooth pairing..."); } else { log_info("Disabling Bluetooth..."); } uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_BT_CTRL, val, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Attempt to retrieve the BT pairing pin of an RNode. Blocking function, * should be run in a separate thread. * Params: timeout (in ms, minimum value 200) * Scope: public * Returns * > 0 - bluetooth pairing code * 0 - success * -1 - generic error * -4 - rnode did not respond */ int rnode_get_bt_pin(struct RNode* rn, uint32_t timeout) { int err_code = 0; time_t ltime; time(<ime); bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; unsigned int start = ltime; unsigned int current = ltime; if (timeout < 200) { return -1; } while (current <= start + timeout) { if (err_code >= 0) { sleep_ms(50); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); time(<ime); current = ltime; if (rn->bt_pairing_pin != 0) { // If we successfully retrieved the pairing pin, return it return rn->bt_pairing_pin; } } else { return err_code; } } return -4; } /* Display related functions */ /* Set display intensity on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_disp_int(struct RNode* rn, uint8_t disp_int) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_INT, disp_int, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Set display timeout on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_disp_timeout(struct RNode* rn, uint8_t timeout) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_INT, timeout, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Set display address on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_disp_addr(struct RNode* rn, uint8_t addr) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_ADDR, addr, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Set display rotation on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_disp_rot(struct RNode* rn, uint8_t rot) { int err_code = 0; if (rot < 0) rot = 0; else if (rot > 3) rot = 3; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_ROT, rot, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Start display reconditioning on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_start_disp_recon(struct RNode* rn) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_RCND, 1, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Set neopixel intensity on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_np_int(struct RNode* rn, uint8_t np_int) { int err_code; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_NP_INT, np_int, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Get available interfaces on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_interfaces(struct RNode* rn) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_INTERFACES, 0x00, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); } if (err_code > 0) { return 0; } else { return err_code; } } /* Radio configuration functions */ /* Select interface on an RNode (in preparation for running rnode_set_freq, etc) * Scope: public * Returns * 0 - success * -1 - generic error * -13 - chosen interface does not exist (did you run rnode_get_interfaces)? */ int rnode_select_interface(struct RNode* rn, uint8_t interface) { int err_code; uint8_t tx_buf[4]; if (rn->interfaces[interface] != 0) { memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SEL_INT, interface, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); } else { return err_code; } if (!err_code) { rn->sel_int = interface; } return err_code; } /* Set frequency on an RNode (in preparation for TNC mode) * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_freq(struct RNode* rn, uint32_t freq) { uint8_t tx_buf[11]; uint8_t escaped_freq[8]; uint32_t escaped_freq_size; uint8_t freq_a[4]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(freq_a, (uint8_t[4]){freq >> 24, freq >> 16 & 0xFF, freq >> 8 & 0xFF, freq & 0xFF}, 4*sizeof(uint8_t)); memcpy(tx_buf, (uint8_t[2]){FEND, CMD_FREQUENCY}, 2*sizeof(uint8_t)); encode_slip(escaped_freq, &escaped_freq_size, freq_a, 4, false); memcpy(tx_buf + 2, escaped_freq, escaped_freq_size*sizeof(uint8_t)); tx_buf[2 + escaped_freq_size] = FEND; err_code = write_port(rn->fd, tx_buf, 2 + escaped_freq_size + 1); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->freq; } else { return err_code; } } else { return err_code; } } /* Get frequency on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_freq(struct RNode* rn) { uint8_t tx_buf[7]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[7]){FEND, CMD_FREQUENCY, 0, 0, 0, 0, FEND}, 7*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 7); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->freq; } else { return err_code; } } else { return err_code; } } /* Set bandwidth on an RNode (in preparation for TNC mode) * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_bw(struct RNode* rn, uint32_t bw) { int err_code; uint8_t tx_buf[11]; uint8_t escaped_bw[8]; uint32_t escaped_bw_size; uint8_t bw_a[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(bw_a, (uint8_t[4]){bw >> 24, bw >> 16 & 0xFF, bw >> 8 & 0xFF, bw & 0xFF}, 4*sizeof(uint8_t)); memcpy(tx_buf, (uint8_t[2]){FEND, CMD_BANDWIDTH}, 2*sizeof(uint8_t)); encode_slip(escaped_bw, &escaped_bw_size, bw_a, 4, false); memcpy(tx_buf + 2, escaped_bw, escaped_bw_size*sizeof(uint8_t)); tx_buf[2 + escaped_bw_size] = FEND; err_code = write_port(rn->fd, tx_buf, 2 + escaped_bw_size + 1); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->bw; } else { return err_code; } } else { return err_code; } } /* Get bandwidth on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_bw(struct RNode* rn) { uint8_t tx_buf[7]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[7]){FEND, CMD_BANDWIDTH, 0, 0, 0, 0, FEND}, 7*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 7); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->bw; } else { return err_code; } } else { return err_code; } } /* Set transmission power on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_txp(struct RNode* rn, uint8_t txp) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_TXPOWER, txp, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return txp == rn->txp ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Get transmission power on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_txp(struct RNode* rn) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_TXPOWER, 0xFF, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->txp; } else { return err_code; } } else { return err_code; } } /* Set spreading factor on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_sf(struct RNode* rn, uint8_t sf) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SF, sf, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return sf == rn->sf ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Get spreading factor on an RNode * Scope: public * Returns * >=5 - spreading factor * 0 - success * -1 - generic error */ int rnode_get_sf(struct RNode* rn) { uint8_t tx_buf[11]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SF, 0xFF, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (!err_code) { return rn->sf; } else { return err_code; } } else { return err_code; } } /* Set coding rate on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_cr(struct RNode* rn, uint8_t cr) { uint8_t tx_buf[4]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CR, cr, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return cr == rn->cr ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Get coding rate on an RNode * Scope: public * Returns * >=5 - coding rate * 0 - success * -1 - generic error */ int rnode_get_cr(struct RNode* rn) { uint8_t tx_buf[4]; int err_code; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CR, 0xFF, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return rn->cr; } else { return err_code; } } else { return err_code; } } /* Set short term airtime limit on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ float rnode_set_st_alock(struct RNode* rn, float at_l) { int err_code; int at = at_l * 100; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[2]){FEND, CMD_ST_ALOCK}, 2*sizeof(uint8_t)); memcpy(tx_buf + 2, (uint8_t[2]){(at >> 8 & 0xFF), (at & 0xFF)}, 1*sizeof(uint8_t)); tx_buf[2 + 1] = FEND; err_code = write_port(rn->fd, tx_buf, 2 + 1 + 1); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return at_l == rn->st_alock ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Set long term airtime limit on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ float rnode_set_lt_alock(struct RNode* rn, float at_l) { int err_code; int at = at_l * 100; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[2]){FEND, CMD_LT_ALOCK}, 2*sizeof(uint8_t)); memcpy(tx_buf + 2, (uint8_t[2]){(at >> 8 & 0xFF), (at & 0xFF)}, 1*sizeof(uint8_t)); tx_buf[2 + 1] = FEND; err_code = write_port(rn->fd, tx_buf, 2 + 1 + 1); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return at_l == rn->lt_alock ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Start or stop selected interface on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_interface_state(struct RNode* rn, bool state) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[10] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_RADIO_STATE, state, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); if (err_code > 0) { return state == rn->int_state[rn->sel_int] ? 0 : -1; } else { return err_code; } } else { return err_code; } } /* Get selected interface state on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_interface_state(struct RNode* rn) { return rn->int_state[rn->sel_int]; } /* Mode selection functions */ /* Enable the host-controlled (normal) mode of operation on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_normal_mode(struct RNode* rn) { uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CONF_DELETE, 0x00, FEND}, 4*sizeof(uint8_t)); return write_port(rn->fd, tx_buf, 4); } /* Enable the serial TNC mode of operation on an RNode * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_tnc_mode(struct RNode* rn) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CONF_SAVE, 0x00, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* Misc functions */ int rnode_set_int_avoid(struct RNode* rn, bool avoid) { int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DIS_IA, !avoid, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); return err_code; } /* EEPROM related functions */ /* Sets the firmware hash on an RNode. * Scope: public * Returns: * 0 - success * -1 - generic error */ int rnode_set_fw_hash(struct RNode* rn, uint8_t* hash) { uint8_t tx_buf[2 + (FW_HASH_SIZE * 2) + 1]; uint8_t escaped_hash[FW_HASH_SIZE*2]; uint32_t escaped_hash_size; unsigned char hash_str[FW_HASH_SIZE * 2 + 1] = ""; log_debug("Setting firmware hash..."); for (int i = 0; i < FW_HASH_SIZE; i++) { sprintf(hash_str+i*2, "%02x", hash[i]); } log_trace("Firmware hash is %s", hash_str); memcpy(tx_buf, (uint8_t[2]){FEND, CMD_FW_HASH}, 2*sizeof(uint8_t)); encode_slip(escaped_hash, &escaped_hash_size, hash, FW_HASH_SIZE, false); memcpy(tx_buf + 2, escaped_hash, escaped_hash_size*sizeof(uint8_t)); tx_buf[2 + escaped_hash_size] = FEND; return write_port(rn->fd, tx_buf, 2 + escaped_hash_size + 1); } /* Write to an arbitrary address in an RNode's EEPROM * Scope: private * Returns * 0 - success * -1 - generic error */ int rnode_write_eeprom(struct RNode* rn, uint8_t address, uint8_t value) { int err_code = 0; uint8_t tx_buf[3 + 2*2]; uint8_t data[2]; uint8_t escaped_data[2*2]; uint32_t escaped_data_size; data[0] = address; data[1] = value; memcpy(tx_buf, (uint8_t[2]){FEND, CMD_ROM_WRITE}, 2*sizeof(uint8_t)); encode_slip(escaped_data, &escaped_data_size, data, 2, false); memcpy(tx_buf+2, escaped_data, escaped_data_size*sizeof(uint8_t)); tx_buf[2 + escaped_data_size] = FEND; err_code = write_port(rn->fd, tx_buf, 2 + escaped_data_size + 1); if (!err_code) { sleep_ms(6); } return err_code; } /* Sets an RNode's product in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_product(struct RNode* rn, uint8_t product) { rn->product = product; return rnode_write_eeprom(rn, ADDR_PRODUCT, product); } /* Sets an RNode's model in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_model(struct RNode* rn, uint8_t model) { rn->model = model; return rnode_write_eeprom(rn, ADDR_MODEL, model); } /* Sets an RNode's hardware revision in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_hw_rev(struct RNode* rn, uint8_t hw_rev) { rn->hw_rev = hw_rev; return rnode_write_eeprom(rn, ADDR_HW_REV, hw_rev); } /* Sets an RNode's serial in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_serial(struct RNode* rn, uint8_t* serial) { int err_code = 0; for (int i = 0; i < 4; i++) { err_code = rnode_write_eeprom(rn, ADDR_SERIAL+i, serial[i]); rn->serial[i] = serial[i]; if (err_code) { break; } } return err_code; } /* Sets an RNode's manufacture time in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_made_time(struct RNode* rn, uint32_t time) { int err_code = 0; uint8_t data[4]; data[3] = time & 0x000000FF; data[2] = time >> 8 & 0x000000FF; data[1] = time >> 16 & 0x000000FF; data[0] = time >> 24 & 0x000000FF; for (int i = 0; i < 4; i++) { err_code = rnode_write_eeprom(rn, ADDR_MADE+i, data[i]); rn->made[i] = data[i]; if (err_code) { break; } } return err_code; } /* Calculates an RNode's checksum for its EEPROM. Must ONLY be called AFTER * rnode_set_product, rnode_set_model, rnode_set_hw_rev, rnode_set_serial and * rnode_set_made_time are all called. OR alternatively, after rnode_get_eeprom. * Scope: public * Returns * 0 - success * -1 - generic error * -3 - provided array was too small */ int rnode_calculate_checksum(struct RNode* rn, uint8_t* checksum) { uint8_t data[11]; int temp_val; unsigned char checksum_s[CHECKSUM_SIZE * 2]; data[0] = rn->product; data[1] = rn->model; data[2] = rn->hw_rev; data[3] = rn->serial[0]; data[4] = rn->serial[1]; data[5] = rn->serial[2]; data[6] = rn->serial[3]; data[7] = rn->made[0]; data[8] = rn->made[1]; data[9] = rn->made[2]; data[10] = rn->made[3]; MD5Data(data, 11, checksum_s); for (int i = 0; i < CHECKSUM_SIZE && sscanf(checksum_s + i * 2, "%2x", &temp_val); i++) { checksum[i] = temp_val; } return 0; } /* Sets an RNode's checksum in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_checksum(struct RNode* rn, uint8_t* checksum) { int err_code = 0; for (int i = 0; i < CHECKSUM_SIZE; i++) { err_code = rnode_write_eeprom(rn, ADDR_CHKSUM+i, checksum[i]); if (err_code) { break; } } memcpy(rn->checksum, checksum, CHECKSUM_SIZE); return err_code; } /* Generate's an RNode's EEPROM signature from the checksum * THE RETURNED POINTER MUST BE OPENSSL_free()'D! * Scope: public * Returns * 0 - success * -1 - generic error * -3 - malloc failure */ void* rnode_generate_signature(struct RNode* rn, EVP_PKEY *signing_key) { EVP_PKEY_CTX *ctx; size_t hash_len = 256 / 8, sig_len; uint8_t hash[256 / 8]; void* signature; /* * NB: assumes signing_key and md are set up before the next * step. signing_key must be an RSA private key and md must * point to the SHA-256 digest to be signed. */ ctx = EVP_PKEY_CTX_new(signing_key, NULL /* no engine */); if (ctx == NULL) return NULL; if (EVP_PKEY_sign_init(ctx) <= 0) return NULL; if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) return NULL; if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) return NULL; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_MAX) <= 0) return NULL; if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) <= 0) return NULL; SHA256(rn->checksum, CHECKSUM_SIZE, hash); /* Determine buffer length */ if (EVP_PKEY_sign(ctx, NULL, &sig_len, hash, hash_len) <= 0) return NULL; // todo need to free signature signature = OPENSSL_malloc(sig_len); if (signature == NULL) return NULL; if (EVP_PKEY_sign(ctx, signature, &sig_len, hash, hash_len) <= 0) return NULL; /* Signature is siglen bytes written to buffer sig */ EVP_PKEY_CTX_free(ctx); return signature; } /* Sets an RNode's signature in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_signature(struct RNode* rn, uint8_t* data) { int err_code = 0; for (int i = 0; i < SIGNATURE_SIZE; i++) { err_code = rnode_write_eeprom(rn, ADDR_SIGNATURE+i, data[i]); if (err_code) { break; } } return err_code; } /* Sets an RNode's lock byte in its EEPROM * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_set_lock(struct RNode* rn) { return rnode_write_eeprom(rn, ADDR_INFO_LOCK, INFO_LOCK_BYTE); } /* Retrieve an RNode's EEPROM values into its struct * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_get_eeprom(struct RNode* rn) { int err_code = rnode_dump_eeprom(rn); // Populate remote EEPROM values memcpy(&rn->product, rn->r_eeprom, 1); memcpy(&rn->model, rn->r_eeprom+1, 1); memcpy(&rn->hw_rev, rn->r_eeprom+2, 1); memcpy(&rn->serial, rn->r_eeprom+3, 4); memcpy(&rn->made, rn->r_eeprom+7, 4); memcpy(&rn->checksum, rn->r_eeprom+11, 16); memcpy(&rn->signature, rn->r_eeprom+27, 128); memcpy(&rn->lock_byte, rn->r_eeprom+155, 1); memcpy(&rn->sf, rn->r_eeprom+156, 1); memcpy(&rn->cr, rn->r_eeprom+157, 1); memcpy(&rn->txp, rn->r_eeprom+158, 1); memcpy(&rn->bw, rn->r_eeprom+159, 4); memcpy(&rn->freq, rn->r_eeprom+163, 4); memcpy(&rn->cfg_ok, rn->r_eeprom+167, 4); memcpy(&rn->bt, rn->r_eeprom+168, 1); memcpy(&rn->disp_set, rn->r_eeprom+169, 1); memcpy(&rn->disp_int, rn->r_eeprom+170, 1); memcpy(&rn->disp_addr, rn->r_eeprom+171, 1); return err_code; } /* Dump an RNode's entire EEPROM to r_eeprom * Note: This will populate r_eeprom but not the cached EEPROM values. If you * want to update the cached values, call rnode_get_eeprom instead. * Scope: public * Returns * 0 - success * -1 - generic error */ int rnode_dump_eeprom(struct RNode* rn) { int err_code; uint8_t tx_buf[4]; bool in_frame = false; uint8_t cmd = CMD_UNKNOWN; uint8_t cmd_buf[256] = {0}; uint8_t cmd_buf_l = 0; uint16_t frame_len = 0; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_ROM_READ, 0x00, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); if (!err_code) { sleep_ms(100); err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len); } return err_code > 0 ? 0 : -1; } /* Verify an RNode's EEPROM matches the expected values * Scope: public * Returns * 0 - success * value > 0 or value < 0 - EEPROM invalid */ int rnode_verify_eeprom(struct RNode* rn) { int err_code; err_code = rnode_dump_eeprom(rn); if (!err_code) { err_code = memcmp(&rn->product, rn->r_eeprom, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->model, rn->r_eeprom+1, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->hw_rev, rn->r_eeprom+2, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->serial, rn->r_eeprom+3, sizeof(4)); } if (!err_code) { err_code = memcmp(&rn->made, rn->r_eeprom+7, sizeof(4)); } if (!err_code) { err_code = memcmp(&rn->checksum, rn->r_eeprom+11, sizeof(16)); } if (!err_code) { err_code = memcmp(&rn->signature, rn->r_eeprom+27, sizeof(128)); } if (!err_code) { err_code = memcmp(&rn->lock_byte, rn->r_eeprom+155, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->sf, rn->r_eeprom+156, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->cr, rn->r_eeprom+157, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->txp, rn->r_eeprom+158, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->bw, rn->r_eeprom+159, sizeof(4)); } if (!err_code) { err_code = memcmp(&rn->freq, rn->r_eeprom+163, sizeof(4)); } if (!err_code) { err_code = memcmp(&rn->cfg_ok, rn->r_eeprom+167, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->bt, rn->r_eeprom+168, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->disp_set, rn->r_eeprom+169, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->disp_int, rn->r_eeprom+170, sizeof(1)); } if (!err_code) { err_code = memcmp(&rn->disp_addr, rn->r_eeprom+171, sizeof(1)); } return err_code; } /* Wipes an RNode's EEPROM. * Scope: public * Returns: * 0 - success * -1 - generic error * -2 - rnode not supported (firmware too old, please update) */ int rnode_wipe_eeprom(struct RNode* rn) { log_info("Erasing EEPROM, please switch off your device IMMEDIATELY if you did not intend to do this!"); int err_code = 0; uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_UNLOCK_ROM, 0xF8, FEND}, 4*sizeof(uint8_t)); if (rn->fw_ver != 0 && rn->fw_ver < MIN_FW_VER) { log_trace("RNode firmware version too low!"); return -2; } else { err_code = write_port(rn->fd, tx_buf, 4); } if (err_code) { return err_code; } err_code = close_port(rn->fd); if (!err_code) { sleep_ms(13000); if (rn->platform == PLATFORM_NRF52) { // Due to the current janky emulated EEPROM implementation for the // RAK4631, extra time must be given to allow for writing. sleep_ms(10000); } } else { return err_code; } rn->fd = open_port(rn->port, rn->baud); return (err_code == 0) ? 0 : -1; } int rnode_flash_progress_cb(struct RNode* rn, void (*ptr)(uint8_t)) { rn->prog_cb = ptr; } /* Flashes an RNode. * Scope: public * Returns: * 0 - success * -1 - generic error * -9 - ESP32 image invalid, SHA256 digest incorrect */ int rnode_flash(struct RNode* rn, char* zip_path, bool update, uint8_t* serial, EVP_PKEY* priv_key, bool touch) { int err_code = 0; uint8_t hash[FW_HASH_SIZE]; if (update) { // show update logo on RNode display uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_FW_UPD, 0x01, FEND}, 4*sizeof(uint8_t)); err_code = write_port(rn->fd, tx_buf, 4); } // Close port to prepare for flashing if (!err_code) { err_code = close_port(rn->fd); } if (!err_code) { switch (rn->platform) { case PLATFORM_ESP32: uint8_t img_hash[32]; struct zip_t *zip_pkg; char* bin_ext = ".bin"; char* bootloader_ext = ".bootloader"; char* boot_app0_ext = ".boot_app0"; char* partitions_ext = ".partitions"; char* console_image_name = "console_image.bin"; char *bin, *bootloader, *boot_app0, *partitions, *console_image = 0, *bin_path, *bootloader_path, *boot_app0_path, *partitions_path, *console_image_path, *filename, *filename_no_ext, *dir, *zip_path_cpy; size_t bin_size, bootloader_size, boot_app0_size, partitions_size, console_image_size = 0, bin_path_size, bootloader_path_size, boot_app0_path_size, partitions_path_size, console_image_path_size, filename_no_ext_size; zip_pkg = zip_open(zip_path, 0, 'r'); if (zip_pkg == NULL) { log_error("Cannot open ZIP firmware package file!"); return -1; } else { zip_path_cpy = strdup(zip_path); filename = basename(zip_path_cpy); for (int i = 0; i < strlen(filename); i++) { if (filename[i] == '.') { filename_no_ext_size = i; break; } } filename_no_ext = malloc(filename_no_ext_size+2); memcpy(filename_no_ext, filename, filename_no_ext_size); filename_no_ext[filename_no_ext_size+1] = '\0'; // Retrieve bin file bin_path_size = filename_no_ext_size+strlen(bin_ext); bin_path = malloc(bin_path_size+1); strcpy(bin_path, filename_no_ext); strcat(bin_path, bin_ext); if (zip_entry_open(zip_pkg, bin_path)) { return -1; } else { bin = malloc(FILE_MAX_SIZE); if (zip_entry_read(zip_pkg, (void**)&bin, &bin_size)) { return -1; } else { if (zip_entry_close(zip_pkg)) { return -1; } } } // Retrieve bootloader bootloader_path_size = filename_no_ext_size+strlen(bootloader_ext); bootloader_path = malloc(bootloader_path_size+1); strcpy(bootloader_path, filename_no_ext); strcat(bootloader_path, bootloader_ext); if (zip_entry_open(zip_pkg, bootloader_path)) { return -1; } else { bootloader = malloc(FILE_MAX_SIZE); if (zip_entry_read(zip_pkg, (void**)&bootloader, &bootloader_size)) { return -1; } else { if (zip_entry_close(zip_pkg)) { return -1; } } } // Retrieve boot_app0 boot_app0_path_size = filename_no_ext_size+strlen(boot_app0_ext); boot_app0_path = malloc(boot_app0_path_size+1); strcpy(boot_app0_path, filename_no_ext); strcat(boot_app0_path, boot_app0_ext); if (zip_entry_open(zip_pkg, boot_app0_path)) { return -1; } else { boot_app0 = malloc(FILE_MAX_SIZE); if (zip_entry_read(zip_pkg, (void**)&boot_app0, &boot_app0_size)) { return -1; } else { if (zip_entry_close(zip_pkg)) { return -1; } } } // Retrieve partitions partitions_path_size = filename_no_ext_size+strlen(partitions_ext); partitions_path = malloc(partitions_path_size+1); strcpy(partitions_path, filename_no_ext); strcat(partitions_path, partitions_ext); if (zip_entry_open(zip_pkg, partitions_path)) { return -1; } else { partitions = malloc(FILE_MAX_SIZE); if (zip_entry_read(zip_pkg, (void**)&partitions, &partitions_size)) { return -1; } else { if (zip_entry_close(zip_pkg)) { return -1; } } } // Retrieve console image if (zip_entry_open(zip_pkg, console_image_name)) { // No console image on this build, skipping } else { console_image = malloc(FILE_MAX_SIZE); if (zip_entry_read(zip_pkg, (void**)&console_image, &console_image_size)) { return -1; } else { if (zip_entry_close(zip_pkg)) { return -1; } } } // Check firmware hash memcpy(img_hash, (bin + bin_size) - 32, 32); SHA256(bin, bin_size-32, hash); if (memcmp(hash, img_hash, 32*sizeof(uint8_t) != 0)) { return -9; } flash_full(rn, boot_app0, boot_app0_size, bootloader, bootloader_size, bin, bin_size, partitions, partitions_size, console_image, console_image_size); free(console_image); free(partitions); free(partitions_path); free(boot_app0); free(boot_app0_path); free(bootloader); free(bootloader_path); free(bin); free(bin_path); free(filename_no_ext); free(zip_path_cpy); } break; case PLATFORM_NRF52: { // No space for console image on flash on this target currently dfu_param_t dfu_param; uart_drv_t uart_drv; uart_drv.p_PortName = rn->port; err_code = uart_slip_open(&uart_drv, touch); if (!err_code) { dfu_param.p_uart = &uart_drv; dfu_param.p_pkg_file = zip_path; err_code = dfu_send_package(&dfu_param, rn, hash); } else { return -1; } err_code = uart_slip_close(&uart_drv); } } } // Wait for reset and boot. This value may have to be adjusted in the // future if firmware becomes more complex. sleep_ms(15000); rn->fd = open_port(rn->port, rn->baud); if (rn->fd > 0) { if (!update) { uint8_t checksum[CHECKSUM_SIZE]; void* signature; log_info("Bootstrapping RNode EEPROM..."); // Provision EEPROM err_code = rnode_set_product(rn, rn->product); if (!err_code) { err_code = rnode_set_model(rn, rn->model); } if (!err_code) { err_code = rnode_set_hw_rev(rn, rn->hw_rev); } if (!err_code) { err_code = rnode_set_serial(rn, serial); } if (!err_code) { time_t current = time(NULL); err_code = rnode_set_made_time(rn, current); } if (!err_code) { err_code = rnode_calculate_checksum(rn, checksum); } if (!err_code) { err_code = rnode_set_checksum(rn, checksum); } if (!err_code) { signature = rnode_generate_signature(rn, priv_key); } if (signature != NULL) { err_code = rnode_set_signature(rn, signature); } if (!err_code) { OPENSSL_free(signature); err_code = rnode_set_lock(rn); } } } else { err_code = -1; } if (!err_code) { sleep_ms(750); err_code = rnode_set_fw_hash(rn, hash); } else { return err_code; } if (!err_code) { log_info("Waiting 10 seconds for RNode to come online..."); sleep_ms(10000); if (rn->platform == PLATFORM_NRF52) { err_code = close_port(rn->fd); // Due to the current janky emulated EEPROM implementation for the // RAK4631, extra time must be given to allow for writing. log_info("Waiting an extra 15 seconds for RNode to come online..."); sleep_ms(15000); } else if (rn->platform == PLATFORM_ESP32) { rnode_reset(rn); err_code = close_port(rn->fd); log_info("Waiting for ESP32 reset..."); sleep_ms(7000); } err_code = rnode_init(rn, rn->port, rn->baud, true, true); } else { return err_code; } if (!err_code) { err_code = rnode_verify_eeprom(rn); } return err_code; } int rnode_disconnect(struct RNode* rn) { uint8_t tx_buf[4]; memcpy(tx_buf, (uint8_t[4]){FEND, CMD_LEAVE, 0xFF, FEND}, 4*sizeof(uint8_t)); return write_port(rn->fd, tx_buf, 4); } int rnode_cleanup(struct RNode* rn) { close_port(rn->fd); }