Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@
#define MODEL_16 0x16 // T-Echo 433 MHz
#define MODEL_17 0x17 // T-Echo 868/915 MHz

#define PRODUCT_T1000E 0x16 // Seeed Tracker T1000-E (nRF52840 + LR1110)
#define BOARD_T1000E 0x46
#define MODEL_18 0x18 // T1000-E, 433 MHz
#define MODEL_19 0x19 // T1000-E, 868/915 MHz

#define PRODUCT_RAK4631 0x10
#define BOARD_RAK4631 0x51
#define MODEL_11 0x11 // RAK4631, 433 Mhz
Expand Down Expand Up @@ -171,6 +176,8 @@
#define MODEM SX1262
#elif BOARD_MODEL == BOARD_GENERIC_NRF52
#define MODEM SX1262
#elif BOARD_MODEL == BOARD_T1000E
#define MODEM LR1110
#else
#define MODEM SX1276
#endif
Expand Down Expand Up @@ -1053,6 +1060,70 @@
const int DISPLAY_BL_PIN = PIN_T114_TFT_BLGT;
const int DISPLAY_RST = PIN_T114_TFT_RST;

#elif BOARD_MODEL == BOARD_T1000E
// Seeed Tracker T1000-E: nRF52840 + Semtech LR1110 (LoRa + Wi-Fi/GNSS
// geolocation, though only LoRa is used here). The RF front end has an
// on-chip RF switch driven by the LR1110's DIO5/DIO6/DIO7/DIO8 pins
// (configured via SET_DIO_AS_RF_SWITCH) and a TCXO supplied through DIO3
// at 1.6 V. Pin map adapted from the MeshCore t1000-e variant.
#define MODEM LR1110
#define HAS_EEPROM false
#define HAS_DISPLAY false
#define HAS_BLUETOOTH false
#define HAS_BLE true
#define HAS_CONSOLE false
#define HAS_PMU true
#define HAS_NP false
#define HAS_SD false
#define HAS_TCXO true
#define HAS_BUSY true
#define HAS_INPUT true
#define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_SIZE 6144
#define CONFIG_QUEUE_MAX_LENGTH 200
#define EEPROM_SIZE 296
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
#define BLE_MANUFACTURER "Seeed Studio"
#define BLE_MODEL "T1000-E"

// SPI (shares the Arduino SPI instance via Config.h spiModem on SPIM2).
const int pin_sclk = 11; // P0.11
const int pin_mosi = 41; // P1.9
const int pin_miso = 40; // P1.8

// LR1110 control pins.
const int pin_cs = 12; // P0.12
const int pin_reset = 42; // P1.10
const int pin_busy = 7; // P0.7
const int pin_dio = 33; // P1.1 (LR1110 DIO1 = IRQ line)
const int pin_tcxo_enable = -1; // TCXO powered via DIO3 (SET_TCXO_MODE)
const int pin_led_rx = LED_GREEN; // P0.24
const int pin_led_tx = LED_GREEN;

// User button (P0.6).
const int pin_btn_usr1 = 6;

// TCXO: 1.6 V on DIO3. The LR11x0 TCXO delay register unit is ~30.52 us;
// 5000 us -> ~164, matching the RadioLib default wakeup window.
#define LR11X0_TCXO_TUNE 0x00 // 1.6 V
#define LR11X0_TCXO_DELAY_US 5000

// On-chip RF switch (DIO5/DIO6/DIO7/DIO8). The 8-byte SET_DIO_AS_RF_SWITCH
// payload below is derived from the MeshCore RF-switch table: enable bits
// DIO5..DIO8, then a per-mode DIO drive config (which of DIO5..DIO8 are
// HIGH) for [stby, rx, tx, tx_hp, tx_hf, gnss, wifi].
#define LR11X0_RFSWITCH_ENABLE 0x0F // DIO5 | DIO6 | DIO7 | DIO8
#define LR11X0_RFSW_STBY 0x00
#define LR11X0_RFSW_RX 0x09 // DIO5 + DIO8
#define LR11X0_RFSW_TX 0x0B // DIO5 + DIO6 + DIO8
#define LR11X0_RFSW_TX_HP 0x0A // DIO6 + DIO8
#define LR11X0_RFSW_TX_HF 0x00
#define LR11X0_RFSW_GNSS 0x00 // GNSS switch unused (LoRa only)
#define LR11X0_RFSW_WIFI 0x00

// RX boosted gain improves sensitivity on the T1000-E front end.
#define LR11X0_RX_BOOSTED 1

#else
#error An unsupported nRF board was selected. Cannot compile RNode firmware.
#endif
Expand Down
2 changes: 1 addition & 1 deletion Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
#define eeprom_addr(a) (a+EEPROM_OFFSET)
#define config_addr(a) (a+CONFIG_OFFSET)

#if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA)
#if (MODEM == SX1262 || MODEM == SX1280 || MODEM == LR1110) && defined(NRF52840_XXAA)
SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi);
#endif

Expand Down
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ firmware-heltec_t114:
firmware-techo:
arduino-cli compile --log --fqbn adafruit:nrf52:pca10056 -e --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""

# Seeed Tracker T1000-E (nRF52840 + Semtech LR1110). This board has no stock
# arduino-cli FQBN, so the build goes through PlatformIO using the custom
# board (boards/tracker-t1000-e.json) and variant (variants/t1000e/).
firmware-t1000e:
pio run -e seeed_t1000e

firmware-xiao_s3:
arduino-cli compile --log --fqbn "esp32:esp32:XIAO_ESP32S3" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3E\""

Expand Down Expand Up @@ -270,6 +276,11 @@ upload-techo:
@sleep 6
rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0)

upload-t1000e:
pio run -e seeed_t1000e -t upload
@sleep 6
rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0)

upload-xiao_s3:
arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:XIAO_ESP32S3
@sleep 1
Expand All @@ -279,7 +290,7 @@ upload-xiao_s3:

release: release-all

release-all: console-site spiffs-image release-tbeam release-tbeam_sx1262 release-lora32_v10 release-lora32_v20 release-lora32_v21 release-lora32_v10_extled release-lora32_v20_extled release-lora32_v21_extled release-lora32_v21_tcxo release-featheresp32 release-genericesp32 release-heltec32_v2 release-heltec32_v3 release-heltec32_v4 release-heltec32_v2_extled release-heltec_t114 release-techo release-rnode_ng_20 release-rnode_ng_21 release-t3s3 release-t3s3_sx127x release-t3s3_sx1280_pa release-tdeck release-tbeam_supreme release-rak4631 release-xiao_s3 release-hashes
release-all: console-site spiffs-image release-tbeam release-tbeam_sx1262 release-lora32_v10 release-lora32_v20 release-lora32_v21 release-lora32_v10_extled release-lora32_v20_extled release-lora32_v21_extled release-lora32_v21_tcxo release-featheresp32 release-genericesp32 release-heltec32_v2 release-heltec32_v3 release-heltec32_v4 release-heltec32_v2_extled release-heltec_t114 release-techo release-rnode_ng_20 release-rnode_ng_21 release-t3s3 release-t3s3_sx127x release-t3s3_sx1280_pa release-tdeck release-tbeam_supreme release-rak4631 release-xiao_s3 release-t1000e release-hashes

release-hashes:
python ./release_hashes.py > ./Release/release.json
Expand Down Expand Up @@ -507,6 +518,10 @@ release-techo:
cp build/adafruit.nrf52.pca10056/RNode_Firmware.ino.hex build/rnode_firmware_techo.hex
adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_techo.hex Release/rnode_firmware_techo.zip

release-t1000e:
pio run -e seeed_t1000e
cp .pio/build/seeed_t1000e/firmware.zip Release/rnode_firmware_t1000e.zip

release-xiao_s3:
arduino-cli compile --fqbn "esp32:esp32:XIAO_ESP32S3" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3E\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_xiao_esp32s3.boot_app0
Expand Down
1 change: 1 addition & 0 deletions Modem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define SX1278 0x02
#define SX1262 0x03
#define SX1280 0x04
#define LR1110 0x05

// Sentinel for native targets where the modem family is chosen at runtime
// from rnoded.conf. Drivers and MODEM-gated call sites use this to
Expand Down
37 changes: 35 additions & 2 deletions Power.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,24 @@ float pmu_temperature = PMU_TEMP_MIN-1;
bool bat_voltage_dropping = false;
float bat_delay_v = 0;
float bat_state_change_v = 0;
#elif BOARD_MODEL == BOARD_T1000E
// Seeed T1000-E: battery on P0.02/AIN0 via a 1:2 divider, ADC reference
// 3.0 V, 12-bit resolution (Seeed datasheet / MeshCore T1000eBoard).
#define BAT_V_MIN 3.15
#define BAT_V_MAX 4.2
#define BAT_V_CHG 4.48
#define BAT_V_FLOAT 4.33
#define BAT_SAMPLES 7
const uint8_t pin_vbat = BATTERY_PIN; // P0.02
float bat_p_samples[BAT_SAMPLES];
float bat_v_samples[BAT_SAMPLES];
uint8_t bat_samples_count = 0;
int bat_discharging_samples = 0;
int bat_charging_samples = 0;
int bat_charged_samples = 0;
bool bat_voltage_dropping = false;
float bat_delay_v = 0;
float bat_state_change_v = 0;
#endif

uint32_t last_pmu_update = 0;
Expand All @@ -235,7 +253,7 @@ void measure_temperature() {
}

void measure_battery() {
#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_HELTEC_TRACKER_V2 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401
#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_HELTEC_TRACKER_V2 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401 || BOARD_MODEL == BOARD_T1000E
battery_installed = true;
#if BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_HELTEC_TRACKER_V2
battery_indeterminate = false;
Expand All @@ -258,6 +276,9 @@ void measure_battery() {
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.007067;
#elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.005068;
#elif BOARD_MODEL == BOARD_T1000E
// 12-bit ADC, 1:2 divider, 3.0 V reference (Seeed T1000-E).
float battery_measurement = (float)(analogRead(pin_vbat)) * 3.0f * 2.0f / 4096.0f;
#else
float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*7.26;
#endif
Expand Down Expand Up @@ -438,6 +459,15 @@ void measure_battery() {
}

void update_pmu() {
#if MCU_VARIANT == MCU_NRF52
// The nRF52840 POWER peripheral exposes USB VBUS presence directly.
// VBUSDETECT reads true whenever USB power is connected (independent
// of enumeration), which is what the LED/battery UX treats as
// "external power". Sampled every pass so hot-plug is reflected promptly.
#if defined(NRF_POWER) && defined(POWER_USBREGSTATUS_VBUSDETECT_Msk)
external_power = (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0;
#endif
#endif
if (millis()-last_pmu_update >= pmu_update_interval) {
measure_battery();
measure_temperature();
Expand All @@ -450,11 +480,14 @@ bool init_pmu() {
pmu_temp_sensor_ready = true;
#endif

#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401
#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401 || BOARD_MODEL == BOARD_T1000E
pinMode(pin_vbat, INPUT);
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_RAK3401
analogReference(AR_INTERNAL_3_0);
analogReadResolution(10);
#elif BOARD_MODEL == BOARD_T1000E
analogReference(AR_INTERNAL_3_0);
analogReadResolution(12);
#endif
return true;
#elif BOARD_MODEL == BOARD_HELTEC32_V3
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,23 @@ Build a single environment (board):
pio run -e ttgo-t-beam
pio run -e heltec-wireless-tracker-v2
pio run -e wiscore_rak4631
pio run -e seeed_t1000e
```

Build and upload a single environment (board):
```
pio run -e ttgo-t-beam -t upload
pio run -e heltec-wireless-tracker-v2 -t upload
pio run -e wiscore_rak4631 -t upload
pio run -e seeed_t1000e -t upload
```

Build and package a single environment (board):
```
pio run -e ttgo-t-beam -t package
pio run -e heltec-wireless-tracker-v2 -t package
pio run -e wiscore_rak4631 -t package
pio run -e seeed_t1000e -t package
```

On erased EEPROM, the Heltec Wireless Tracker V2 enables BLE and explicitly
Expand All @@ -97,6 +100,31 @@ Build and package all environments (boards):
pio run -t package
```

### Flashing the Seeed Tracker T1000-E (UF2)

Unlike the other supported boards, the T1000-E does **not** flash over serial
DFU. Its bootloader exposes a **UF2 mass-storage** volume, and you flash by
dragging a `.uf2` firmware file onto it.

The build produces this file automatically alongside the usual artifacts:

```
.pio/build/seeed_t1000e/rnode_firmware_t1000e.uf2
```
(generate it explicitly with `pio run -e seeed_t1000e -t uf2`, or find the
packaged copy at `Release/rnode_firmware_t1000e.uf2` after `-t package`.)

To flash:

1. Put the board in **DFU mode**: rapidly disconnect and reconnect the USB
cable (or double-tap the reset button) until a `T1000-E` drive mounts.
2. Drag `rnode_firmware_t1000e.uf2` onto that drive. The board flashes and
reboots into the application automatically when the copy finishes.

`pio run -e seeed_t1000e -t upload` automates this: it builds the `.uf2`,
looks for the mounted DFU volume and copies the file there. If no volume is
found it prints the drag-and-drop instructions above as a fallback.

Write version info:
python release_hashes.py > Release/release.json

Expand Down
26 changes: 23 additions & 3 deletions RNode_Firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,8 @@ void setup() {
LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen);
#elif MODEM == SX1280
LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen, pin_txen);
#elif MODEM == LR1110
LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy);
#endif

#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 || MCU_VARIANT == MCU_NATIVE
Expand Down Expand Up @@ -1359,6 +1361,9 @@ void ISR_VECT receive_callback(int packet_size) {
}

if (ready) {
#if BOARD_MODEL == BOARD_T1000E
led_event_blip(1);
#endif
#if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 && MCU_VARIANT != MCU_NATIVE
// We first signal the RSSI of the
// recieved packet to the host.
Expand Down Expand Up @@ -1485,7 +1490,11 @@ volatile bool queue_flushing = false;
void flush_queue(void) {
if (!queue_flushing) {
queue_flushing = true;
led_tx_on();
#if BOARD_MODEL == BOARD_T1000E
led_event_blip(2);
#else
led_tx_on();
#endif

#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 || MCU_VARIANT == MCU_NATIVE
while (!fifo16_isempty(&packet_starts)) {
Expand Down Expand Up @@ -1525,7 +1534,12 @@ void flush_queue(void) {

void pop_queue() {
if (!queue_flushing) {
queue_flushing = true; led_tx_on();
queue_flushing = true;
#if BOARD_MODEL == BOARD_T1000E
led_event_blip(2);
#else
led_tx_on();
#endif

#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 || MCU_VARIANT == MCU_NATIVE
if (!fifo16_isempty(&packet_starts)) {
Expand Down Expand Up @@ -1596,7 +1610,7 @@ void add_airtime(uint16_t written) {
lora_symbols += lora_preamble_symbols + 0.25 + 8;
packet_cost_ms += lora_symbols * lora_symbol_time_ms;

#elif MODEM == SX1262 || MODEM == SX1280
#elif MODEM == SX1262 || MODEM == SX1280 || MODEM == LR1110
if (lora_sf < 7) {
lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*lora_sf + PHY_HEADER_LORA_SYMBOLS);
lora_symbols /= 4*lora_sf;
Expand Down Expand Up @@ -1845,6 +1859,9 @@ void serial_callback(uint8_t sbyte) {
#else
if (txp > 13) txp = 13;
#endif
#elif MODEM == LR1110
// LR1110 high-power PA tops out at 22 dBm.
if (txp > 22) txp = 22;
#else
if (txp > 17) txp = 17;
#endif
Expand Down Expand Up @@ -2405,6 +2422,7 @@ void update_modem_status() {
if (carrier_detected) { dcd = true; } else { dcd = false; }

dcd_led = dcd;
#if BOARD_MODEL != BOARD_T1000E
if (dcd_led) { led_rx_on(); }
else {
if (interference_detected && noise_floor_sampled) {
Expand All @@ -2414,6 +2432,7 @@ void update_modem_status() {
else { led_rx_off(); led_id_off(); }
}
}
#endif

update_noise_floor();
}
Expand Down Expand Up @@ -2718,6 +2737,7 @@ void loop() {

tx_queue_handler();
check_modem_status();
led_update();
#if MCU_VARIANT == MCU_NATIVE
// Drop a TCP host client that's gone silent past the idle window.
// poll_accept() in buffer_serial() handles the connect side; this
Expand Down
Loading