#define BOARD_RNODE_NG_21 0x41
#define BOARD_RNODE_NG_22 0x42
#define BOARD_GENERIC_NRF52 0x50
- #define BOARD_RAK4630 0x51
+ #define BOARD_RAK4631 0x51
#if defined(__AVR_ATmega1284P__)
#define PLATFORM PLATFORM_AVR
#endif
#ifndef MODEM
- #if BOARD_MODEL == BOARD_RAK4630
+ #if BOARD_MODEL == BOARD_RAK4631
#define MODEM SX1262
#elif BOARD_MODEL == BOARD_GENERIC_NRF52
#define MODEM SX1262
#endif
#elif MCU_VARIANT == MCU_NRF52
- #if BOARD_MODEL == BOARD_RAK4630
+ #if BOARD_MODEL == BOARD_RAK4631
#define HAS_EEPROM false
- #define HAS_DISPLAY true
+ #define HAS_DISPLAY false
#define HAS_BLUETOOTH false
#define HAS_BLE true
#define HAS_CONSOLE false
#define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_SIZE 6144
#define CONFIG_QUEUE_MAX_LENGTH 200
- #define EEPROM_SIZE 200
+ #define EEPROM_SIZE 296
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
#define BLE_MANUFACTURER "RAK Wireless"
#define BLE_MODEL "RAK4640"
#include "esp_ota_ops.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"
+
+#elif MCU_VARIANT == MCU_NRF52
+#include "Adafruit_nRFCrypto.h"
+
+// size of chunk to retrieve from flash sector
+#define CHUNK_SIZE 128
+
+#define END_SECTION_SIZE 256
+
+#if defined(NRF52840_XXAA)
+// https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather/hathach-memory-map
+// each section follows along from one another, in this order
+// this is always at the start of the memory map
+#define APPLICATION_START 0x26000
+
+#define USER_DATA_START 0xED000
+#endif
+
#endif
// Forward declaration from Utilities.h
void eeprom_update(int mapped_addr, uint8_t byte);
+void eeprom_flush();
uint8_t eeprom_read(uint32_t addr);
void hard_reset(void);
for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
eeprom_update(dev_fwhash_addr(i), dev_firmware_hash_target[i]);
}
+ eeprom_flush();
if (!fw_signature_validated) hard_reset();
}
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_NRF52
+void calculate_region_hash(unsigned long long start, unsigned long long end, uint8_t* return_hash) {
+ // this function calculates the hash digest of a region of memory,
+ // currently it is only designed to work for the application region
+ uint8_t chunk[CHUNK_SIZE] = {0};
+
+ // to store potential last chunk of program
+ uint8_t chunk_next[CHUNK_SIZE] = {0};
+ nRFCrypto_Hash hash;
+
+ hash.begin(CRYS_HASH_SHA256_mode);
+
+ bool finish = false;
+ uint8_t size;
+ bool application = true;
+ int end_count = 0;
+ unsigned long length = 0;
+
+ while (start < end - 1 ) {
+ const void* src = (const void*)start;
+ if (start + CHUNK_SIZE >= end) {
+ size = (end - 1) - start;
+ }
+ else {
+ size = CHUNK_SIZE;
+ }
+
+ memcpy(chunk, src, CHUNK_SIZE);
+
+ // check if we've reached the end of the program
+ // if we're checking the application region
+ if (application) {
+ for (int i = 0; i < CHUNK_SIZE; i++) {
+ if (chunk[i] == 0xFF) {
+ bool matched = true;
+ end_count = 1;
+ // check if rest of chunk is FFs as well, only if FF is not
+ // at the end of chunk
+ if (i < CHUNK_SIZE - 1) {
+ for (int x = 0; x < CHUNK_SIZE - i; x++) {
+ if (chunk[i+x] != 0xFF) {
+ matched = false;
+ break;
+ }
+ end_count++;
+ }
+ }
+
+ if (matched) {
+ while (end_count < END_SECTION_SIZE) {
+ // check if bytes in next chunk up to total
+ // required are also FFs
+ for (int x = 1; x <= ceil(END_SECTION_SIZE / CHUNK_SIZE); x++) {
+ const void* src_next = (const void*)start + CHUNK_SIZE*x;
+ if ((END_SECTION_SIZE - end_count) > CHUNK_SIZE) {
+ size = CHUNK_SIZE;
+ } else {
+ size = END_SECTION_SIZE - end_count;
+ }
+ memcpy(chunk_next, src_next, size);
+ for (int y = 0; y < size; y++) {
+ if (chunk_next[y] != 0xFF) {
+ matched = false;
+ break;
+ }
+ end_count++;
+ }
+
+ if (!matched) {
+ break;
+ }
+ }
+ if (!matched) {
+ break;
+ }
+ }
+
+ if (matched) {
+ finish = true;
+ size = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (finish) {
+ hash.update(chunk, size);
+ length += size;
+ break;
+ } else {
+ hash.update(chunk, size);
+ }
+
+ start += CHUNK_SIZE;
+ length += CHUNK_SIZE;
+ }
+ hash.end(return_hash);
+}
+#endif
+
void device_validate_partitions() {
device_load_firmware_hash();
+ #if MCU_VARIANT == MCU_ESP32
esp_partition_t partition;
partition.address = ESP_PARTITION_TABLE_OFFSET;
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
partition.type = ESP_PARTITION_TYPE_APP;
esp_partition_get_sha256(&partition, dev_bootloader_hash);
esp_partition_get_sha256(esp_ota_get_running_partition(), dev_firmware_hash);
+ #elif MCU_VARIANT == MCU_NRF52
+ // todo, add bootloader, partition table, or softdevice?
+ calculate_region_hash(APPLICATION_START, USER_DATA_START, dev_firmware_hash);
+ #endif
#if VALIDATE_FIRMWARE
for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
if (dev_firmware_hash_target[i] != dev_firmware_hash[i]) {
}
#endif
}
-#endif
bool device_firmware_ok() {
return fw_signature_validated;
}
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
bool device_init() {
if (bt_ready) {
+ #if MCU_VARIANT == MCU_ESP32
for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=EEPROM.read(eeprom_addr(ADDR_SIGNATURE+i));}
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_update(&ctx, dev_eeprom_signature, EEPROM_SIG_LEN);
mbedtls_md_finish(&ctx, dev_hash);
mbedtls_md_free(&ctx);
+ #elif MCU_VARIANT == MCU_NRF52
+ for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=eeprom_read(eeprom_addr(ADDR_SIGNATURE+i));}
+ nRFCrypto.begin();
+
+ nRFCrypto_Hash hash;
+
+ hash.begin(CRYS_HASH_SHA256_mode);
+
+ #if HAS_BLUETOOTH == true || HAS_BLE == true
+ hash.update(dev_bt_mac, BT_DEV_ADDR_LEN);
+ #else
+ // TODO: Get from BLE stack instead
+ // hash.update(dev_bt_mac, BT_DEV_ADDR_LEN);
+ #endif
+ hash.update(dev_eeprom_signature, EEPROM_SIG_LEN);
+
+ hash.end(dev_hash);
+ #endif
device_load_signature();
device_validate_signature();
+
device_validate_partitions();
+
+ #if MCU_VARIANT == MCU_NRF52
+ nRFCrypto.end();
+ #endif
device_init_done = true;
return device_init_done && fw_signature_validated;
} else {
firmware-genericesp32:
arduino-cli compile --fqbn esp32:esp32:esp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\""
-firmware-rak4630:
+firmware-rak4631:
arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\""
upload:
@sleep 3
python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin
-upload-rak4630:
+upload-rak4631:
arduino-cli upload -p /dev/ttyACM0 --fqbn rakwireless:nrf52:WisCoreRAK4631Board
arduino-cli compile --fqbn arduino:avr:mega -e --build-property "compiler.cpp.extra_flags=\"-DMODEM=0x01\""
cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_m2560.hex
rm -r build
+
+release-rak4631:
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\""
+ cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware.ino.hex build/rnode_firmware_rak4631.hex
+ adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_rak4631.hex Release/rnode_firmware_rak4631.zip
fifo_init(&serialFIFO, serialBuffer, CONFIG_UART_BUFFER_SIZE);
Serial.begin(serial_baudrate);
+ #if BOARD_MODEL != BOARD_RAK4631
+ // Some boards need to wait until the hardware UART is set up before booting
+ // the full firmware. In the case of the RAK4631, the line below will wait
+ // until a serial connection is actually established with a master. Thus, it
+ // is disabled on this platform.
while (!Serial);
+ #endif
serial_interrupt_init();
if (eeprom_checksum_valid()) {
eeprom_ok = true;
if (modem_installed) {
- #if PLATFORM == PLATFORM_ESP32
+ #if PLATFORM == PLATFORM_ESP32 || PLATFORM == PLATFORM_NRF52
if (device_init()) {
hw_ready = true;
} else {
#define PRODUCT_T32_21 0xB1
#define PRODUCT_H32_V2 0xC0
#define PRODUCT_H32_V3 0xC1
+ #define PRODUCT_RAK4631 0x10
+ #define MODEL_11 0x11
+ #define MODEL_12 0x12
#define MODEL_A1 0xA1
#define MODEL_A6 0xA6
#define MODEL_A4 0xA4
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
#endif
#elif MCU_VARIANT == MCU_NRF52
- #if BOARD_MODEL == BOARD_RAK4630
+ #if BOARD_MODEL == BOARD_RAK4631
void led_rx_on() { digitalWrite(pin_led_rx, HIGH); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
#elif MCU_VARIANT == MCU_ESP32
ESP.restart();
#elif MCU_VARIANT == MCU_NRF52
- // currently not possible to restart on this platform
+ NVIC_SystemReset();
#endif
}
serial_write(FEND);
}
+#if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+void eeprom_flush() {
+ // sync file contents to flash
+ file.close();
+ file.open(EEPROM_FILE, FILE_O_WRITE);
+ written_bytes = 0;
+}
+#endif
+
void eeprom_update(int mapped_addr, uint8_t byte) {
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
EEPROM.update(mapped_addr, byte);
EEPROM.commit();
}
#elif !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+ // todo: clean up this implementation, writing one byte and syncing
+ // each time is really slow, but this is also suboptimal
uint8_t read_byte;
void* read_byte_ptr = &read_byte;
file.seek(mapped_addr);
file.write(byte);
}
written_bytes++;
+
+ if ((mapped_addr - eeprom_addr(0)) == ADDR_INFO_LOCK) {
+ #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+ // have to do a flush because we're only writing 1 byte and it syncs after 4
+ eeprom_flush();
+ #endif
+ }
- if (written_bytes >= 8) {
+ if (written_bytes >= 4) {
file.close();
file.open(EEPROM_FILE, FILE_O_WRITE);
written_bytes = 0;
#endif
}
-#if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
-void eeprom_flush() {
- // sync file contents to flash
- file.close();
- file.open(EEPROM_FILE, FILE_O_WRITE);
- written_bytes = 0;
-}
-#endif
-
void eeprom_write(uint8_t addr, uint8_t byte) {
if (!eeprom_info_locked() && addr >= 0 && addr < EEPROM_RESERVED) {
eeprom_update(eeprom_addr(addr), byte);
#elif PLATFORM == PLATFORM_ESP32
if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3) {
#elif PLATFORM == PLATFORM_NRF52
- if (rval == PRODUCT_HMBRW) {
+ if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW) {
#else
if (false) {
#endif
if (model == MODEL_C4 || model == MODEL_C9) {
#elif BOARD_MODEL == BOARD_HELTEC32_V3
if (model == MODEL_C5 || model == MODEL_CA) {
- #elif BOARD_MODEL == BOARD_RAK4630
- if (model == MODEL_FF) {
+ #elif BOARD_MODEL == BOARD_RAK4631
+ if (model == MODEL_11 || model == MODEL_12) {
#elif BOARD_MODEL == BOARD_HUZZAH32
if (model == MODEL_FF) {
#elif BOARD_MODEL == BOARD_GENERIC_ESP32
void sx126x::enableTCXO() {
#if HAS_TCXO
- #if BOARD_MODEL == BOARD_RAK4630 || BOARD_MODEL == BOARD_HELTEC32_V3
+ #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3
uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
#elif BOARD_MODEL == BOARD_TBEAM
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};