bt_state = BT_STATE_ON;
}
++ void bt_flush() { if (bt_state == BT_STATE_CONNECTED) { SerialBT.flushTXD(); } }
++
void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
BLEConnection* connection = Bluefruit.Connection(conn_handle);
#define BOARD_HELTEC32_V3 0x3A
#define MODEL_C5 0xC5 // Heltec Lora32 v3, 433 MHz
#define MODEL_CA 0xCA // Heltec Lora32 v3, 868 MHz
++
++ #define PRODUCT_H_W_PAPER 0xC3
++ #define BOARD_H_W_PAPER 0x3E
++ #define MODEL_C8 0xC8
+
+ #define PRODUCT_RAK4631 0x10 // RAK4631 - sold by RAKWireless
+ #define BOARD_RAK4631 0x51
+ #define MODEL_11 0x11 // RAK4631, 433 MHz
+ #define MODEL_12 0x12 // RAK4631, 868 MHz
+ #define MODEL_13 0x13 // RAK4631, 433MHz with WisBlock SX1280 module (LIBSYS002)
+ #define MODEL_14 0x14 // RAK4631, 868/915 MHz with WisBlock SX1280 module (LIBSYS002)
+
+ #define PRODUCT_OPENCOM_XL 0x20 // openCom XL - sold by Liberated Embedded Systems
+ #define BOARD_OPENCOM_XL 0x52
+ #define MODEL_21 0x21 // openCom XL, 868/915 MHz
+
+ #define BOARD_E22_ESP32 0x45 // Custom Ebyte E22 board design for meshtastic, source:
+ // https://github.com/NanoVHF/Meshtastic-DIY/blob/main/Schematics/E-Byte_E22/Mesh_Ebyte_E22-XXXM30S.pdf
- #define PRODUCT_RAK4631 0x10
- #define BOARD_RAK4631 0x51
- #define MODEL_11 0x11 // RAK4631, 433 Mhz
- #define MODEL_12 0x12 // RAK4631, 868 Mhz
-
+ #define PRODUCT_HELTEC_T114 0xC2 // Heltec Mesh Node T114
+ #define BOARD_HELTEC_T114 0x3C
+ #define MODEL_C6 0xC6 // Heltec Mesh Node T114, 470-510 MHz
+ #define MODEL_C7 0xC7 // Heltec Mesh Node T114, 863-928 MHz
+
+ #define PRODUCT_TECHO 0x15 // LilyGO T-Echo devices
+ #define BOARD_TECHO 0x44
+ #define MODEL_16 0x16 // T-Echo 433 MHz
+ #define MODEL_17 0x17 // T-Echo 868/915 MHz
+
#define PRODUCT_HMBRW 0xF0
#define BOARD_HMBRW 0x32
#define BOARD_HUZZAH32 0x34
const int pin_led_rx = 2;
const int pin_led_tx = 4;
- #if MODEM == SX1262
- #define HAS_TCXO true
- #define HAS_BUSY true
- #define DIO2_AS_RF_SWITCH true
- #define OCP_TUNED 0x38
- const int pin_busy = 32;
- const int pin_dio = 33;
- const int pin_tcxo_enable = -1;
- #else
- const int pin_dio = 26;
+ #if BOARD_VARIANT == MODEL_E4 || BOARD_VARIANT == MODEL_E9
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX127X
+ {
+ true, // DEFAULT_SPI
+ false, // HAS_TCXO
+ false // DIO2_AS_RF_SWITCH
+ },
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX127X
+ {
+ 18, // pin_ss
+ -1, // pin_sclk
+ -1, // pin_mosi
+ -1, // pin_miso
+ -1, // pin_busy
+ 26, // pin_dio
+ 23, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+
+ #elif BOARD_VARIANT == MODEL_E3 || BOARD_VARIANT == MODEL_E8
- const uint8_t interfaces[INTERFACE_COUNT] = {SX126X};
++ #define OCP_TUNED 0x38
++ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ true, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ },
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 18, // pin_ss
+ -1, // pin_sclk
+ -1, // pin_mosi
+ -1, // pin_miso
+ 32, // pin_busy
+ 33, // pin_dio
+ 23, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
#endif
#elif BOARD_MODEL == BOARD_HUZZAH32
#define HAS_SLEEP true
#define PIN_WAKEUP GPIO_NUM_0
#define WAKEUP_LEVEL 0
- #define MODEM SX1262
- #define HAS_TCXO true
- const int pin_tcxo_enable = -1;
- #define HAS_BUSY true
- #define DIO2_AS_RF_SWITCH true
+ #define INTERFACE_COUNT 1
+ #define OCP_TUNED 0x38
+
+ const int pin_btn_usr1 = 0;
+
+ #if defined(EXTERNAL_LEDS)
+ const int pin_led_rx = 13;
+ const int pin_led_tx = 14;
+ #else
+ const int pin_led_rx = 35;
+ const int pin_led_tx = 35;
+ #endif
+
++ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
++ const bool interface_cfg[INTERFACE_COUNT][3] = {
++ // SX1262
++ {
++ true, // DEFAULT_SPI
++ true, // HAS_TCXO
++ true // DIO2_AS_RF_SWITCH
++ },
++ };
++ const int8_t interface_pins[INTERFACE_COUNT][10] = {
++ // SX1262
++ {
++ 8, // pin_ss
++ 9, // pin_sclk
++ 10, // pin_mosi
++ 11, // pin_miso
++ 13, // pin_busy
++ 14, // pin_dio
++ 12, // pin_reset
++ -1, // pin_txen
++ -1, // pin_rxen
++ -1 // pin_tcxo_enable
++ }
++ };
++
++ #elif BOARD_MODEL == BOARD_H_W_PAPER
++ // Heltec Wireless Paper. Basically the Heltec LoRa32 v3 with an eink display.
++ // TODO, need to confirm that the display is a 213BN
++ #define IS_ESP32S3 true
++ #define HAS_DISPLAY true
++ #define HAS_BLUETOOTH false
++ #define HAS_BLE true
++ #define HAS_PMU true
++ #define HAS_CONSOLE true
++ #define HAS_EEPROM true
++ #define HAS_INPUT true
++ #define HAS_SLEEP true
++ #define HAS_DISPLAY true
++ #define DISPLAY EINK_BW
++ #define DISPLAY_SCALE_OVERRIDE true
++ #define DISPLAY_SCALE 1.90625
++ #define DISPLAY_MODEL GxEPD2_213_BN
++ #define PIN_WAKEUP GPIO_NUM_0
++ #define WAKEUP_LEVEL 0
++ #define INTERFACE_COUNT 1
++ #define OCP_TUNED 0x38
- // Following pins are for the SX1262
- const int pin_cs = 8;
- const int pin_busy = 13;
- const int pin_dio = 14;
- const int pin_reset = 12;
- const int pin_mosi = 10;
- const int pin_miso = 11;
- const int pin_sclk = 9;
+ const int pin_btn_usr1 = 0;
+
+ #if defined(EXTERNAL_LEDS)
+ const int pin_led_rx = 13;
+ const int pin_led_tx = 14;
+ #else
+ const int pin_led_rx = 35;
+ const int pin_led_tx = 35;
+ #endif
+
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ true, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ },
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 8, // pin_ss
+ 9, // pin_sclk
+ 10, // pin_mosi
+ 11, // pin_miso
+ 13, // pin_busy
+ 14, // pin_dio
+ 12, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+
++ const int pin_disp_cs = 4;
++ const int pin_disp_dc = 5;
++ const int pin_disp_reset = 6;
++ const int pin_disp_busy = 7;
++ const int pin_disp_en = 45;
+
#elif BOARD_MODEL == BOARD_RNODE_NG_20
#define HAS_DISPLAY true
+ #define DISPLAY OLED
#define HAS_BLUETOOTH true
#define HAS_NP true
#define HAS_CONSOLE true
#endif
#endif
-
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX127X
+ {
+ true, // DEFAULT_SPI
+ false, // HAS_TCXO
+ false // DIO2_AS_RF_SWITCH
+ },
+ };
+ const uint8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX127X
+ {
+ 18, // pin_ss
+ -1, // pin_sclk
+ -1, // pin_mosi
+ -1, // pin_miso
+ -1, // pin_busy
+ 26, // pin_dio
+ 23, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
++
++ // todo ce-ise all boards below here
+
#elif BOARD_MODEL == BOARD_T3S3
#define IS_ESP32S3 true
#define HAS_DISPLAY true
- #define HAS_CONSOLE false
+ #define DISPLAY OLED
+ #define HAS_CONSOLE true
#define HAS_BLUETOOTH false
#define HAS_BLE true
#define HAS_PMU true
#endif
#endif
++ #elif BOARD_MODEL == BOARD_TDECK
++ #define IS_ESP32S3 true
++ #define MODEM SX1262
++ #define DIO2_AS_RF_SWITCH true
++ #define HAS_BUSY true
++ #define HAS_TCXO true
++
++ #define HAS_DISPLAY false
++
++ const int pin_poweron = 10;
++ const int pin_btn_usr1 = 0;
++
++ const int pin_cs = 9;
++ const int pin_reset = 17;
++ const int pin_sclk = 40;
++ const int pin_mosi = 41;
++ const int pin_miso = 38;
++ const int pin_tcxo_enable = -1;
++ const int pin_dio = 45;
++ const int pin_busy = 13;
++
++ const int SD_MISO = 38;
++ const int SD_MOSI = 41;
++ const int SD_CLK = 40;
++ const int SD_CS = 39;
++
++ const int DISPLAY_DC = 11;
++ const int DISPLAY_CS = 12;
++ const int DISPLAY_MISO = 38;
++ const int DISPLAY_MOSI = 41;
++ const int DISPLAY_CLK = 40;
++ const int DISPLAY_BL_PIN = 42;
++
++ #if HAS_NP == false
++ #if defined(EXTERNAL_LEDS)
++ const int pin_led_rx = 43;
++ const int pin_led_tx = 43;
++ #else
++ const int pin_led_rx = 43;
++ const int pin_led_tx = 43;
++ #endif
++ #endif
++
++ #elif BOARD_MODEL == BOARD_TBEAM_S_V1
++ #define IS_ESP32S3 true
++ #define MODEM SX1262
++ #define DIO2_AS_RF_SWITCH true
++ #define HAS_BUSY true
++ #define HAS_TCXO true
++ #define OCP_TUNED 0x38
++
++ #define HAS_DISPLAY true
++ #define HAS_CONSOLE true
++ #define HAS_BLUETOOTH false
++ #define HAS_BLE true
++ #define HAS_PMU true
++ #define HAS_NP false
++ #define HAS_SD false
++ #define HAS_EEPROM true
++
++ #define HAS_INPUT true
++ #define HAS_SLEEP false
++
++ #define PMU_IRQ 40
++ #define I2C_SCL 41
++ #define I2C_SDA 42
++
++ const int pin_btn_usr1 = 0;
++
++ const int pin_cs = 10;
++ const int pin_reset = 5;
++ const int pin_sclk = 12;
++ const int pin_mosi = 11;
++ const int pin_miso = 13;
++ const int pin_tcxo_enable = -1;
++ const int pin_dio = 1;
++ const int pin_busy = 4;
++
++ const int SD_MISO = 37;
++ const int SD_MOSI = 35;
++ const int SD_CLK = 36;
++ const int SD_CS = 47;
++
++ const int IMU_CS = 34;
++
++ #if HAS_NP == false
++ #if defined(EXTERNAL_LEDS)
++ const int pin_led_rx = 43;
++ const int pin_led_tx = 43;
++ #else
++ const int pin_led_rx = 43;
++ const int pin_led_tx = 43;
++ #endif
++ #endif
++
+ #if BOARD_VARIANT == MODEL_A1
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ false, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ },
+ };
+ const uint8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 7, // pin_ss
+ 5, // pin_sclk
+ 6, // pin_mosi
+ 3, // pin_miso
+ 34, // pin_busy
+ 33, // pin_dio
+ 8, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+ #elif BOARD_VARIANT == MODEL_AC // SX1280 with PA
+ #define CONFIG_UART_BUFFER_SIZE 40000
+ #define CONFIG_QUEUE_0_SIZE 40000
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX1280};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1280
+ {
+ false, // DEFAULT_SPI
+ false, // HAS_TCXO
+ false // DIO2_AS_RF_SWITCH
+ },
+ };
+ const uint8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1280
+ {
+ 7, // pin_ss
+ 5, // pin_sclk
+ 6, // pin_mosi
+ 3, // pin_miso
+ 36, // pin_busy
+ 9, // pin_dio
+ 8, // pin_reset
+ 10, // pin_txen
+ 21, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+ #endif
+
+ #elif BOARD_MODEL == BOARD_E22_ESP32
+ #define HAS_DISPLAY true
+ #define DISPLAY OLED
+ #define HAS_BLUETOOTH true
+ #define HAS_BLE true
+ #define HAS_CONSOLE true
+ #define HAS_SD false
+ #define HAS_EEPROM true
+ #define I2C_SDA 21
+ #define I2C_SCL 22
+ #define INTERFACE_COUNT 1
+ const int pin_led_rx = 2;
+ const int pin_led_tx = 4;
+
+ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ true, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ },
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 18, // pin_ss
+ 5, // pin_sclk
+ 27, // pin_mosi
+ 19, // pin_miso
+ 32, // pin_busy
+ 33, // pin_dio
+ 23, // pin_reset
+ -1, // pin_txen
+ 14, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+
#elif BOARD_MODEL == BOARD_TDECK
#define IS_ESP32S3 true
#define MODEM SX1262
const int pin_led_tx = 43;
#endif
#endif
+ #endif
- #else
- #error An unsupported ESP32 board was selected. Cannot compile RNode firmware.
- #endif
#elif MCU_VARIANT == MCU_NRF52
- #if BOARD_MODEL == BOARD_RAK4631
- #define HAS_EEPROM false
+ #if BOARD_MODEL == BOARD_TECHO
+ #define VALIDATE_FIRMWARE false
+ #define HAS_INPUT true
+ //#define GPS_BAUD_RATE 115200
+ //#define PIN_GPS_TX 41
+ //#define PIN_GPS_RX 40
+ #define EEPROM_SIZE 296
+ #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
+ //#define HAS_EEPROM true
+ //#define HAS_SD true
#define HAS_DISPLAY true
- const uint8_t interfaces[INTERFACE_COUNT] = {SX126X};
+ #define DISPLAY EINK_BW
+ #define DISPLAY_MODEL GxEPD2_154_D67
+ //#define HAS_CONSOLE true
+ //#define HAS_TXCO true
+ //#define HAS_BLE true
+ //#define HAS_PMU true
+ #define CONFIG_UART_BUFFER_SIZE 40000
+ #define CONFIG_QUEUE_0_SIZE 6144
+ #define CONFIG_QUEUE_MAX_LENGTH 200
+ //#define BLE_MANUFACTURER "LilyGO"
+ //#define BLE_MODEL "T-Echo"
+ #define INTERFACE_COUNT 1
+ //#define I2C_SDA 26
+ //#define I2C_SCL 27
+ #define CONFIG_QUEUE_1_SIZE 40000
+ // first interface in list is the primary
- #define HAS_DISPLAY true
- #define DISPLAY EINK_BW
++ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ false, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ }
+ };
+
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 24, // pin_ss
+ 19, // pin_sclk
+ 22, // pin_mosi
+ 23, // pin_miso
+ 17, // pin_busy
+ 20, // pin_dio
+ 25, // pin_reset
+ -1, // pin_txen
+ -1, // pin_rxen
+ 21 // pin_tcxo_enable
+ }
+ };
+
+ const int pin_disp_cs = 30;
+ const int pin_disp_dc = 28;
+ const int pin_disp_reset = 2;
+ const int pin_disp_busy = 3;
+ const int pin_disp_en = 43;
+ const int pin_disp_sck = 31;
+ const int pin_disp_mosi = 29;
+ const int pin_disp_miso = -1;
+
+ #define HAS_BACKLIGHT true
+ const int pin_btn_usr1 = 42;
+ const int pin_backlight = 43;
+
+ const int pin_led_rx = LED_BLUE;
+ const int pin_led_tx = LED_RED;
+ #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
+ #define HAS_EEPROM false
++ #define HAS_DISPLAY false
#define HAS_BLUETOOTH false
#define HAS_BLE true
#define HAS_CONSOLE false
#define BLE_MANUFACTURER "RAK Wireless"
#define BLE_MODEL "RAK4640"
- const int pin_btn_usr1 = 9;
+ #if BOARD_VARIANT == MODEL_11 || BOARD_VARIANT == MODEL_12
+ #define INTERFACE_COUNT 1
+
+ // first interface in list is the primary
- const uint8_t interfaces[INTERFACE_COUNT] = {SX126X};
++ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ false, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ }
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 42, // pin_ss
+ 43, // pin_sclk
+ 44, // pin_mosi
+ 45, // pin_miso
+ 46, // pin_busy
+ 47, // pin_dio
+ 38, // pin_reset
+ -1, // pin_txen
+ 37, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+ #elif BOARD_VARIANT == MODEL_13 || BOARD_VARIANT == MODEL_14 || BOARD_VARIANT == MODEL_21
++ #define HAS_DISPLAY true
++ #define DISPLAY EINK_BW
++ #define DISPLAY_SCALE_OVERRIDE true
++ #define DISPLAY_SCALE 1.90625
++ #define DISPLAY_MODEL GxEPD2_213_BN
+ #define INTERFACE_COUNT 2
+
+ #define CONFIG_QUEUE_1_SIZE 40000
+ #define CONFIG_UART_BUFFER_SIZE 40000 // \todo, does it have to be this big?
+
+ // first interface in list is the primary
- const uint8_t interfaces[INTERFACE_COUNT] = {SX126X, SX128X};
++ const uint8_t interfaces[INTERFACE_COUNT] = {SX1262, SX1280};
+ const bool interface_cfg[INTERFACE_COUNT][3] = {
+ // SX1262
+ {
+ false, // DEFAULT_SPI
+ true, // HAS_TCXO
+ true // DIO2_AS_RF_SWITCH
+ },
+ // SX1280
+ {
+ true, // DEFAULT_SPI
+ false,// HAS_TCXO
+ false // DIO2_AS_RF_SWITCH
+ }
+ };
+ const int8_t interface_pins[INTERFACE_COUNT][10] = {
+ // SX1262
+ {
+ 42, // pin_ss
+ 43, // pin_sclk
+ 44, // pin_mosi
+ 45, // pin_miso
+ 46, // pin_busy
+ 47, // pin_dio
+ 38, // pin_reset
+ -1, // pin_txen
+ 37, // pin_rxen
+ -1 // pin_tcxo_enable
+ },
+ // SX1280
+ {
+ 24, // pin_ss
+ 3, // pin_sclk
+ 30, // pin_mosi
+ 29, // pin_miso
+ 25, // pin_busy
+ 15, // pin_dio
+ 16, // pin_reset
+ 20, // pin_txen
+ 19, // pin_rxen
+ -1 // pin_tcxo_enable
+ }
+ };
+ #endif
+
- // Following pins are for the sx1262
- const int pin_rxen = 37;
- const int pin_txen = -1;
- const int pin_reset = 38;
- const int pin_cs = 42;
- const int pin_sclk = 43;
- const int pin_mosi = 44;
- const int pin_miso = 45;
- const int pin_busy = 46;
- const int pin_dio = 47;
+ const int pin_disp_cs = SS;
+ const int pin_disp_dc = WB_IO1;
+ const int pin_disp_reset = -1;
+ const int pin_disp_busy = WB_IO4;
+ const int pin_disp_en = WB_IO2;
+
+ const int pin_btn_usr1 = 9;
const int pin_led_rx = LED_BLUE;
const int pin_led_tx = LED_GREEN;
- const int pin_tcxo_enable = -1;
+ #elif BOARD_MODEL == BOARD_TECHO
+ #define _PINNUM(port, pin) ((port) * 32 + (pin))
+ #define MODEM SX1262
+ #define HAS_EEPROM 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 HAS_SLEEP true
+ #define BLE_MANUFACTURER "LilyGO"
+ #define BLE_MODEL "T-Echo"
+
+ #define HAS_INPUT true
+ #define EEPROM_SIZE 296
+ #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
+
+ #define CONFIG_UART_BUFFER_SIZE 32768
+ #define CONFIG_QUEUE_SIZE 6144
+ #define CONFIG_QUEUE_MAX_LENGTH 200
+
+ #define HAS_DISPLAY true
++ #define DISPLAY EINK_BW
+ #define HAS_BACKLIGHT true
+ #define DISPLAY_SCALE 1
+
+ #define LED_ON LOW
+ #define LED_OFF HIGH
+ #define PIN_LED_GREEN _PINNUM(1, 1)
+ #define PIN_LED_RED _PINNUM(1, 3)
+ #define PIN_LED_BLUE _PINNUM(0, 14)
+ #define PIN_VEXT_EN _PINNUM(0, 12)
+
+ const int pin_disp_cs = 30;
+ const int pin_disp_dc = 28;
+ const int pin_disp_reset = 2;
+ const int pin_disp_busy = 3;
+ const int pin_disp_en = -1;
+ const int pin_disp_sck = 31;
+ const int pin_disp_mosi = 29;
+ const int pin_disp_miso = -1;
+ const int pin_backlight = 43;
+
+ const int pin_btn_usr1 = _PINNUM(1, 10);
+ const int pin_btn_touch = _PINNUM(0, 11);
+
+ const int pin_reset = 25;
+ const int pin_cs = 24;
+ const int pin_sclk = 19;
+ const int pin_mosi = 22;
+ const int pin_miso = 23;
+ const int pin_busy = 17;
+ const int pin_dio = 20;
+ const int pin_tcxo_enable = 21;
+ const int pin_led_rx = PIN_LED_BLUE;
+ const int pin_led_tx = PIN_LED_RED;
+
+ #elif BOARD_MODEL == BOARD_HELTEC_T114
+ #define MODEM SX1262
+ #define HAS_EEPROM false
+ #define HAS_DISPLAY true
+ #define HAS_BLUETOOTH false
+ #define HAS_BLE true
+ #define HAS_CONSOLE false
+ #define HAS_PMU true
+ #define HAS_NP true
+ #define HAS_SD false
+ #define HAS_TCXO true
+ #define HAS_BUSY true
+ #define HAS_INPUT true
+ #define HAS_SLEEP true
+ #define DIO2_AS_RF_SWITCH 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 "Heltec"
+ #define BLE_MODEL "T114"
+
+ #define PIN_T114_ADC_EN 6
+ #define PIN_VEXT_EN 21
+
+ // LED
+ #define LED_T114_GREEN 3
+ #define PIN_T114_LED 14
+ #define NP_M 1
+ const int pin_np = PIN_T114_LED;
+
+ // SPI
+ #define PIN_T114_MOSI 22
+ #define PIN_T114_MISO 23
+ #define PIN_T114_SCK 19
+ #define PIN_T114_SS 24
+
+ // SX1262
+ #define PIN_T114_RST 25
+ #define PIN_T114_DIO1 20
+ #define PIN_T114_BUSY 17
+
+ // TFT
++ #define DISPLAY_SCALE_OVERRIDE true
+ #define DISPLAY_SCALE 2
+ #define PIN_T114_TFT_MOSI 9
+ #define PIN_T114_TFT_MISO 11 // not connected
+ #define PIN_T114_TFT_SCK 8
+ #define PIN_T114_TFT_SS 11
+ #define PIN_T114_TFT_DC 12
+ #define PIN_T114_TFT_RST 2
+ #define PIN_T114_TFT_EN 3
+ #define PIN_T114_TFT_BLGT 15
+
+ // pins for buttons on Heltec T114
+ const int pin_btn_usr1 = 42;
+
+ // pins for sx1262 on Heltec T114
+ const int pin_reset = PIN_T114_RST;
+ const int pin_cs = PIN_T114_SS;
+ const int pin_sclk = PIN_T114_SCK;
+ const int pin_mosi = PIN_T114_MOSI;
+ const int pin_miso = PIN_T114_MISO;
+ const int pin_busy = PIN_T114_BUSY;
+ const int pin_dio = PIN_T114_DIO1;
+ const int pin_led_rx = 35;
+ const int pin_led_tx = 35;
+ const int pin_tcxo_enable = -1;
+
+ // pins for ST7789 display on Heltec T114
+ const int DISPLAY_DC = PIN_T114_TFT_DC;
+ const int DISPLAY_CS = PIN_T114_TFT_SS;
+ const int DISPLAY_MISO = PIN_T114_TFT_MISO;
+ const int DISPLAY_MOSI = PIN_T114_TFT_MOSI;
+ const int DISPLAY_CLK = PIN_T114_TFT_SCK;
+ const int DISPLAY_BL_PIN = PIN_T114_TFT_BLGT;
+ const int DISPLAY_RST = PIN_T114_TFT_RST;
+
#else
#error An unsupported nRF board was selected. Cannot compile RNode firmware.
#endif
#endif
- #ifndef HAS_RF_SWITCH_RX_TX
- const int pin_rxen = -1;
- const int pin_txen = -1;
- #endif
-
+
+ #ifndef DISPLAY_SCALE
+ #define DISPLAY_SCALE 1
+ #endif
+
- #ifndef DIO2_AS_RF_SWITCH
- #define DIO2_AS_RF_SWITCH false
- #endif
-
+ #ifndef HAS_BUSY
+ const int pin_busy = -1;
+ #endif
+
+ #ifndef LED_ON
+ #define LED_ON HIGH
+ #endif
+
+ #ifndef LED_OFF
+ #define LED_OFF LOW
+ #endif
+
+ // Default OCP value if not specified
+ // in board configuration
+ #ifndef OCP_TUNED
+ #define OCP_TUNED 0x38
+ #endif
+
+ #ifndef NP_M
+ #define NP_M 0.15
+ #endif
+
#endif
// packet RSSI register
const int rssi_offset = 157;
- // Default LoRa settings
- const int lora_rx_turnaround_ms = 66;
- const int lora_post_tx_yield_slots = 6;
- #define LORA_CAD_SYMBOLS 3
- // Default LoRa settings
- #define PHY_HEADER_LORA_SYMBOLS 20
- #define PHY_CRC_LORA_BITS 16
- #define LORA_PREAMBLE_SYMBOLS_MIN 18
- #define LORA_PREAMBLE_TARGET_MS 24
- #define LORA_PREAMBLE_FAST_DELTA 18
- #define LORA_FAST_THRESHOLD_BPS 30E3
- #define LORA_LIMIT_THRESHOLD_BPS 60E3
- long lora_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
- long lora_preamble_time_ms = 0;
- long lora_header_time_ms = 0;
- float lora_symbol_time_ms = 0.0;
- float lora_symbol_rate = 0.0;
- float lora_us_per_byte = 0.0;
- bool lora_low_datarate = false;
- bool lora_limit_rate = false;
-
- // CSMA Parameters
- #define CSMA_SIFS_MS 0
- #define CSMA_POST_TX_YIELD_SLOTS 3
- #define CSMA_SLOT_MAX_MS 100
- #define CSMA_SLOT_MIN_MS 24
- #define CSMA_SLOT_MIN_FAST_DELTA 18
- #define CSMA_SLOT_SYMBOLS 12
- #define CSMA_CW_BANDS 4
- #define CSMA_CW_MIN 0
- #define CSMA_CW_PER_BAND_WINDOWS 15
- #define CSMA_BAND_1_MAX_AIRTIME 7
- #define CSMA_BAND_N_MIN_AIRTIME 85
- #define CSMA_INFR_THRESHOLD_DB 12
- bool interference_detected = false;
- bool avoid_interference = true;
- int csma_slot_ms = CSMA_SLOT_MIN_MS;
- unsigned long difs_ms = CSMA_SIFS_MS + 2*csma_slot_ms;
- unsigned long difs_wait_start = -1;
- unsigned long cw_wait_start = -1;
- unsigned long cw_wait_target = -1;
- unsigned long cw_wait_passed = 0;
- int csma_cw = -1;
- uint8_t cw_band = 1;
- uint8_t cw_min = 0;
- uint8_t cw_max = CSMA_CW_PER_BAND_WINDOWS;
-
- // LoRa settings
- int lora_sf = 0;
- int lora_cr = 5;
- int lora_txp = 0xFF;
- uint32_t lora_bw = 0;
- uint32_t lora_freq = 0;
- uint32_t lora_bitrate = 0;
--
// Operational variables
- bool radio_locked = true;
- bool radio_online = false;
bool community_fw = true;
bool hw_ready = false;
- bool radio_error = false;
bool disp_ready = false;
bool pmu_ready = false;
bool promisc = false;
uint8_t model = 0x00;
uint8_t hwrev = 0x00;
- #define NOISE_FLOOR_SAMPLES 64
- int noise_floor = -292;
+ int current_rssi = -292;
int last_rssi = -292;
uint8_t last_rssi_raw = 0x00;
uint8_t last_snr_raw = 0x80;
uint32_t stat_rx = 0;
uint32_t stat_tx = 0;
- unsigned long last_tx = 0;
- unsigned long last_rx = 0;
- #define STATUS_INTERVAL_MS 3
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
- #define DCD_SAMPLES 2500
- #define UTIL_UPDATE_INTERVAL_MS 1000
- #define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
- #define AIRTIME_LONGTERM 3600
- #define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
- #define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
- #define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
- bool util_samples[DCD_SAMPLES];
- uint16_t airtime_bins[AIRTIME_BINS];
- float longterm_bins[AIRTIME_BINS];
- int dcd_sample = 0;
- float local_channel_util = 0.0;
- float total_channel_util = 0.0;
- float longterm_channel_util = 0.0;
- float airtime = 0.0;
- float longterm_airtime = 0.0;
- #define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
- #endif
- float st_airtime_limit = 0.0;
- float lt_airtime_limit = 0.0;
- bool airtime_lock = false;
-
+ bool stat_signal_detected = false;
+ bool stat_signal_synced = false;
+ bool stat_rx_ongoing = false;
+ bool dcd = false;
+ bool dcd_led = false;
+ bool dcd_waiting = false;
+ long dcd_wait_until = 0;
+ uint16_t dcd_count = 0;
+ uint16_t dcd_threshold = 2;
+
- uint32_t status_interval_ms = STATUS_INTERVAL_MS;
+ uint32_t last_status_update = 0;
+ uint32_t last_dcd = 0;
+
++ uint32_t last_rx = 0;
++ uint32_t last_tx = 0;
+
// Power management
#define BATTERY_STATE_UNKNOWN 0x00
#define BATTERY_STATE_DISCHARGING 0x01
sourcepack:
@echo Packing firmware sources...
- zip --junk-paths -r build/pkg/rnode_firmware.zip ../arduino-cli.yaml ../src/ble/BLESerial.cpp ../src/ble/BLESerial.h ../Bluetooth.h ../Boards.h ../Config.h ../Console.h ../Device.h ../Display.h ../Framing.h ../Graphics.h .../Input.h ../Interfaces.h ../LICENSE ../Makefile ../src/misc/FIFOBuffer.c ../src/misc/FIFOBuffer.h ../src/misc/MD5.cpp ../src/misc/MD5.h ../partition_hashes ../Power.h ../README.md ../release_hashes.py ../RNode_Firmware_CE.ino ../ROM.h ../Radio.cpp ../Radio.hpp ../Utilities.h ../esp32_btbufs.py
- zip --junk-paths -r build/pkg/rnode_firmware.zip ../arduino-cli.yaml ../BLESerial.cpp ../BLESerial.h ../Bluetooth.h ../Boards.h ../Config.h ../Console.h ../Device.h ../Display.h ../Framing.h ../Graphics.h ../LICENSE ../Makefile ../MD5.cpp ../MD5.h ../partition_hashes ../Power.h ../README.md ../release_hashes.py ../RNode_Firmware.ino ../ROM.h ../sx126x.cpp ../sx126x.h ../sx127x.cpp ../sx127x.h ../sx128x.cpp ../sx128x.h ../Utilities.h ../esp32_btbufs.py
++ cd .. && zip -r build/pkg/rnode_firmware.zip * -x Builds/\* Console/\* Documentation/images/\* Documentation/RNode_v1_Manual.pdf Graphics/\* Python\ Module/\* Release/\* build/\* partition_hashes
data:
@echo Including assets...
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-#include "Graphics.h"
#include <Adafruit_GFX.h>
-#if BOARD_MODEL != BOARD_TECHO
- #if BOARD_MODEL == BOARD_TDECK
+#if DISPLAY == OLED
+#include <Wire.h>
+#include <Adafruit_SSD1306.h>
+#define DISPLAY_BLACK SSD1306_BLACK
+#define DISPLAY_WHITE SSD1306_WHITE
-
- #if BOARD_MODEL == BOARD_TDECK
- #include <Adafruit_ST7789.h>
- #define DISPLAY_BLACK ST77XX_BLACK
- #define DISPLAY_WHITE ST77XX_WHITE
- #elif BOARD_MODEL == BOARD_TBEAM_S_V1
- #include <Adafruit_SH110X.h>
- #define DISPLAY_BLACK ST77XX_BLACK
- #define DISPLAY_WHITE ST77XX_WHITE
- #else
- #include <Wire.h>
- #include <Adafruit_SSD1306.h>
- #endif
-
- #include "Fonts/Org_01.h"
-
+#define DISP_W 128
+#define DISP_H 64
++
+#elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
+void (*display_callback)();
- void display_add_callback(void (*callback)()) {
- display_callback = callback;
- }
- void busyCallback(const void* p) {
- display_callback();
- }
++void display_add_callback(void (*callback)()) { display_callback = callback; }
++void busyCallback(const void* p) { display_callback(); }
+#define DISPLAY_BLACK GxEPD_BLACK
+#define DISPLAY_WHITE GxEPD_WHITE
++
++#elif DISPLAY == ADAFRUIT_TFT
++ // t-deck
+ #include <Adafruit_ST7789.h>
- #elif BOARD_MODEL == BOARD_HELTEC_T114
++
++#elif DISPLAY == TFT
++ // t114
+ #include "ST7789.h"
- #define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
- #elif BOARD_MODEL == BOARD_TBEAM_S_V1
++ //#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
++
++#elif DISPLAY == MONO_OLED
++ // tbeam_s
+ #include <Adafruit_SH110X.h>
- #else
- #include <Wire.h>
- #include <Adafruit_SSD1306.h>
- #endif
+#endif
+
-#else
- void (*display_callback)();
- void display_add_callback(void (*callback)()) { display_callback = callback; }
- void busyCallback(const void* p) { display_callback(); }
- #define SSD1306_BLACK GxEPD_BLACK
- #define SSD1306_WHITE GxEPD_WHITE
- #include <GxEPD2_BW.h>
- #include <SPI.h>
+#if DISPLAY == EINK_BW
+// use GxEPD2 because adafruit EPD support for partial refresh is bad
+#include <GxEPD2_BW.h>
+#include <SPI.h>
+#elif DISPLAY == EINK_3C
+#include <GxEPD2_3C.h>
+#include <SPI.h>
#endif
#include "Fonts/Org_01.h"
-#define DISP_W 128
-#define DISP_H 64
+
#if BOARD_MODEL == BOARD_RNODE_NG_20 || BOARD_MODEL == BOARD_LORA32_V2_0
+ #if DISPLAY == OLED
#define DISP_RST -1
#define DISP_ADDR 0x3C
+ #endif
#elif BOARD_MODEL == BOARD_TBEAM
+ #if DISPLAY == OLED
#define DISP_RST 13
#define DISP_ADDR 0x3C
#define DISP_CUSTOM_ADDR true
#define DISP_ADDR 0x3C
#define SCL_OLED 17
#define SDA_OLED 18
- // todo: add support for OLED board
+ #endif
+#elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
+ #if DISPLAY == OLED
+ // RAK1921/SSD1306
+ #define DISP_RST -1
+ #define DISP_ADDR 0x3C
+ #define SCL_OLED 14
+ #define SDA_OLED 13
- #define DISPLAY_MODEL GxEPD2_213_BN
+ #elif DISPLAY == EINK_BW
+ // todo: change this to be defined in Boards.h in the future
+ #define DISP_W 250
+ #define DISP_H 122
+ #define DISP_ADDR -1
- #define DISPLAY_MODEL GxEPD2_213_Z98c
+ #elif DISPLAY == EINK_3C
+ #define DISP_W 250
+ #define DISP_H 122
+ #define DISP_ADDR -1
+ #endif
#elif BOARD_MODEL == BOARD_TECHO
SPIClass displaySPI = SPIClass(NRF_SPIM0, pin_disp_miso, pin_disp_sck, pin_disp_mosi);
#define DISP_W 128
#define SCL_OLED 18
#define SDA_OLED 17
#define DISP_CUSTOM_ADDR false
++#elif BOARD_MODEL == BOARD_H_W_PAPER
++ #define DISP_W 250
++ #define DISP_H 122
++ #define DISP_ADDR -1
#else
#define DISP_RST -1
#define DISP_ADDR 0x3C
#define SMALL_FONT &Org_01
-#if BOARD_MODEL == BOARD_TDECK
- Adafruit_ST7789 display = Adafruit_ST7789(DISPLAY_CS, DISPLAY_DC, -1);
- #define SSD1306_WHITE ST77XX_WHITE
- #define SSD1306_BLACK ST77XX_BLACK
-#elif BOARD_MODEL == BOARD_HELTEC_T114
- ST7789Spi display(&SPI1, DISPLAY_RST, DISPLAY_DC, DISPLAY_CS);
- #define SSD1306_WHITE ST77XX_WHITE
- #define SSD1306_BLACK ST77XX_BLACK
-#elif BOARD_MODEL == BOARD_TBEAM_S_V1
- Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1);
- #define SSD1306_WHITE SH110X_WHITE
- #define SSD1306_BLACK SH110X_BLACK
-#elif BOARD_MODEL == BOARD_TECHO
- GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(pin_disp_cs, pin_disp_dc, pin_disp_reset, pin_disp_busy));
+#include "Graphics.h"
+
- #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
++#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL || BOARD_MODEL == BOARD_H_W_PAPER
+ #if DISPLAY == EINK_BW
+ GxEPD2_BW<DISPLAY_MODEL, DISPLAY_MODEL::HEIGHT> display(DISPLAY_MODEL(pin_disp_cs, pin_disp_dc, pin_disp_reset, pin_disp_busy));
- float disp_target_fps = 0.2;
++ float disp_target_fps = 0.5;
+ uint32_t last_epd_refresh = 0;
++ uint32_t last_epd_full_refresh = 0;
+ #define REFRESH_PERIOD 300000 // 5 minutes in ms
+ #elif DISPLAY == EINK_3C
+ GxEPD2_3C<DISPLAY_MODEL, DISPLAY_MODEL::HEIGHT> display(DISPLAY_MODEL(pin_disp_cs, pin_disp_dc, pin_disp_reset, pin_disp_busy));
- float disp_target_fps = 0.05; // refresh usually takes longer on 3C, hence this is 4x the BW refresh period
++ float disp_target_fps = 0.05; // refresh usually takes longer on 3C
uint32_t last_epd_refresh = 0;
- #define REFRESH_PERIOD 300000
+ uint32_t last_epd_full_refresh = 0;
+ #define REFRESH_PERIOD 600000 // 10 minutes in ms
+ #endif
+#elif BOARD_MODEL == BOARD_TECHO
+GxEPD2_BW<DISPLAY_MODEL, DISPLAY_MODEL::HEIGHT> display(DISPLAY_MODEL(pin_disp_cs, pin_disp_dc, pin_disp_reset, pin_disp_busy));
+float disp_target_fps = 0.2;
+uint32_t last_epd_refresh = 0;
++uint32_t last_epd_full_refresh = 0;
+#define REFRESH_PERIOD 300000 // 5 minutes in ms
#else
- Adafruit_SSD1306 display(DISP_W, DISP_H, &Wire, DISP_RST);
+ #if DISPLAY == OLED
+ #if BOARD_MODEL == BOARD_TDECK
+ Adafruit_ST7789 display = Adafruit_ST7789(DISPLAY_CS, DISPLAY_DC, -1);
+ #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+ Adafruit_SH1106G display = Adafruit_SH1106G(DISP_W, DISP_H, &Wire, -1);
++ #elif BOARD_MODEL == BOARD_HELTEC_T114
++ ST7789Spi display(&SPI1, DISPLAY_RST, DISPLAY_DC, DISPLAY_CS);
+ #else
+ Adafruit_SSD1306 display(DISP_W, DISP_H, &Wire, DISP_RST);
+ #endif
+ float disp_target_fps = 7;
+ #define SCREENSAVER_TIME 500 // ms
+ uint32_t last_screensaver = 0;
+ #define SCREENSAVER_INTERVAL 600000 // 10 minutes in ms
+ bool screensaver_enabled = false;
+ #endif
#endif
-float disp_target_fps = 7;
-float epd_update_fps = 0.5;
-
#define DISP_MODE_UNKNOWN 0x00
#define DISP_MODE_LANDSCAPE 0x01
#define DISP_MODE_PORTRAIT 0x02
uint8_t disp_ext_fb = false;
unsigned char fb[512];
uint32_t last_disp_update = 0;
+ uint32_t last_unblank_event = 0;
+ uint32_t display_blanking_timeout = DISPLAY_BLANKING_TIMEOUT;
+ uint8_t display_unblank_intensity = display_intensity;
+ bool display_blanked = false;
bool display_tx = false;
+ bool recondition_display = false;
int disp_update_interval = 1000/disp_target_fps;
-
+ int epd_update_interval = 1000/disp_target_fps;
uint32_t last_page_flip = 0;
+uint32_t last_interface_page_flip = 0;
int page_interval = 4000;
bool device_signatures_ok();
bool device_firmware_ok();
+bool stat_area_initialised = false;
+bool radio_online = false;
+
+#define START_PAGE 0
+const uint8_t pages = 3;
+uint8_t disp_page = START_PAGE;
+uint8_t interface_page = START_PAGE;
+
+uint8_t online_interface_list[INTERFACE_COUNT] = {0};
+
+uint8_t online_interfaces = 0;
+
+#if DISP_H == 64
#define WATERFALL_SIZE 46
- #elif DISP_H == 122
- #define WATERFALL_SIZE 92
-int waterfall[WATERFALL_SIZE];
-int waterfall_head = 0;
+#else
+#define WATERFALL_SIZE int(DISP_H * 0.75) // default to 75% of the display height
+#endif
+
+int waterfall[INTERFACE_COUNT][WATERFALL_SIZE] = {0};
+int waterfall_head[INTERFACE_COUNT] = {0};
int p_ad_x = 0;
int p_ad_y = 0;
}
uint8_t display_contrast = 0x00;
- #if DISPLAY == OLED
#if BOARD_MODEL == BOARD_TBEAM_S_V1
- void set_contrast(Adafruit_SH1106G *display, uint8_t value) {
- }
+ void set_contrast(Adafruit_SH1106G *display, uint8_t value) {
+ }
+ #elif BOARD_MODEL == BOARD_HELTEC_T114
+ void set_contrast(ST7789Spi *display, uint8_t value) { }
+ #elif BOARD_MODEL == BOARD_TECHO
+ void set_contrast(void *display, uint8_t value) {
+ if (value == 0) { analogWrite(pin_backlight, 0); }
+ else { analogWrite(pin_backlight, value); }
+ }
#elif BOARD_MODEL == BOARD_TDECK
- void set_contrast(Adafruit_ST7789 *display, uint8_t value) {
- static uint8_t level = 0;
- static uint8_t steps = 16;
- if (value > 15) value = 15;
- if (value == 0) {
- digitalWrite(DISPLAY_BL_PIN, 0);
- delay(3);
- level = 0;
- return;
- }
- if (level == 0) {
- digitalWrite(DISPLAY_BL_PIN, 1);
- level = steps;
- delayMicroseconds(30);
- }
- int from = steps - level;
- int to = steps - value;
- int num = (steps + to - from) % steps;
- for (int i = 0; i < num; i++) {
- digitalWrite(DISPLAY_BL_PIN, 0);
- digitalWrite(DISPLAY_BL_PIN, 1);
- }
- level = value;
- }
+ void set_contrast(Adafruit_ST7789 *display, uint8_t value) {
+ static uint8_t level = 0;
+ static uint8_t steps = 16;
+ if (value > 15) value = 15;
+ if (value == 0) {
+ digitalWrite(DISPLAY_BL_PIN, 0);
+ delay(3);
+ level = 0;
+ return;
+ }
+ if (level == 0) {
+ digitalWrite(DISPLAY_BL_PIN, 1);
+ level = steps;
+ delayMicroseconds(30);
+ }
+ int from = steps - level;
+ int to = steps - value;
+ int num = (steps + to - from) % steps;
+ for (int i = 0; i < num; i++) {
+ digitalWrite(DISPLAY_BL_PIN, 0);
+ digitalWrite(DISPLAY_BL_PIN, 1);
+ }
+ level = value;
+ }
++#elif BOARD_MODEL == BOARD_OPENCOM_XL || BOARD_MODEL == BOARD_H_W_PAPER
++ // no backlight on these displays
++ void set_contrast (void* display, uint8_t contrast) {};
#else
void set_contrast(Adafruit_SSD1306 *display, uint8_t contrast) {
display->ssd1306_command(SSD1306_SETCONTRAST);
delay(50);
digitalWrite(pin_display_en, HIGH);
Wire.begin(SDA_OLED, SCL_OLED);
- #elif BOARD_MODEL == BOARD_TECHO
- pinMode(pin_disp_en, INPUT_PULLUP);
- digitalWrite(pin_disp_en, HIGH);
-
- display.init(0, true, 10, false, displaySPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
- display.setPartialWindow(0, 0, DISP_W, DISP_H);
-
- // Because refreshing this display can take some time, sometimes serial
- // commands will be missed. Therefore, during periods where the device is
- // waiting for the display to update, it will poll the serial buffer to
- // check for any commands from the host.
- display.epd2.setBusyCallback(busyCallback);
+ #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
+ #if DISPLAY == OLED
+ #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
+ pinMode(pin_disp_en, INPUT_PULLUP);
+ digitalWrite(pin_disp_en, HIGH);
+ display.init(0, true, 10, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
+ display.setPartialWindow(0, 0, DISP_W, DISP_H);
+
- // Because refreshing this display can take some time, sometimes serial
- // commands will be missed. Therefore, during periods where the device is
- // waiting for the display to update, it will poll the serial buffer to
- // check for any commands from the host.
+ display.epd2.setBusyCallback(busyCallback);
++ #elif BOARD_MODEL == BOARD_H_W_PAPER
++ pinMode(pin_disp_en, OUTPUT);
++ digitalWrite(pin_disp_en, LOW);
++ display.init(0, true, 10, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
++ display.setPartialWindow(0, 0, DISP_W, DISP_H);
++
++ display.epd2.setBusyCallback(busyCallback);
+ #elif BOARD_MODEL == BOARD_HELTEC_T114
+ pinMode(PIN_T114_TFT_EN, OUTPUT);
+ digitalWrite(PIN_T114_TFT_EN, LOW);
+ #elif BOARD_MODEL == BOARD_TECHO
+ display.init(0, true, 10, false, displaySPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
+ display.setPartialWindow(0, 0, DISP_W, DISP_H);
+ display.epd2.setBusyCallback(busyCallback);
+ #if HAS_BACKLIGHT
+ pinMode(pin_backlight, OUTPUT);
+ analogWrite(pin_backlight, 0);
#endif
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
Wire.begin(SDA_OLED, SCL_OLED);
uint8_t display_address = DISP_ADDR;
#endif
- #if BOARD_MODEL == BOARD_TECHO
- // Don't check if display is actually connected
+ #if HAS_EEPROM
+ if (EEPROM.read(eeprom_addr(ADDR_CONF_BSET)) == CONF_OK_BYTE) {
+ uint8_t db_timeout = EEPROM.read(eeprom_addr(ADDR_CONF_DBLK));
+ if (db_timeout == 0x00) {
+ display_blanking_enabled = false;
+ } else {
+ display_blanking_enabled = true;
+ display_blanking_timeout = db_timeout*1000;
+ }
+ }
+ #elif MCU_VARIANT == MCU_NRF52
+ if (eeprom_read(eeprom_addr(ADDR_CONF_BSET)) == CONF_OK_BYTE) {
+ uint8_t db_timeout = eeprom_read(eeprom_addr(ADDR_CONF_DBLK));
+ if (db_timeout == 0x00) {
+ display_blanking_enabled = false;
+ } else {
+ display_blanking_enabled = true;
+ display_blanking_timeout = db_timeout*1000;
+ }
+ }
+ #endif
+
- // don't check if display is actually connected
+ #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
if(false) {
#elif BOARD_MODEL == BOARD_TDECK
display.init(240, 320);
#endif
return false;
} else {
- set_contrast(&display, display_contrast);
+ #if DISPLAY == OLED
+ set_contrast(&display, display_contrast);
+ #endif
- #if BOARD_MODEL == BOARD_RNODE_NG_20
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_RNODE_NG_21
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V1_0
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V2_0
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V2_1
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TBEAM
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TBEAM_S_V1
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_HELTEC32_V2
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
- #if DISPLAY == OLED
- #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
- disp_mode = DISP_MODE_PORTRAIT;
- #endif
- #elif BOARD_MODEL == BOARD_TECHO
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_HELTEC32_V3
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TDECK
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #else
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #endif
+ if (display_rotation != 0xFF) {
+ if (display_rotation == 0 || display_rotation == 2) {
+ disp_mode = DISP_MODE_LANDSCAPE;
+ } else {
+ disp_mode = DISP_MODE_PORTRAIT;
+ }
+ display.setRotation(display_rotation);
+ } else {
- #if BOARD_MODEL == BOARD_RNODE_NG_20
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_RNODE_NG_21
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V1_0
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V2_0
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_LORA32_V2_1
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TBEAM
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TBEAM_S_V1
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_HELTEC32_V2
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_HELTEC32_V3
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_HELTEC_T114
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(1);
- #elif BOARD_MODEL == BOARD_RAK4631
- disp_mode = DISP_MODE_LANDSCAPE;
- display.setRotation(0);
- #elif BOARD_MODEL == BOARD_TDECK
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #elif BOARD_MODEL == BOARD_TECHO
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #else
- disp_mode = DISP_MODE_PORTRAIT;
- display.setRotation(3);
- #endif
++ #if BOARD_MODEL == BOARD_RNODE_NG_20
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #elif BOARD_MODEL == BOARD_RNODE_NG_21
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #elif BOARD_MODEL == BOARD_LORA32_V1_0
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #elif BOARD_MODEL == BOARD_LORA32_V2_0
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #elif BOARD_MODEL == BOARD_LORA32_V2_1
++ disp_mode = DISP_MODE_LANDSCAPE;
++ display.setRotation(0);
++ #elif BOARD_MODEL == BOARD_TBEAM
++ disp_mode = DISP_MODE_LANDSCAPE;
++ display.setRotation(0);
++ #elif BOARD_MODEL == BOARD_TBEAM_S_V1
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(1);
++ #elif BOARD_MODEL == BOARD_HELTEC32_V2
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(1);
++ #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
++ #if DISPLAY == OLED
++ #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
++ disp_mode = DISP_MODE_PORTRAIT;
++ #endif
++ #elif BOARD_MODEL == BOARD_TECHO
++ disp_mode = DISP_MODE_LANDSCAPE;
++ display.setRotation(3);
++ #elif BOARD_MODEL == BOARD_HELTEC32_V3
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(1);
++ #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
++ disp_mode = DISP_MODE_LANDSCAPE;
++ display.setRotation(0);
++ #elif BOARD_MODEL == BOARD_TDECK
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #else
++ disp_mode = DISP_MODE_PORTRAIT;
++ display.setRotation(3);
++ #endif
+ }
update_area_positions();
-
- for (int i = 0; i < WATERFALL_SIZE; i++) { waterfall[i] = 0; }
++ for (int i = 0; i < INTERFACE_COUNT; i++) {
++ for (int j = 0; j < WATERFALL_SIZE; j++) { waterfall[i][j] = 0; }
++ }
last_page_flip = millis();
stat_area.cp437(true);
disp_area.cp437(true);
+
+ #if BOARD_MODEL != BOARD_HELTEC_T114
display.cp437(true);
+ #endif
- #if MCU_VARIANT != MCU_NRF52
+ #if HAS_EEPROM
display_intensity = EEPROM.read(eeprom_addr(ADDR_CONF_DINT));
- #else
+ #elif MCU_VARIANT == MCU_NRF52
display_intensity = eeprom_read(eeprom_addr(ADDR_CONF_DINT));
#endif
+ display_unblank_intensity = display_intensity;
+
+ #if BOARD_MODEL == BOARD_TECHO
+ #if HAS_BACKLIGHT
+ if (display_intensity == 0) { analogWrite(pin_backlight, 0); }
+ else { analogWrite(pin_backlight, display_intensity); }
+ #endif
+ #endif
#if BOARD_MODEL == BOARD_TDECK
- display.fillScreen(SSD1306_BLACK);
+ display.fillScreen(DISPLAY_BLACK);
#endif
- fillRect(p_ad_x, p_ad_y, 128, 128, SSD1306_BLACK);
- fillRect(p_as_x, p_as_y, 128, 128, SSD1306_BLACK);
+ #if BOARD_MODEL == BOARD_HELTEC_T114
+ // Enable backlight led (display is always black without this)
++ fillRect(p_ad_x, p_ad_y, 128, 128, DISPLAY_BLACK);
++ fillRect(p_as_x, p_as_y, 128, 128, DISPLAY_BLACK);
+ pinMode(PIN_T114_TFT_BLGT, OUTPUT);
+ digitalWrite(PIN_T114_TFT_BLGT, LOW);
+ #endif
+
return true;
}
#else
#endif
}
- if(colour == SSD1306_WHITE){
+ // Draws a line on the screen
+ void drawLine(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t colour) {
+ #if BOARD_MODEL == BOARD_HELTEC_T114
- } else if(colour == SSD1306_BLACK) {
++ if(colour == DISPLAY_WHITE){
+ display.setColor(WHITE);
- if(colour == SSD1306_WHITE){
++ } else if(colour == DISPLAY_BLACK) {
+ display.setColor(BLACK);
+ }
+ display.drawLine(x, y, width, height);
+ #else
+ display.drawLine(x, y, width, height, colour);
+ #endif
+ }
+
+ // Draws a filled rectangle on the screen
+ void fillRect(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t colour) {
+ #if BOARD_MODEL == BOARD_HELTEC_T114
- } else if(colour == SSD1306_BLACK) {
++ if(colour == DISPLAY_WHITE){
+ display.setColor(WHITE);
- #if DISPLAY_SCALE == 1
++ } else if(colour == DISPLAY_BLACK) {
+ display.setColor(BLACK);
+ }
+ display.fillRect(x, y, width, height);
+ #else
+ display.fillRect(x, y, width, height, colour);
+ #endif
+ }
+
+ // Draws a bitmap to the display and auto scales it based on the boards configured DISPLAY_SCALE
+ void drawBitmap(int16_t startX, int16_t startY, const uint8_t* bitmap, int16_t bitmapWidth, int16_t bitmapHeight, uint16_t foregroundColour, uint16_t backgroundColour) {
- fillRect(startX + col * DISPLAY_SCALE, startY + row * DISPLAY_SCALE, DISPLAY_SCALE, DISPLAY_SCALE, foregroundColour);
++ #if !DISPLAY_SCALE_OVERRIDE
+ display.drawBitmap(startX, startY, bitmap, bitmapWidth, bitmapHeight, foregroundColour, backgroundColour);
+ #else
+ for(int16_t row = 0; row < bitmapHeight; row++){
+ for(int16_t col = 0; col < bitmapWidth; col++){
+
+ // determine index and bitmask
+ int16_t index = row * ((bitmapWidth + 7) / 8) + (col / 8);
+ uint8_t bitmask = 1 << (7 - (col % 8));
+
+ // check if the current pixel is set in the bitmap
+ if(bitmap[index] & bitmask){
+ // draw a scaled rectangle for the foreground pixel
- fillRect(startX + col * DISPLAY_SCALE, startY + row * DISPLAY_SCALE, DISPLAY_SCALE, DISPLAY_SCALE, backgroundColour);
++ fillRect(ceil(startX + col * DISPLAY_SCALE), ceil(startY + row * DISPLAY_SCALE), ceil(DISPLAY_SCALE), ceil(DISPLAY_SCALE), foregroundColour);
+ } else {
+ // draw a scaled rectangle for the background pixel
++ fillRect(ceil(startX + col * DISPLAY_SCALE), ceil(startY + row * DISPLAY_SCALE), ceil(DISPLAY_SCALE), ceil(DISPLAY_SCALE), backgroundColour);
+ }
+
+ }
+ }
+ #endif
+ }
+
void draw_cable_icon(int px, int py) {
if (cable_state == CABLE_STATE_DISCONNECTED) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_cable+0*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_cable+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_cable+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_cable+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
} else if (cable_state == CABLE_STATE_CONNECTED) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_cable+1*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_cable+1*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_cable+1*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_cable+1*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
}
}
void draw_bt_icon(int px, int py) {
if (bt_state == BT_STATE_OFF) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_bt+0*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
} else if (bt_state == BT_STATE_ON) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_bt+1*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_bt+1*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_bt+1*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_bt+1*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
} else if (bt_state == BT_STATE_PAIRING) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_bt+2*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_bt+2*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_bt+2*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_bt+2*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
} else if (bt_state == BT_STATE_CONNECTED) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_bt+3*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_bt+3*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_bt+3*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_bt+3*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_bt+0*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(px, py, bm_bt+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
}
}
-void draw_lora_icon(int px, int py) {
+void draw_lora_icon(RadioInterface* radio, int px, int py) {
+ // todo: make display show other interfaces
if (radio_online) {
- #if DISP_H == 122
- if (online_interface_list[interface_page] != radio->getIndex()) {
- stat_area.drawBitmap(px - 2, py - 2, bm_dot_sqr, 34, 36, DISPLAY_WHITE, DISPLAY_BLACK);
-
- // redraw stat area on next refresh
- stat_area_initialised = false;
- }
- if (radio->getRadioOnline()) {
- stat_area.drawBitmap(px, py, bm_rf+1*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- } else {
- stat_area.drawBitmap(px, py, bm_rf+0*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- }
- #else
- stat_area.drawBitmap(px, py, bm_rf+1*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
- } else {
- stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
- }
+ if (online_interface_list[interface_page] != radio->getIndex()) {
+ stat_area.drawBitmap(px - 1, py - 1, bm_dot_sqr, 18, 19, DISPLAY_WHITE, DISPLAY_BLACK);
+
+ // redraw stat area on next refresh
+ stat_area_initialised = false;
+ }
+ if (radio->getRadioOnline()) {
+ stat_area.drawBitmap(px, py, bm_rf+1*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
+ } else {
+ stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
+ }
- #endif
+ } else {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_rf+0*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
+ stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
+ }
}
void draw_mw_icon(int px, int py) {
- if (mw_radio_online) {
- stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+ if (INTERFACE_COUNT >= 2) {
+ if (interface_obj[1]->getRadioOnline()) {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_rf+3*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
++ stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
+ } else {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_rf+2*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
++ stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
+ }
} else {
- #if DISP_H == 122
- stat_area.drawBitmap(px, py, bm_rf+2*128, 30, 32, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+ stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
}
}
}
if (battery_indeterminate && battery_state == BATTERY_STATE_CHARGING && !disable_charge_status) {
- #if DISP_H == 122
- stat_area.fillRect(px-2, py-2, 24, 9, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-5, bm_plug, 34, 13, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, SSD1306_WHITE, SSD1306_BLACK);
++ //#if DISP_H == 122
++ // stat_area.fillRect(px-2, py-2, 24, 9, DISPLAY_BLACK);
++ // stat_area.drawBitmap(px-2, py-5, bm_plug, 34, 13, DISPLAY_WHITE, DISPLAY_BLACK);
++ //#else
++ // stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
++ // stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
++ //#endif
++ stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
++ stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
if (battery_state == BATTERY_STATE_CHARGED) {
- #if DISP_H == 122
- stat_area.fillRect(px-2, py-2, 24, 9, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-5, bm_plug, 34, 13, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
++ stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- stat_area.fillRect(px, py, 20, 5, DISPLAY_BLACK);
- stat_area.fillRect(px-2, py-4, 34, 19, DISPLAY_BLACK);
- stat_area.drawRect(px-2, py-2, 23, 9, DISPLAY_WHITE);
- stat_area.drawLine(px+21, py, px+21, py+5, DISPLAY_WHITE);
- if (battery_value > 0) stat_area.drawLine(px, py, px, py+4, DISPLAY_WHITE);
- if (battery_value >= 10) stat_area.drawLine(px+1*2, py, px+1*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 20) stat_area.drawLine(px+2*2, py, px+2*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 30) stat_area.drawLine(px+3*2, py, px+3*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 40) stat_area.drawLine(px+4*2, py, px+4*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 50) stat_area.drawLine(px+5*2, py, px+5*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 60) stat_area.drawLine(px+6*2, py, px+6*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 70) stat_area.drawLine(px+7*2, py, px+7*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 80) stat_area.drawLine(px+8*2, py, px+8*2, py+4, DISPLAY_WHITE);
- if (battery_value >= 90) stat_area.drawLine(px+9*2, py, px+9*2, py+4, DISPLAY_WHITE);
- #else
- // stat_area.fillRect(px, py, 14, 3, SSD1306_BLACK);
- stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK);
- stat_area.drawRect(px-2, py-2, 17, 7, SSD1306_WHITE);
- stat_area.drawLine(px+15, py, px+15, py+3, SSD1306_WHITE);
- if (battery_value > 7) stat_area.drawLine(px, py, px, py+2, SSD1306_WHITE);
- if (battery_value > 20) stat_area.drawLine(px+1*2, py, px+1*2, py+2, SSD1306_WHITE);
- if (battery_value > 33) stat_area.drawLine(px+2*2, py, px+2*2, py+2, SSD1306_WHITE);
- if (battery_value > 46) stat_area.drawLine(px+3*2, py, px+3*2, py+2, SSD1306_WHITE);
- if (battery_value > 59) stat_area.drawLine(px+4*2, py, px+4*2, py+2, SSD1306_WHITE);
- if (battery_value > 72) stat_area.drawLine(px+5*2, py, px+5*2, py+2, SSD1306_WHITE);
- if (battery_value > 85) stat_area.drawLine(px+6*2, py, px+6*2, py+2, SSD1306_WHITE);
+ stat_area.fillRect(px, py, 14, 3, DISPLAY_BLACK);
+ stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
+ stat_area.drawRect(px-2, py-2, 17, 7, DISPLAY_WHITE);
+ stat_area.drawLine(px+15, py, px+15, py+3, DISPLAY_WHITE);
+ if (battery_value > 7) stat_area.drawLine(px, py, px, py+2, DISPLAY_WHITE);
+ if (battery_value > 20) stat_area.drawLine(px+1*2, py, px+1*2, py+2, DISPLAY_WHITE);
+ if (battery_value > 33) stat_area.drawLine(px+2*2, py, px+2*2, py+2, DISPLAY_WHITE);
+ if (battery_value > 46) stat_area.drawLine(px+3*2, py, px+3*2, py+2, DISPLAY_WHITE);
+ if (battery_value > 59) stat_area.drawLine(px+4*2, py, px+4*2, py+2, DISPLAY_WHITE);
+ if (battery_value > 72) stat_area.drawLine(px+5*2, py, px+5*2, py+2, DISPLAY_WHITE);
+ if (battery_value > 85) stat_area.drawLine(px+6*2, py, px+6*2, py+2, DISPLAY_WHITE);
- #endif
}
}
} else {
- #if DISP_H == 122
- stat_area.fillRect(px-2, py-2, 24, 9, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-5, bm_plug, 34, 13, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, SSD1306_WHITE, SSD1306_BLACK);
+ stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
+ stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
}
}
} else {
- #if DISP_H == 122
- stat_area.fillRect(px-2, py-2, 24, 9, DISPLAY_BLACK);
- stat_area.drawBitmap(px-2, py-5, bm_plug, 34, 13, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK);
- stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, SSD1306_WHITE, SSD1306_BLACK);
+ stat_area.fillRect(px-2, py-2, 18, 7, DISPLAY_BLACK);
+ stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
}
}
if (quality > 100.0) quality = 100.0;
if (quality < 0.0) quality = 0.0;
- #if DISP_H == 122
- stat_area.fillRect(px, py, 26, 14, DISPLAY_BLACK);
- if (quality > 0) {
- stat_area.drawLine(px+0*4, py+14, px+0*4, py+6, DISPLAY_WHITE);
- stat_area.drawLine(px+0*4+1, py+14, px+0*4+1, py+6, DISPLAY_WHITE);
- }
- if (quality > 15) {
- stat_area.drawLine(px+1*4, py+14, px+1*4, py+5, DISPLAY_WHITE);
- stat_area.drawLine(px+1*4+1, py+14, px+1*4+1, py+5, DISPLAY_WHITE);
- }
- if (quality > 30) {
- stat_area.drawLine(px+2*4, py+14, px+2*4, py+4, DISPLAY_WHITE);
- stat_area.drawLine(px+2*4+1, py+14, px+2*4+1, py+4, DISPLAY_WHITE);
- }
- if (quality > 45) {
- stat_area.drawLine(px+3*4, py+14, px+3*4, py+3, DISPLAY_WHITE);
- stat_area.drawLine(px+3*4+1, py+14, px+3*4+1, py+3, DISPLAY_WHITE);
- }
- if (quality > 60) {
- stat_area.drawLine(px+4*4, py+14, px+4*4, py+2, DISPLAY_WHITE);
- stat_area.drawLine(px+4*4+1, py+14, px+4*4+1, py+2, DISPLAY_WHITE);
- }
- if (quality > 75) {
- stat_area.drawLine(px+5*4, py+14, px+5*4, py+1, DISPLAY_WHITE);
- stat_area.drawLine(px+5*4+1, py+14, px+5*4+1, py+1, DISPLAY_WHITE);
- }
- if (quality > 90) {
- stat_area.drawLine(px+6*4, py+14, px+6*4, py+0, DISPLAY_WHITE);
- stat_area.drawLine(px+6*4+1, py+14, px+6*4+1, py+0, DISPLAY_WHITE);
- }
- #else
- stat_area.fillRect(px, py, 13, 7, DISPLAY_BLACK);
++//#if DISP_H == 122
++// stat_area.fillRect(px, py, 26, 14, DISPLAY_BLACK);
++// if (quality > 0) {
++// stat_area.drawLine(px+0*4, py+14, px+0*4, py+6, DISPLAY_WHITE);
++// stat_area.drawLine(px+0*4+1, py+14, px+0*4+1, py+6, DISPLAY_WHITE);
++// }
++// if (quality > 15) {
++// stat_area.drawLine(px+1*4, py+14, px+1*4, py+5, DISPLAY_WHITE);
++// stat_area.drawLine(px+1*4+1, py+14, px+1*4+1, py+5, DISPLAY_WHITE);
++// }
++// if (quality > 30) {
++// stat_area.drawLine(px+2*4, py+14, px+2*4, py+4, DISPLAY_WHITE);
++// stat_area.drawLine(px+2*4+1, py+14, px+2*4+1, py+4, DISPLAY_WHITE);
++// }
++// if (quality > 45) {
++// stat_area.drawLine(px+3*4, py+14, px+3*4, py+3, DISPLAY_WHITE);
++// stat_area.drawLine(px+3*4+1, py+14, px+3*4+1, py+3, DISPLAY_WHITE);
++// }
++// if (quality > 60) {
++// stat_area.drawLine(px+4*4, py+14, px+4*4, py+2, DISPLAY_WHITE);
++// stat_area.drawLine(px+4*4+1, py+14, px+4*4+1, py+2, DISPLAY_WHITE);
++// }
++// if (quality > 75) {
++// stat_area.drawLine(px+5*4, py+14, px+5*4, py+1, DISPLAY_WHITE);
++// stat_area.drawLine(px+5*4+1, py+14, px+5*4+1, py+1, DISPLAY_WHITE);
++// }
++// if (quality > 90) {
++// stat_area.drawLine(px+6*4, py+14, px+6*4, py+0, DISPLAY_WHITE);
++// stat_area.drawLine(px+6*4+1, py+14, px+6*4+1, py+0, DISPLAY_WHITE);
++// }
++//#else
++// stat_area.fillRect(px, py, 13, 7, DISPLAY_BLACK);
++// if (quality > 0) stat_area.drawLine(px+0*2, py+7, px+0*2, py+6, DISPLAY_WHITE);
++// if (quality > 15) stat_area.drawLine(px+1*2, py+7, px+1*2, py+5, DISPLAY_WHITE);
++// if (quality > 30) stat_area.drawLine(px+2*2, py+7, px+2*2, py+4, DISPLAY_WHITE);
++// if (quality > 45) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, DISPLAY_WHITE);
++// if (quality > 60) stat_area.drawLine(px+4*2, py+7, px+4*2, py+2, DISPLAY_WHITE);
++// if (quality > 75) stat_area.drawLine(px+5*2, py+7, px+5*2, py+1, DISPLAY_WHITE);
++// if (quality > 90) stat_area.drawLine(px+6*2, py+7, px+6*2, py+0, DISPLAY_WHITE);
++//#endif
+ // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
- if (quality > 0) stat_area.drawLine(px+0*2, py+7, px+0*2, py+6, SSD1306_WHITE);
- if (quality > 15) stat_area.drawLine(px+1*2, py+7, px+1*2, py+5, SSD1306_WHITE);
- if (quality > 30) stat_area.drawLine(px+2*2, py+7, px+2*2, py+4, SSD1306_WHITE);
- if (quality > 45) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE);
- if (quality > 60) stat_area.drawLine(px+4*2, py+7, px+4*2, py+2, SSD1306_WHITE);
- if (quality > 75) stat_area.drawLine(px+5*2, py+7, px+5*2, py+1, SSD1306_WHITE);
- if (quality > 90) stat_area.drawLine(px+6*2, py+7, px+6*2, py+0, SSD1306_WHITE);
- }
++ // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
+ if (quality > 0) stat_area.drawLine(px+0*2, py+7, px+0*2, py+6, DISPLAY_WHITE);
+ if (quality > 15) stat_area.drawLine(px+1*2, py+7, px+1*2, py+5, DISPLAY_WHITE);
+ if (quality > 30) stat_area.drawLine(px+2*2, py+7, px+2*2, py+4, DISPLAY_WHITE);
+ if (quality > 45) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, DISPLAY_WHITE);
+ if (quality > 60) stat_area.drawLine(px+4*2, py+7, px+4*2, py+2, DISPLAY_WHITE);
+ if (quality > 75) stat_area.drawLine(px+5*2, py+7, px+5*2, py+1, DISPLAY_WHITE);
+ if (quality > 90) stat_area.drawLine(px+6*2, py+7, px+6*2, py+0, DISPLAY_WHITE);
- #endif
- // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
++ //}
}
- #define S_RSSI_MIN -135.0
- #define S_RSSI_MAX -75.0
+ #if MODE == SX1280
+ #define S_RSSI_MIN -105.0
+ #define S_RSSI_MAX -65.0
+ #else
+ #define S_RSSI_MIN -135.0
+ #define S_RSSI_MAX -75.0
+ #endif
#define S_RSSI_SPAN (S_RSSI_MAX-S_RSSI_MIN)
void draw_signal_bars(int px, int py) {
- stat_area.fillRect(px, py, 13, 7, SSD1306_BLACK);
++ stat_area.fillRect(px, py, 13, 7, DISPLAY_BLACK);
+
+ if (radio_online) {
int rssi_val = last_rssi;
if (rssi_val < S_RSSI_MIN) rssi_val = S_RSSI_MIN;
if (rssi_val > S_RSSI_MAX) rssi_val = S_RSSI_MAX;
if (signal > 100.0) signal = 100.0;
if (signal < 0.0) signal = 0.0;
- #if DISP_H == 122
- stat_area.fillRect(px, py, 26, 14, DISPLAY_BLACK);
- if (signal > 85) {
- stat_area.drawLine(px+0*4, py+14, px+0*4, py+0, DISPLAY_WHITE);
- stat_area.drawLine(px+0*4+1, py+14, px+0*4+1, py+0, DISPLAY_WHITE);
- }
- if (signal > 72) {
- stat_area.drawLine(px+1*4, py+14, px+1*4, py+1, DISPLAY_WHITE);
- stat_area.drawLine(px+1*4+1, py+14, px+1*4+1, py+1, DISPLAY_WHITE);
- }
- if (signal > 59) {
- stat_area.drawLine(px+2*4, py+14, px+2*4, py+2, DISPLAY_WHITE);
- stat_area.drawLine(px+2*4+1, py+14, px+2*4+1, py+2, DISPLAY_WHITE);
- }
- if (signal > 46) {
- stat_area.drawLine(px+3*4, py+14, px+3*4, py+3, DISPLAY_WHITE);
- stat_area.drawLine(px+3*4+1, py+14, px+3*4+1, py+3, DISPLAY_WHITE);
- }
- if (signal > 33) {
- stat_area.drawLine(px+4*4, py+14, px+4*4, py+4, DISPLAY_WHITE);
- stat_area.drawLine(px+4*4+1, py+14, px+4*4+1, py+4, DISPLAY_WHITE);
- }
- if (signal > 20) {
- stat_area.drawLine(px+5*4, py+14, px+5*4, py+5, DISPLAY_WHITE);
- stat_area.drawLine(px+5*4+1, py+14, px+5*4+1, py+5, DISPLAY_WHITE);
- }
- if (signal > 7) {
- stat_area.drawLine(px+6*4, py+14, px+6*4, py+6, DISPLAY_WHITE);
- stat_area.drawLine(px+6*4+1, py+14, px+6*4+1, py+6, DISPLAY_WHITE);
- }
- #else
- stat_area.fillRect(px, py, 13, 7, DISPLAY_BLACK);
++//#if DISP_H == 122
++// stat_area.fillRect(px, py, 26, 14, DISPLAY_BLACK);
++// if (signal > 85) {
++// stat_area.drawLine(px+0*4, py+14, px+0*4, py+0, DISPLAY_WHITE);
++// stat_area.drawLine(px+0*4+1, py+14, px+0*4+1, py+0, DISPLAY_WHITE);
++// }
++// if (signal > 72) {
++// stat_area.drawLine(px+1*4, py+14, px+1*4, py+1, DISPLAY_WHITE);
++// stat_area.drawLine(px+1*4+1, py+14, px+1*4+1, py+1, DISPLAY_WHITE);
++// }
++// if (signal > 59) {
++// stat_area.drawLine(px+2*4, py+14, px+2*4, py+2, DISPLAY_WHITE);
++// stat_area.drawLine(px+2*4+1, py+14, px+2*4+1, py+2, DISPLAY_WHITE);
++// }
++// if (signal > 46) {
++// stat_area.drawLine(px+3*4, py+14, px+3*4, py+3, DISPLAY_WHITE);
++// stat_area.drawLine(px+3*4+1, py+14, px+3*4+1, py+3, DISPLAY_WHITE);
++// }
++// if (signal > 33) {
++// stat_area.drawLine(px+4*4, py+14, px+4*4, py+4, DISPLAY_WHITE);
++// stat_area.drawLine(px+4*4+1, py+14, px+4*4+1, py+4, DISPLAY_WHITE);
++// }
++// if (signal > 20) {
++// stat_area.drawLine(px+5*4, py+14, px+5*4, py+5, DISPLAY_WHITE);
++// stat_area.drawLine(px+5*4+1, py+14, px+5*4+1, py+5, DISPLAY_WHITE);
++// }
++// if (signal > 7) {
++// stat_area.drawLine(px+6*4, py+14, px+6*4, py+6, DISPLAY_WHITE);
++// stat_area.drawLine(px+6*4+1, py+14, px+6*4+1, py+6, DISPLAY_WHITE);
++// }
++//#else
++// stat_area.fillRect(px, py, 13, 7, DISPLAY_BLACK);
++// if (signal > 85) stat_area.drawLine(px+0*2, py+7, px+0*2, py+0, DISPLAY_WHITE);
++// if (signal > 72) stat_area.drawLine(px+1*2, py+7, px+1*2, py+1, DISPLAY_WHITE);
++// if (signal > 59) stat_area.drawLine(px+2*2, py+7, px+2*2, py+2, DISPLAY_WHITE);
++// if (signal > 46) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, DISPLAY_WHITE);
++// if (signal > 33) stat_area.drawLine(px+4*2, py+7, px+4*2, py+4, DISPLAY_WHITE);
++// if (signal > 20) stat_area.drawLine(px+5*2, py+7, px+5*2, py+5, DISPLAY_WHITE);
++// if (signal > 7) stat_area.drawLine(px+6*2, py+7, px+6*2, py+6, DISPLAY_WHITE);
++//#endif
+ // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
- if (signal > 85) stat_area.drawLine(px+0*2, py+7, px+0*2, py+0, SSD1306_WHITE);
- if (signal > 72) stat_area.drawLine(px+1*2, py+7, px+1*2, py+1, SSD1306_WHITE);
- if (signal > 59) stat_area.drawLine(px+2*2, py+7, px+2*2, py+2, SSD1306_WHITE);
- if (signal > 46) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE);
- if (signal > 33) stat_area.drawLine(px+4*2, py+7, px+4*2, py+4, SSD1306_WHITE);
- if (signal > 20) stat_area.drawLine(px+5*2, py+7, px+5*2, py+5, SSD1306_WHITE);
- if (signal > 7) stat_area.drawLine(px+6*2, py+7, px+6*2, py+6, SSD1306_WHITE);
++
++#define WF_TX_SIZE 5
++#define WF_TX_WIDTH 5
++ // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
+ if (signal > 85) stat_area.drawLine(px+0*2, py+7, px+0*2, py+0, DISPLAY_WHITE);
+ if (signal > 72) stat_area.drawLine(px+1*2, py+7, px+1*2, py+1, DISPLAY_WHITE);
+ if (signal > 59) stat_area.drawLine(px+2*2, py+7, px+2*2, py+2, DISPLAY_WHITE);
+ if (signal > 46) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, DISPLAY_WHITE);
+ if (signal > 33) stat_area.drawLine(px+4*2, py+7, px+4*2, py+4, DISPLAY_WHITE);
+ if (signal > 20) stat_area.drawLine(px+5*2, py+7, px+5*2, py+5, DISPLAY_WHITE);
+ if (signal > 7) stat_area.drawLine(px+6*2, py+7, px+6*2, py+6, DISPLAY_WHITE);
- #endif
- // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality);
+ }
}
- #define WF_TX_SIZE 5
- #define WF_TX_WIDTH 5
-#if MODEM == SX1280
- #define WF_TX_SIZE 5
-#else
++//#if MODEM == SX1280
++// #define WF_TX_SIZE 5
++//#else
+ #define WF_TX_SIZE 5
-#endif
++//#endif
#define WF_RSSI_MAX -60
#define WF_RSSI_MIN -135
-#define WF_RSSI_SPAN (WF_RSSI_MAX-WF_RSSI_MIN)
-#define WF_PIXEL_WIDTH 10
+#define WF_RSSI_SPAN (WF_RSSI_MAX - WF_RSSI_MIN)
- #if DISP_H == 122
- #define WF_PIXEL_WIDTH 22
- #elif disp_mode == DISP_MODE_LANDSCAPE
++#if disp_mode == DISP_MODE_LANDSCAPE
+#define WF_PIXEL_WIDTH (DISP_H / WF_RSSI_SPAN)
+#else
+#define WF_PIXEL_WIDTH (DISP_W / WF_RSSI_SPAN)
+#endif
void draw_waterfall(int px, int py) {
- int rssi_val = current_rssi;
+ int rssi_val = interface_obj[interface_page]->currentRssi();
if (rssi_val < WF_RSSI_MIN) rssi_val = WF_RSSI_MIN;
if (rssi_val > WF_RSSI_MAX) rssi_val = WF_RSSI_MAX;
int rssi_normalised = ((rssi_val - WF_RSSI_MIN)*(1.0/WF_RSSI_SPAN))*WF_PIXEL_WIDTH;
}
}
-bool stat_area_intialised = false;
void draw_stat_area() {
if (device_init_done) {
- if (!stat_area_intialised) {
- stat_area.drawBitmap(0, 0, bm_frame, 64, 64, SSD1306_WHITE, SSD1306_BLACK);
- stat_area_intialised = true;
+ if (!stat_area_initialised) {
- #if DISP_H == 122
- stat_area.drawBitmap(0, 0, bm_frame, stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
- #else
+ stat_area.drawBitmap(0, 0, bm_frame, 64, 64, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
+ stat_area_initialised = true;
}
- #if DISP_H == 122
- draw_cable_icon(6, 18);
- draw_bt_icon(6, 60);
- draw_lora_icon(interface_obj[0], 86, 18);
-
- // todo, expand support to show more than two interfaces on screen
- if (INTERFACE_COUNT > 1) {
- draw_lora_icon(interface_obj[1], 86, 60);
- }
- draw_battery_bars(8, 113);
- #else
+ if (millis()-last_interface_page_flip >= page_interval) {
+ int online_interfaces_check = 0;
+
+ // todo, is there a more efficient way of doing this?
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ if (interface_obj[i]->getRadioOnline()) {
+ online_interfaces_check++;
+ }
+ }
+
+ if (online_interfaces != online_interfaces_check) {
+ online_interfaces = online_interfaces_check;
+ }
+
+ // cap at two for now, as only two boxes to symbolise interfaces
+ // available on display
+ if (online_interfaces > 2) {
+ online_interfaces = 2;
+ }
+
+ uint8_t index = 0;
+
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ if (interface_obj[i]->getRadioOnline()) {
+ online_interface_list[index] = i;
+ index++;
+ }
+ }
+
+ if (online_interfaces > 0) {
+ interface_page = (++interface_page%online_interfaces);
+ }
+ last_interface_page_flip = millis();
+ }
+
++ //#if DISP_H == 122
++ //draw_cable_icon(6, 18);
++ //draw_bt_icon(6, 60);
++ //draw_lora_icon(interface_obj[0], 86, 18);
++
++ //// todo, expand support to show more than two interfaces on screen
++ //if (INTERFACE_COUNT > 1) {
++ // draw_lora_icon(interface_obj[1], 86, 60);
++ //}
++ //draw_battery_bars(8, 113);
++ //#else
draw_cable_icon(3, 8);
draw_bt_icon(3, 30);
- draw_lora_icon(45, 8);
- draw_mw_icon(45, 30);
+ draw_lora_icon(interface_obj[0], 45, 8);
+
+ // todo, expand support to show more than two interfaces on screen
+ if (INTERFACE_COUNT > 1) {
+ draw_lora_icon(interface_obj[1], 45, 30);
+ }
draw_battery_bars(4, 58);
- #endif
+ radio_online = false;
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ if (interface_obj[i]->getRadioOnline()) {
+ radio_online = true;
+ break;
+ }
+ }
++ //#if DISP_H == 122
++ //draw_quality_bars(53, 109);
++ //draw_signal_bars(83, 109);
++ //draw_waterfall(50, 8);
++ //#else
+ draw_quality_bars(28, 56);
+ draw_signal_bars(44, 56);
if (radio_online) {
- #if DISP_H == 122
- draw_quality_bars(53, 109);
- draw_signal_bars(83, 109);
- draw_waterfall(50, 8);
- #else
- draw_quality_bars(28, 56);
- draw_signal_bars(44, 56);
draw_waterfall(27, 4);
+ #endif
}
}
}
void update_stat_area() {
if (eeprom_ok && !firmware_update_mode && !console_active) {
-
draw_stat_area();
if (disp_mode == DISP_MODE_PORTRAIT) {
- display.drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
- drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
} else if (disp_mode == DISP_MODE_LANDSCAPE) {
- display.drawBitmap(p_as_x+2, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
- drawBitmap(p_as_x+2, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK);
- if (device_init_done && !disp_ext_fb) drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE);
++ drawBitmap(p_as_x+2, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
+ if (device_init_done && !disp_ext_fb) display.drawLine(p_as_x, 0, p_as_x, DISP_W/2, DISPLAY_WHITE);
}
} else {
if (firmware_update_mode) {
- display.drawBitmap(p_as_x, p_as_y, bm_updating, stat_area.width(), stat_area.height(), DISPLAY_BLACK, DISPLAY_WHITE);
- drawBitmap(p_as_x, p_as_y, bm_updating, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE);
++ drawBitmap(p_as_x, p_as_y, bm_updating, stat_area.width(), stat_area.height(), DISPLAY_BLACK, DISPLAY_WHITE);
} else if (console_active && device_init_done) {
- display.drawBitmap(p_as_x, p_as_y, bm_console, stat_area.width(), stat_area.height(), DISPLAY_BLACK, DISPLAY_WHITE);
- drawBitmap(p_as_x, p_as_y, bm_console, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE);
++ drawBitmap(p_as_x, p_as_y, bm_console, stat_area.width(), stat_area.height(), DISPLAY_BLACK, DISPLAY_WHITE);
if (disp_mode == DISP_MODE_LANDSCAPE) {
- display.drawLine(p_as_x, 0, p_as_x, DISP_W/2, DISPLAY_WHITE);
- drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE);
++ drawLine(p_as_x, 0, p_as_x, DISP_W/2, DISPLAY_WHITE);
}
}
}
}
-#define START_PAGE 0
-const uint8_t pages = 3;
-uint8_t disp_page = START_PAGE;
void draw_disp_area() {
++ //if (!device_init_done || firmware_update_mode) {
++ // uint8_t p_by = 37;
++ // if (disp_mode == DISP_MODE_LANDSCAPE || firmware_update_mode) {
++ // p_by = 18;
++ // disp_area.fillRect(0, 0, disp_area.width(), disp_area.height(), DISPLAY_BLACK);
++ // }
++ // #if DISP_H == 122
++ // if (!device_init_done) disp_area.drawBitmap(0, p_by, bm_boot, disp_area.width(), 54, DISPLAY_WHITE);
++ // if (firmware_update_mode) disp_area.drawBitmap(0, p_by, bm_fw_update, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // if (!device_init_done) disp_area.drawBitmap(0, p_by, bm_boot, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // if (firmware_update_mode) disp_area.drawBitmap(0, p_by, bm_fw_update, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ //} else {
++ // if (!disp_ext_fb or bt_ssp_pin != 0) {
++ // if (radio_online && display_diagnostics) {
++ // #if DISP_H == 122
++ // selected_radio = interface_obj[online_interface_list[interface_page]];
++ // disp_area.fillRect(0,12,disp_area.width(),57, DISPLAY_BLACK); disp_area.fillRect(0,69,disp_area.width(),56, DISPLAY_WHITE);
++ // disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(DISPLAY_WHITE);
++ // disp_area.setTextSize(2); // scale text 2x
++
++ // disp_area.setCursor(2, 22);
++ // disp_area.print("On");
++ // disp_area.setCursor(14*2, 22);
++ // disp_area.print("@");
++ // disp_area.setCursor(21*2, 22);
++ // disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
++
++ // disp_area.setCursor(2, 36);
++ // disp_area.print("Airtime:");
++
++ // disp_area.setCursor(7+12, 53);
++ // if (selected_radio->getTotalChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
++ // }
++
++ // disp_area.drawBitmap(2, 41, bm_hg_low, 10, 18, DISPLAY_WHITE, DISPLAY_BLACK);
++
++ // disp_area.setCursor(64+17, 53);
++ // if (selected_radio->getLongtermChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
++ // }
++ // disp_area.drawBitmap(64, 41, bm_hg_high, 10, 18, DISPLAY_WHITE, DISPLAY_BLACK);
++
++ // disp_area.setTextColor(DISPLAY_BLACK);
++
++ // disp_area.setCursor(2, 88);
++ // disp_area.print("Channel");
++ // disp_area.setCursor(38*2, 88);
++ // disp_area.print("Load:");
++ //
++ // disp_area.setCursor(7+12, 110);
++ // if (selected_radio->getTotalChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
++ // }
++ // disp_area.drawBitmap(2, 98, bm_hg_low, 10, 18, DISPLAY_BLACK, DISPLAY_WHITE);
++
++ // disp_area.setCursor(64+17, 110);
++ // if (selected_radio->getLongtermChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
++ // }
++ // disp_area.drawBitmap(64, 98, bm_hg_high, 10, 18, DISPLAY_BLACK, DISPLAY_WHITE);
++ // #else
++ // selected_radio = interface_obj[online_interface_list[interface_page]];
++ // disp_area.fillRect(0,8,disp_area.width(),37, DISPLAY_BLACK); disp_area.fillRect(0,37,disp_area.width(),27, DISPLAY_WHITE);
++ // disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(DISPLAY_WHITE);
++
++ // disp_area.setCursor(2, 13);
++ // disp_area.print("On");
++ // disp_area.setCursor(14, 13);
++ // disp_area.print("@");
++ // disp_area.setCursor(21, 13);
++ // disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
++
++ // disp_area.setCursor(2, 23-1);
++ // disp_area.print("Airtime:");
++
++ // disp_area.setCursor(11, 33-1);
++ // if (selected_radio->getTotalChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
++ // }
++
++ // disp_area.drawBitmap(2, 26-1, bm_hg_low, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
++
++ // disp_area.setCursor(32+11, 33-1);
++
++ // if (selected_radio->getLongtermChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
++ // }
++ // disp_area.drawBitmap(32+2, 26-1, bm_hg_high, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
++
++ // disp_area.setTextColor(DISPLAY_BLACK);
++
++ // disp_area.setCursor(2, 46);
++ // disp_area.print("Channel");
++ // disp_area.setCursor(38, 46);
++ // disp_area.print("Load:");
++
++ // disp_area.setCursor(11, 57);
++ // if (selected_radio->getTotalChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
++ // }
++ // disp_area.drawBitmap(2, 50, bm_hg_low, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
++ //
++ // disp_area.setCursor(32+11, 57);
++ // if (selected_radio->getLongtermChannelUtil() < 0.099) {
++ // disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
++ // } else {
++ // disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
++ // }
++ // disp_area.drawBitmap(32+2, 50, bm_hg_high, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
++ // #endif
++
++ // } else {
++ // if (device_signatures_ok()) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 71, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // } else {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 71, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // }
++
++ // if (!hw_ready || !device_firmware_ok()) {
++ // if (!device_firmware_ok()) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_fw_corrupt, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // } else {
++ // if (!modems_installed) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_no_radio, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // } else {
++ // //disp_area.drawBitmap(0, 71, bm_hwfail, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // disp_area.drawBitmap(0, 37, bm_conf_missing, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // }
++ // }
++ // } else if (bt_state == BT_STATE_PAIRING and bt_ssp_pin != 0) {
++ // char *pin_str = (char*)malloc(DISP_PIN_SIZE+1);
++ // sprintf(pin_str, "%06d", bt_ssp_pin);
++
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_pairing, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_pairing, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // for (int i = 0; i < DISP_PIN_SIZE; i++) {
++ // uint8_t numeric = pin_str[i]-48;
++ // #if DISP_H == 122
++ // uint8_t offset = numeric*20;
++ // disp_area.drawBitmap(14+17*i, 71+32, bm_n_uh+offset, 10, 10, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // uint8_t offset = numeric*5;
++ // disp_area.drawBitmap(7+9*i, 37+16, bm_n_uh+offset, 8, 5, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // free(pin_str);
++ // } else {
++ // if (millis()-last_page_flip >= page_interval) {
++ // disp_page = (++disp_page%pages);
++ // last_page_flip = millis();
++ // if (not community_fw and disp_page == 0) disp_page = 1;
++ // }
++
++ // if (radio_online) {
++ // if (!display_diagnostics) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 37, bm_online, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 71, bm_online, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // } else {
++ // if (disp_page == 0) {
++ // if (true || device_signatures_ok()) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_checks, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_checks, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // } else {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_nfr, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_nfr, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // } else if (disp_page == 1) {
++ // if (!console_active) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_hwok, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_hwok, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // } else {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_console_active, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_console_active, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // } else if (disp_page == 2) {
++ // #if DISP_H == 122
++ // disp_area.drawBitmap(0, 71, bm_version, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(0, 37, bm_version, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // char *v_str = (char*)malloc(3+1);
++ // sprintf(v_str, "%01d%02d", MAJ_VERS, MIN_VERS);
++ // for (int i = 0; i < 3; i++) {
++ // #if DISP_H == 122
++ // uint8_t dxp = 43;
++ // uint8_t numeric = v_str[i]-48; uint8_t bm_offset = numeric*20;
++ // if (i == 2) dxp += 9*2+6;
++ // #else
++ // uint8_t dxp = DISP_H * 0.32;
++ // uint8_t numeric = v_str[i]-48; uint8_t bm_offset = numeric*5;
++ // if (i == 2) dxp += 9*2+6;
++ // #endif
++ // if (i == 1) dxp += 9*1+4;
++ // #if DISP_H == 122
++ // // add gap manually rather than oversizing bitmap, as the gfx lib fills in the extra space with black
++ // disp_area.drawBitmap(dxp, 71+32, bm_n_uh+bm_offset, 10, 10, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawBitmap(dxp, 37+16, bm_n_uh+bm_offset, 8, 5, DISPLAY_WHITE, DISPLAY_BLACK);
++ // #endif
++ // }
++ // free(v_str);
++ // #if DISP_H == 122
++ // disp_area.drawLine(27, 37+20, 28, 37+20, DISPLAY_BLACK);
++ // disp_area.drawLine(27, 37+20, 28, 37+20, DISPLAY_BLACK);
++ // #else
++ // disp_area.drawLine(27, 37+19, 28, 37+19, DISPLAY_BLACK);
++ // disp_area.drawLine(27, 37+19, 28, 37+19, DISPLAY_BLACK);
++ // #endif
++ // }
++ // }
++ // }
++ // } else {
++ // disp_area.drawBitmap(0, 0, fb, disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
++ // }
++ //}
if (!device_init_done || firmware_update_mode) {
uint8_t p_by = 37;
if (disp_mode == DISP_MODE_LANDSCAPE || firmware_update_mode) {
p_by = 18;
- disp_area.fillRect(0, 0, disp_area.width(), disp_area.height(), SSD1306_BLACK);
+ disp_area.fillRect(0, 0, disp_area.width(), disp_area.height(), DISPLAY_BLACK);
}
- #if DISP_H == 122
- if (!device_init_done) disp_area.drawBitmap(0, p_by, bm_boot, disp_area.width(), 54, DISPLAY_WHITE);
- if (firmware_update_mode) disp_area.drawBitmap(0, p_by, bm_fw_update, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- if (!device_init_done) disp_area.drawBitmap(0, p_by, bm_boot, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
- if (firmware_update_mode) disp_area.drawBitmap(0, p_by, bm_fw_update, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
+ if (!device_init_done) disp_area.drawBitmap(0, p_by, bm_boot, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
+ if (firmware_update_mode) disp_area.drawBitmap(0, p_by, bm_fw_update, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
} else {
if (!disp_ext_fb or bt_ssp_pin != 0) {
if (radio_online && display_diagnostics) {
- #if DISP_H == 122
- selected_radio = interface_obj[online_interface_list[interface_page]];
- disp_area.fillRect(0,12,disp_area.width(),57, DISPLAY_BLACK); disp_area.fillRect(0,69,disp_area.width(),56, DISPLAY_WHITE);
- disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(DISPLAY_WHITE);
- disp_area.setTextSize(2); // scale text 2x
-
- disp_area.setCursor(2, 22);
- disp_area.print("On");
- disp_area.setCursor(14*2, 22);
- disp_area.print("@");
- disp_area.setCursor(21*2, 22);
- disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
-
- disp_area.setCursor(2, 36);
- disp_area.print("Airtime:");
-
- disp_area.setCursor(7+12, 53);
- if (selected_radio->getTotalChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
- }
- disp_area.fillRect(0,8,disp_area.width(),37, SSD1306_BLACK); disp_area.fillRect(0,37,disp_area.width(),27, SSD1306_WHITE);
- disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(SSD1306_WHITE);
++ disp_area.fillRect(0,8,disp_area.width(),37, DISPLAY_BLACK); disp_area.fillRect(0,37,disp_area.width(),27, DISPLAY_WHITE);
++ disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(DISPLAY_WHITE);
+
- disp_area.drawBitmap(2, 41, bm_hg_low, 10, 18, DISPLAY_WHITE, DISPLAY_BLACK);
++ selected_radio = interface_obj[online_interface_list[interface_page]];
- disp_area.setCursor(64+17, 53);
- if (selected_radio->getLongtermChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
- }
- disp_area.drawBitmap(64, 41, bm_hg_high, 10, 18, DISPLAY_WHITE, DISPLAY_BLACK);
-
- disp_area.setTextColor(DISPLAY_BLACK);
+ disp_area.setCursor(2, 13);
+ disp_area.print("On");
+ disp_area.setCursor(14, 13);
+ disp_area.print("@");
+ disp_area.setCursor(21, 13);
- disp_area.printf("%.1fKbps", (float)lora_bitrate/1000.0);
++ disp_area.printf("%.1fKbps", (float)selected_radio->getBitrate()/1000.0);
- disp_area.setCursor(2, 88);
- disp_area.print("Channel");
- disp_area.setCursor(38*2, 88);
- disp_area.print("Load:");
+ //disp_area.setCursor(31, 23-1);
+ disp_area.setCursor(2, 23-1);
+ disp_area.print("Airtime:");
- disp_area.setCursor(7+12, 110);
- if (selected_radio->getTotalChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
- }
- disp_area.drawBitmap(2, 98, bm_hg_low, 10, 18, DISPLAY_BLACK, DISPLAY_WHITE);
-
- disp_area.setCursor(64+17, 110);
- if (selected_radio->getLongtermChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
- }
- disp_area.drawBitmap(64, 98, bm_hg_high, 10, 18, DISPLAY_BLACK, DISPLAY_WHITE);
- #else
- selected_radio = interface_obj[online_interface_list[interface_page]];
- disp_area.fillRect(0,8,disp_area.width(),37, DISPLAY_BLACK); disp_area.fillRect(0,37,disp_area.width(),27, DISPLAY_WHITE);
- disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(DISPLAY_WHITE);
-
- disp_area.setCursor(2, 13);
- disp_area.print("On");
- disp_area.setCursor(14, 13);
- disp_area.print("@");
- disp_area.setCursor(21, 13);
- disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
-
- disp_area.setCursor(2, 23-1);
- disp_area.print("Airtime:");
-
- disp_area.setCursor(11, 33-1);
- if (selected_radio->getTotalChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
- }
-
- disp_area.drawBitmap(2, 26-1, bm_hg_low, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
-
- disp_area.setCursor(32+11, 33-1);
-
- if (selected_radio->getLongtermChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
- }
- disp_area.drawBitmap(32+2, 26-1, bm_hg_high, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
+ disp_area.setCursor(11, 33-1);
- if (total_channel_util < 0.099) {
++ if (selected_radio->getAirtime() < 0.099) {
+ //disp_area.printf("%.1f%%", total_channel_util*100.0);
- disp_area.printf("%.1f%%", airtime*100.0);
++ disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
+ } else {
+ //disp_area.printf("%.0f%%", total_channel_util*100.0);
- disp_area.printf("%.0f%%", airtime*100.0);
++ disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
+ }
- disp_area.drawBitmap(2, 26-1, bm_hg_low, 5, 9, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(2, 26-1, bm_hg_low, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
- disp_area.setTextColor(DISPLAY_BLACK);
+ disp_area.setCursor(32+11, 33-1);
- if (longterm_channel_util < 0.099) {
++ if (selected_radio->getLongtermAirtime() < 0.099) {
+ //disp_area.printf("%.1f%%", longterm_channel_util*100.0);
- disp_area.printf("%.1f%%", longterm_airtime*100.0);
++ disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
+ } else {
+ //disp_area.printf("%.0f%%", longterm_channel_util*100.0);
- disp_area.printf("%.0f%%", longterm_airtime*100.0);
++ disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
+ }
- disp_area.drawBitmap(32+2, 26-1, bm_hg_high, 5, 9, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(32+2, 26-1, bm_hg_high, 5, 9, DISPLAY_WHITE, DISPLAY_BLACK);
- disp_area.setCursor(2, 46);
- disp_area.print("Channel");
- disp_area.setCursor(38, 46);
- disp_area.print("Load:");
- disp_area.setCursor(11, 57);
- if (selected_radio->getTotalChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
- }
- disp_area.drawBitmap(2, 50, bm_hg_low, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
- disp_area.setTextColor(SSD1306_BLACK);
++ disp_area.setTextColor(DISPLAY_BLACK);
+ disp_area.setCursor(2, 46);
+ disp_area.print("Channel");
+ disp_area.setCursor(38, 46);
+ disp_area.print("Load:");
- disp_area.setCursor(32+11, 57);
- if (selected_radio->getLongtermChannelUtil() < 0.099) {
- disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
- } else {
- disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
- }
- disp_area.drawBitmap(32+2, 50, bm_hg_high, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
- #endif
+ disp_area.setCursor(11, 57);
- if (total_channel_util < 0.099) {
++ if (selected_radio->getTotalChannelUtil() < 0.099) {
+ //disp_area.printf("%.1f%%", airtime*100.0);
- disp_area.printf("%.1f%%", total_channel_util*100.0);
++ disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
+ } else {
+ //disp_area.printf("%.0f%%", airtime*100.0);
- disp_area.printf("%.0f%%", total_channel_util*100.0);
++ disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
+ }
- disp_area.drawBitmap(2, 50, bm_hg_low, 5, 9, SSD1306_BLACK, SSD1306_WHITE);
++ disp_area.drawBitmap(2, 50, bm_hg_low, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
+
+ disp_area.setCursor(32+11, 57);
- if (longterm_channel_util < 0.099) {
++ if (selected_radio->getLongtermChannelUtil() < 0.099) {
+ //disp_area.printf("%.1f%%", longterm_airtime*100.0);
- disp_area.printf("%.1f%%", longterm_channel_util*100.0);
++ disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
+ } else {
+ //disp_area.printf("%.0f%%", longterm_airtime*100.0);
- disp_area.printf("%.0f%%", longterm_channel_util*100.0);
++ disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
+ }
- disp_area.drawBitmap(32+2, 50, bm_hg_high, 5, 9, SSD1306_BLACK, SSD1306_WHITE);
++ disp_area.drawBitmap(32+2, 50, bm_hg_high, 5, 9, DISPLAY_BLACK, DISPLAY_WHITE);
} else {
if (device_signatures_ok()) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 71, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 37, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 0, bm_def_lc, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 71, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 37, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 0, bm_def, disp_area.width(), 37, DISPLAY_WHITE, DISPLAY_BLACK);
}
}
- if (!hw_ready || radio_error || !device_firmware_ok()) {
+ if (!hw_ready || !device_firmware_ok()) {
if (!device_firmware_ok()) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_fw_corrupt, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- if (!modem_installed) {
- disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
+ if (!modems_installed) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_no_radio, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
++ disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_hwfail, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_hwfail, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_conf_missing, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_conf_missing, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
}
}
} else if (bt_state == BT_STATE_PAIRING and bt_ssp_pin != 0) {
char *pin_str = (char*)malloc(DISP_PIN_SIZE+1);
sprintf(pin_str, "%06d", bt_ssp_pin);
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_pairing, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_pairing, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_pairing, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_pairing, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
for (int i = 0; i < DISP_PIN_SIZE; i++) {
uint8_t numeric = pin_str[i]-48;
- #if DISP_H == 122
- uint8_t offset = numeric*20;
- disp_area.drawBitmap(14+17*i, 71+32, bm_n_uh+offset, 10, 10, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- uint8_t offset = numeric*5;
- disp_area.drawBitmap(7+9*i, 37+16, bm_n_uh+offset, 8, 5, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
+ uint8_t offset = numeric*5;
- disp_area.drawBitmap(7+9*i, 37+16, bm_n_uh+offset, 8, 5, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(7+9*i, 37+16, bm_n_uh+offset, 8, 5, DISPLAY_WHITE, DISPLAY_BLACK);
}
free(pin_str);
} else {
if (radio_online) {
if (!display_diagnostics) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 37, bm_online, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 71, bm_online, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_online, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_online, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
}
} else {
if (disp_page == 0) {
if (true || device_signatures_ok()) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_checks, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_checks, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_checks, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_checks, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_nfr, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_nfr, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_nfr, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_nfr, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
}
} else if (disp_page == 1) {
if (!console_active) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_hwok, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_hwok, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_hwok, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_hwok, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
} else {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_console_active, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_console_active, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_console_active, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_console_active, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
}
} else if (disp_page == 2) {
- #if DISP_H == 122
- disp_area.drawBitmap(0, 71, bm_version, disp_area.width(), 54, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
- disp_area.drawBitmap(0, 37, bm_version, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
- disp_area.drawBitmap(0, 37, bm_version, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 37, bm_version, disp_area.width(), 27, DISPLAY_WHITE, DISPLAY_BLACK);
char *v_str = (char*)malloc(3+1);
sprintf(v_str, "%01d%02d", MAJ_VERS, MIN_VERS);
for (int i = 0; i < 3; i++) {
- #if DISP_H == 122
- uint8_t dxp = 43;
- uint8_t numeric = v_str[i]-48; uint8_t bm_offset = numeric*20;
- if (i == 2) dxp += 9*2+6;
- #else
- uint8_t dxp = DISP_H * 0.32;
uint8_t numeric = v_str[i]-48; uint8_t bm_offset = numeric*5;
- if (i == 2) dxp += 9*2+6;
- #endif
+ uint8_t dxp = 20;
if (i == 1) dxp += 9*1+4;
- #if DISP_H == 122
- // add gap manually rather than oversizing bitmap, as the gfx lib fills in the extra space with black
- disp_area.drawBitmap(dxp, 71+32, bm_n_uh+bm_offset, 10, 10, DISPLAY_WHITE, DISPLAY_BLACK);
- #else
+ if (i == 2) dxp += 9*2+4;
- disp_area.drawBitmap(dxp, 37+16, bm_n_uh+bm_offset, 8, 5, SSD1306_WHITE, SSD1306_BLACK);
+ disp_area.drawBitmap(dxp, 37+16, bm_n_uh+bm_offset, 8, 5, DISPLAY_WHITE, DISPLAY_BLACK);
- #endif
}
free(v_str);
- #if DISP_H == 122
- disp_area.drawLine(27, 37+20, 28, 37+20, DISPLAY_BLACK);
- disp_area.drawLine(27, 37+20, 28, 37+20, DISPLAY_BLACK);
- #else
- disp_area.drawLine(27, 37+19, 28, 37+19, DISPLAY_BLACK);
- disp_area.drawLine(27, 37+19, 28, 37+19, SSD1306_BLACK);
- disp_area.drawLine(27, 37+20, 28, 37+20, SSD1306_BLACK);
+ disp_area.drawLine(27, 37+19, 28, 37+19, DISPLAY_BLACK);
- #endif
++ disp_area.drawLine(27, 37+20, 28, 37+20, DISPLAY_BLACK);
}
}
}
} else {
- disp_area.drawBitmap(0, 0, fb, disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
- disp_area.drawBitmap(0, 0, fb, disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ disp_area.drawBitmap(0, 0, fb, disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
}
}
}
void update_disp_area() {
draw_disp_area();
- display.drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
+
- drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
if (disp_mode == DISP_MODE_LANDSCAPE) {
if (device_init_done && !firmware_update_mode && !disp_ext_fb) {
- #if DISP_H == 122
- display.drawLine(0, 0, 0, 63, DISPLAY_WHITE);
- #else
- display.drawLine(0, 0, 0, DISP_H / 2, DISPLAY_WHITE);
- #endif
- drawLine(0, 0, 0, 63, SSD1306_WHITE);
++ //display.drawLine(0, 0, 0, 63, DISPLAY_WHITE);
++ drawLine(0, 0, 0, 63, DISPLAY_WHITE);
}
}
}
- void do_screensaver(uint32_t current){
+ void display_recondition() {
- #if PLATFORM == PLATFORM_ESP32
+ #if DISPLAY == OLED
- // Invert display to protect against OLED screen burn in
- //TODO: Make this a option configurable through rnodeconf
- //TODO: Implement other ways to do the screensaver, such as scrolling the screen.
- if (screensaver_enabled) {
- if (current-last_screensaver >= SCREENSAVER_INTERVAL+SCREENSAVER_TIME) {
- display.invertDisplay(0);
- last_screensaver = current;
- screensaver_enabled = false;
- }
+ for (uint8_t iy = 0; iy < disp_area.height(); iy++) {
+ unsigned char rand_seg [] = {random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF)};
- stat_area.drawBitmap(0, iy, rand_seg, 64, 1, SSD1306_WHITE, SSD1306_BLACK);
- disp_area.drawBitmap(0, iy, rand_seg, 64, 1, SSD1306_WHITE, SSD1306_BLACK);
++ stat_area.drawBitmap(0, iy, rand_seg, 64, 1, DISPLAY_WHITE, DISPLAY_BLACK);
++ disp_area.drawBitmap(0, iy, rand_seg, 64, 1, DISPLAY_WHITE, DISPLAY_BLACK);
}
- else if (current-last_screensaver >= SCREENSAVER_INTERVAL) {
- display.invertDisplay(1);
- screensaver_enabled = true;
+
- drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
+ if (disp_mode == DISP_MODE_PORTRAIT) {
- drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
+ } else if (disp_mode == DISP_MODE_LANDSCAPE) {
- drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK);
++ drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), DISPLAY_WHITE, DISPLAY_BLACK);
}
#endif
}
-#if BOARD_MODEL == BOARD_TECHO
+ bool epd_blanked = false;
- display.fillScreen(SSD1306_WHITE);
++#if DISPLAY == EINK_3C || DISPLAY == EINK_BW
+ void epd_blank(bool full_update = true) {
+ display.setFullWindow();
- display.fillScreen(SSD1306_BLACK);
++ display.fillScreen(DISPLAY_WHITE);
+ display.display(full_update);
+ }
+
+ void epd_black(bool full_update = true) {
+ display.setFullWindow();
++ display.fillScreen(DISPLAY_BLACK);
+ display.display(full_update);
+ }
+ #endif
+
void update_display(bool blank = false) {
- #if DISPLAY == OLED
- if (display_contrast != display_intensity) {
- display_contrast = display_intensity;
- set_contrast(&display, display_contrast);
+ display_updating = true;
+ if (blank == true) {
+ last_disp_update = millis()-disp_update_interval-1;
+ } else {
+ if (display_blanking_enabled && millis()-last_unblank_event >= display_blanking_timeout) {
+ blank = true;
+ display_blanked = true;
+ if (display_intensity != 0) {
+ display_unblank_intensity = display_intensity;
+ }
+ display_intensity = 0;
+ } else {
+ display_blanked = false;
+ if (display_unblank_intensity != 0x00) {
+ display_intensity = display_unblank_intensity;
+ display_unblank_intensity = 0x00;
+ }
}
- display.clearDisplay();
- #endif
+ }
+
if (blank) {
- display.display();
+ if (millis()-last_disp_update >= disp_update_interval) {
+ if (display_contrast != display_intensity) {
+ display_contrast = display_intensity;
+ set_contrast(&display, display_contrast);
+ }
+
- #if BOARD_MODEL == BOARD_TECHO
++ #if DISPLAY == EINK_3C || DISPLAY == EINK_BW
+ if (!epd_blanked) {
+ epd_blank();
+ epd_blanked = true;
+ }
+ #endif
+
+ #if BOARD_MODEL == BOARD_HELTEC_T114
+ display.clear();
+ display.display();
- #elif BOARD_MODEL != BOARD_TDECK && BOARD_MODEL != BOARD_TECHO
++ #elif BOARD_MODEL != BOARD_TDECK && DISPLAY != EINK_3C && DISPLAY != EINK_BW
+ display.clearDisplay();
+ display.display();
+ #else
+ // TODO: Clear screen
+ #endif
+
+ last_disp_update = millis();
+ }
+
} else {
if (millis()-last_disp_update >= disp_update_interval) {
uint32_t current = millis();
- do_screensaver(current);
- #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
- display.setFullWindow();
- display.fillScreen(DISPLAY_WHITE);
+ if (display_contrast != display_intensity) {
+ display_contrast = display_intensity;
+ set_contrast(&display, display_contrast);
+ }
+
+ #if BOARD_MODEL == BOARD_HELTEC_T114
+ display.clear();
- #elif BOARD_MODEL != BOARD_TDECK && BOARD_MODEL != BOARD_TECHO
++ #elif BOARD_MODEL != BOARD_TDECK && DISPLAY != EINK_3C && DISPLAY != EINK_BW
+ display.clearDisplay();
#endif
- update_stat_area();
- update_disp_area();
- #if DISPLAY == OLED
- display.display();
- #else
- if (current-last_epd_refresh >= REFRESH_PERIOD) {
- // Perform a full refresh after the correct time has elapsed
- display.display(false);
- last_epd_refresh = current;
+
+ if (recondition_display) {
+ disp_target_fps = 30;
+ disp_update_interval = 1000/disp_target_fps;
+ display_recondition();
} else {
- // Only perform a partial refresh
- display.display(true);
- #if BOARD_MODEL == BOARD_TECHO
++ #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
+ display.setFullWindow();
- display.fillScreen(SSD1306_WHITE);
++ display.fillScreen(DISPLAY_WHITE);
+ #endif
+
+ update_stat_area();
+ update_disp_area();
}
- #if BOARD_MODEL == BOARD_TECHO
+
++ #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
+ if (current-last_epd_refresh >= epd_update_interval) {
+ if (current-last_epd_full_refresh >= REFRESH_PERIOD) { display.display(false); last_epd_full_refresh = millis(); }
+ else { display.display(true); }
+ last_epd_refresh = millis();
+ epd_blanked = false;
+ }
+ #elif BOARD_MODEL != BOARD_TDECK
+ display.display();
#endif
- last_disp_update = current;
+
+ last_disp_update = millis();
}
}
+ display_updating = false;
+ }
+
+ void display_unblank() {
+ last_unblank_event = millis();
}
void ext_fb_enable() {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
-};
+
+ const unsigned char bm_online [] PROGMEM = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x1c, 0x66, 0x61, 0x8c, 0x24, 0x90, 0x87,
+ 0xe6, 0x49, 0x22, 0x4f, 0x24, 0xe4, 0x93, 0x93, 0xe6, 0x18, 0x20, 0x63, 0x3c, 0x26, 0x30, 0x87,
+ 0xe6, 0x19, 0x24, 0x79, 0x24, 0xe6, 0x33, 0x87, 0xe6, 0x49, 0x26, 0x43, 0x8c, 0x27, 0x70, 0x93,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xe6, 0x73, 0xe7, 0x33, 0x87, 0xff,
+ 0xff, 0xe4, 0xe2, 0x73, 0xe7, 0x13, 0x9f, 0xff, 0xff, 0xe4, 0xe0, 0x73, 0xe7, 0x03, 0x87, 0xff,
+ 0xff, 0xe4, 0xe4, 0x73, 0xe7, 0x23, 0x9f, 0xff, 0xff, 0xf1, 0xe6, 0x70, 0xe7, 0x33, 0x87, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ const unsigned char bm_pairing [] PROGMEM = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf0, 0xe3, 0x98, 0x73, 0x33, 0x87, 0xff, 0xff, 0xf2, 0xc9, 0x99, 0x33, 0x13, 0x3f, 0xff,
+ 0xff, 0xf0, 0xc1, 0x98, 0x73, 0x03, 0x27, 0xff, 0xff, 0xf3, 0xc9, 0x98, 0x73, 0x23, 0x27, 0xff,
+ 0xff, 0xf3, 0xc9, 0x99, 0x33, 0x33, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ const unsigned char bm_n_uh [] PROGMEM = {
+ 0x07, 0x27, 0x27, 0x27, 0x07, 0x8f, 0x8f, 0xcf, 0xcf, 0xcf, 0x07, 0xe7, 0x07, 0x3f, 0x07, 0x07,
+ 0xe7, 0xc7, 0xe7, 0x07, 0x27, 0x27, 0x07, 0xe7, 0xe7, 0x07, 0x3f, 0x07, 0xe7, 0x07, 0x07, 0x3f,
+ 0x07, 0x27, 0x07, 0x07, 0xc7, 0xcf, 0x9f, 0x1f, 0x07, 0x27, 0x07, 0x27, 0x07, 0x07, 0x27, 0x07,
+ 0xe7, 0xe7
+ };
+
+ const unsigned char bm_plug [] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x7f, 0x80, 0x55, 0xfc, 0x00, 0xaa, 0xfc, 0x00, 0x00,
+ 0x7f, 0x80, 0x00, 0x1c, 0x00
+ };
+
+ const unsigned char bm_hg_low [] PROGMEM = {
+ 0xf8, 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0xf8, 0xf8
+ };
+
+ const unsigned char bm_hg_high [] PROGMEM = {
+ 0xf8, 0x88, 0xf8, 0x70, 0x20, 0x70, 0xf8, 0xf8, 0xf8
++};
++
++const unsigned char bm_dot_sqr [] PROGMEM = {
++ 0xaa, 0xd5, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00,
++ 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00,
++ 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00,
++ 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0xaa, 0xd5, 0x40
++};
--- /dev/null
- #define SX127X 0x00
- #define SX1276 0x01
- #define SX1278 0x02
- #define SX126X 0x10
- #define SX1262 0x11
- #define SX128X 0x20
- #define SX1280 0x21
++#define SX1276 0x00
++#define SX1278 0x01
++#define SX1262 0x10
++#define SX1280 0x20
# Version 2.0.17 of the Arduino ESP core is based on ESP-IDF v4.4.7
ARDUINO_ESP_CORE_VER = 2.0.17
- COMMON_ESP_UPLOAD_FlAGS = $(VFLAG) --chip esp32 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000
+V ?= 0
+VFLAG =
+ifeq "$(V)" "1"
+VFLAG =-v
+endif
+
+COMMON_BUILD_FLAGS = $(VFLAG) -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152"
++COMMON_ESP_UPLOAD_FLAGS = $(VFLAG) --chip esp32 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000
+
all: release
clean:
arduino-cli lib install "Adafruit NeoPixel"
arduino-cli lib install "XPowersLib"
arduino-cli lib install "Crypto"
-
-prep-samd:
- arduino-cli core update-index --config-file arduino-cli.yaml
- arduino-cli core install adafruit:samd --config-file arduino-cli.yaml
+ arduino-cli lib install "Adafruit NeoPixel"
+ pip install pyserial rns --upgrade --user --break-system-packages # This looks scary, but it's actually just telling pip to install packages as a user instead of trying to install them systemwide, which bypasses the "externally managed environment" error.
prep-nrf:
- arduino-cli core update-index --config-file arduino-cli.yaml
+ arduino-cli core install adafruit:nrf52 --config-file arduino-cli.yaml
arduino-cli core install rakwireless:nrf52 --config-file arduino-cli.yaml
- arduino-cli core install adafruit:nrf52 --config-file arduino-cli.yaml
+ arduino-cli core install Heltec_nRF52:Heltec_nRF52 --config-file arduino-cli.yaml
+ arduino-cli lib install "Crypto"
+ arduino-cli lib install "Adafruit GFX Library"
arduino-cli lib install "GxEPD2"
- pip install adafruit-nrfutil --upgrade
+ arduino-cli config set library.enable_unsafe_install true
+ arduino-cli lib install --git-url https://github.com/liamcottle/esp8266-oled-ssd1306#e16cee124fe26490cb14880c679321ad8ac89c95
+ pip install pyserial rns --upgrade --user --break-system-packages # This looks scary, but it's actually just telling pip to install packages as a user instead of trying to install them systemwide, which bypasses the "externally managed environment" error.
+ pip install adafruit-nrfutil --upgrade --user --break-system-packages # This looks scary, but it's actually just telling pip to install packages as a user instead of trying to install them systemwide, which bypasses the "externally managed environment" error.
console-site:
make -C Console clean site
check_bt_buffers:
@./esp32_btbufs.py ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/libraries/BluetoothSerial/src/BluetoothSerial.cpp
-firmware:
- arduino-cli compile --log --fqbn unsignedio:avr:rnode
-
-firmware-mega2560:
- arduino-cli compile --log --fqbn arduino:avr:mega
-
firmware-tbeam: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\""
+ arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DBOARD_VARIANT=0xE4\""
+
+firmware-tbeam_sx1262: check_bt_buffers
+ arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DBOARD_VARIANT=0xE8\""
- firmware-techo: firmware-techo4 firmware-techo9
-
- firmware-techo4:
- arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\" \"-DBOARD_VARIANT=0x16\""
-
- firmware-techo9:
- arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\" \"-DBOARD_VARIANT=0x17\""
-firmware-tbeam_sx126x: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\""
++firmware-techo:
++ arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
firmware-t3s3:
- arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x03\""
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x43\" \"-DBOARD_VARIANT=0xAB\""
-firmware-t3s3_sx127x:
- arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x01\""
+firmware-t3s3_sx126x:
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x43\" \"-DBOARD_VARIANT=0xA1\""
firmware-t3s3_sx1280_pa:
- arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x04\""
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x43\" \"-DBOARD_VARIANT=0xAC\""
-firmware-tdeck:
- arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3B\""
-
-firmware-tbeam_supreme:
- arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x3D"
+firmware-e22_esp32:
+ arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x45\" \"-DEXTERNAL_LEDS=true\""
firmware-lora32_v10: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
firmware-lora32_v10_extled: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\""
firmware-lora32_v20: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\""
firmware-lora32_v21: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\""
firmware-lora32_v21_extled: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\""
firmware-lora32_v21_tcxo: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\""
firmware-heltec32_v2: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\""
firmware-heltec32_v2_extled: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\""
firmware-heltec32_v3:
- arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V3 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\""
+
++firmware-heltec_w_paper:
++ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3E\""
+
firmware-rnode_ng_20: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
firmware-rnode_ng_21: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\""
firmware-featheresp32: check_bt_buffers
- arduino-cli compile --log --fqbn esp32:esp32:featheresp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
+ arduino-cli compile --fqbn esp32:esp32:featheresp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
firmware-genericesp32: check_bt_buffers
- arduino-cli compile --log --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\""
+ arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\""
firmware-rak4631:
- arduino-cli compile --log --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\""
-
-firmware-heltec_t114:
- arduino-cli compile --log --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\""
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x12\""
-firmware-techo:
- arduino-cli compile --log --fqbn adafruit:nrf52:pca10056 -e --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
+firmware-rak4631_sx1280:
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x14\""
-upload:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn unsignedio:avr:rnode
+firmware-opencom-xl:
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x52\" \"-DBOARD_VARIANT=0x21\""
-upload-mega2560:
- arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:mega
++firmware-heltec_t114:
++ arduino-cli compile --log --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\""
upload-tbeam:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:t-beam
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:t-beam
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bin)
@sleep 3
- python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --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
+ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
- upload-techo:
- arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn adafruit:nrf52:pca10056
- unzip -o build/adafruit.nrf52.pca10056/RNode_Firmware_CE.ino.zip -d build/adafruit.nrf52.pca10056
- rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(sha256sum ./build/adafruit.nrf52.pca10056/RNode_Firmware_CE.ino.bin | grep -o '^\S*')
+ upload-tbeam_sx1262:
+ arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:t-beam
+ @sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware.ino.bin)
++ rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bin)
+ #@sleep 3
+ #python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --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-lora32_v10:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:ttgo-lora32
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:ttgo-lora32
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- 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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
upload-lora32_v20:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:ttgo-lora32
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:ttgo-lora32
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- 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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
upload-lora32_v21:
- arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:ttgo-lora32
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:ttgo-lora32
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
upload-heltec32_v2:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:heltec_wifi_lora_32_V2
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:heltec_wifi_lora_32_V2
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyUSB1 --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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
--upload-heltec32_v3:
- arduino-cli upload -p /dev/ttyUSB1 --fqbn esp32:esp32:heltec_wifi_lora_32_V3
++upload-heltec_w_paper upload-heltec32_v3:
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:heltec_wifi_lora_32_V3
@sleep 1
- rnodeconf /dev/ttyUSB1 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.bin)
@sleep 3
- python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyUSB1 --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
+ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) --chip esp32-s3 --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-tdeck:
- arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin)
@sleep 3
- python ./Release/esptool/esptool.py --chip esp32-s3 --port $(or $(port), /dev/ttyACM0) --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
- python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyACM0 --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
++ python ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) --chip esp32-s3 --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-tbeam_supreme:
- arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin)
@sleep 3
- python ./Release/esptool/esptool.py --chip esp32-s3 --port $(or $(port), /dev/ttyACM0) --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
- python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyACM0 --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
++ python ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) --chip esp32-s3 --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-rnode_ng_20:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:ttgo-lora32
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:ttgo-lora32
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- 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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
upload-rnode_ng_21:
- arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:ttgo-lora32
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:ttgo-lora32
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin)
@sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
- python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --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
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
upload-t3s3:
- @echo
- @echo Put board into flashing mode by holding BOOT button while momentarily pressing the RESET button. Hit enter when done.
- @read
- arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
- @sleep 2
- python ./Release/esptool/esptool.py --chip esp32s3 --port $(or $(port), /dev/ttyACM0) --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
- @echo
- @echo Press the RESET button on the board now, and hit enter
- @read
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
++ python ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) --chip esp32-s3 --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
+ @sleep 3
- python ./Release/esptool/esptool.py --chip esp32s3 --port /dev/ttyACM0 --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
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin)
upload-featheresp32:
- arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:featheresp32
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:featheresp32
@sleep 1
- rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.featheresp32/RNode_Firmware.ino.bin)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.featheresp32/RNode_Firmware_CE.ino.bin)
@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
+ python ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_UPLOAD_FLAGS) ./Release/console_image.bin
-upload-rak4631:
- arduino-cli upload -p /dev/ttyACM0 --fqbn rakwireless:nrf52:WisCoreRAK4631Board
+upload-opencom-xl upload-rak4631:
+ arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn rakwireless:nrf52:WisCoreRAK4631Board
+ unzip -o build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.ino.zip -d build/rakwireless.nrf52.WisCoreRAK4631Board
+ rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(sha256sum ./build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.ino.bin | grep -o '^\S*')
+
+upload-e22_esp32:
+ arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:esp32
@sleep 1
- rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0)
+ rnodeconf $(or $(port), /dev/ttyUSB0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32/RNode_Firmware_CE.ino.bin)
+ @sleep 3
- python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS) ./Release/console_image.bin
++ python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FLAGS) ./Release/console_image.bin
+
+ upload-heltec_t114:
+ arduino-cli upload -p /dev/ttyACM0 --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262
+ @sleep 1
+ rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0)
+
+ upload-techo:
+ arduino-cli upload -p /dev/ttyACM0 --fqbn adafruit:nrf52:pca10056
+ @sleep 6
+ rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0)
-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_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-hashes
+release: console-site spiffs-image $(shell grep ^release- Makefile | cut -d: -f1)
release-hashes:
- python ./release_hashes.py > ./Release/release.json
-
-release-rnode:
- arduino-cli compile --fqbn unsignedio:avr:rnode -e
- cp build/unsignedio.avr.rnode/RNode_Firmware.ino.hex Release/rnode_firmware.hex
- rm -r build
+ python3 ./release_hashes.py > ./Release/release.json
release-tbeam: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\""
+ arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DBOARD_VARIANT=0xE4\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam.boot_app0
- cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bin build/rnode_firmware_tbeam.bin
- cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tbeam.bootloader
- cp build/esp32.esp32.t-beam/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tbeam.partitions
+ cp build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bin build/rnode_firmware_tbeam.bin
+ cp build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_tbeam.bootloader
+ cp build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_tbeam.partitions
zip --junk-paths ./Release/rnode_firmware_tbeam.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tbeam.boot_app0 build/rnode_firmware_tbeam.bin build/rnode_firmware_tbeam.bootloader build/rnode_firmware_tbeam.partitions
rm -r build
rm -r build
release-heltec32_v2: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0
- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bin build/rnode_firmware_heltec32v2.bin
- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_heltec32v2.bootloader
- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.partitions.bin build/rnode_firmware_heltec32v2.partitions
- zip --junk-paths ./Release/rnode_firmware_heltec32v2.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_heltec32v2.boot_app0 build/rnode_firmware_heltec32v2.bin build/rnode_firmware_heltec32v2.bootloader build/rnode_firmware_heltec32v2.partitions
- rm -r build
+ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.bin build/rnode_firmware_heltec32v2.bin
+ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_heltec32v2.bootloader
+ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_heltec32v2.partitions
release-heltec32_v3:
- arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v3.boot_app0
-- cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.bin build/rnode_firmware_heltec32v3.bin
-- cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_heltec32v3.bootloader
-- cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_heltec32v3.partitions
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.bin build/rnode_firmware_heltec32v3.bin
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_heltec32v3.bootloader
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_heltec32v3.partitions
zip --junk-paths ./Release/rnode_firmware_heltec32v3.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_heltec32v3.boot_app0 build/rnode_firmware_heltec32v3.bin build/rnode_firmware_heltec32v3.bootloader build/rnode_firmware_heltec32v3.partitions
rm -r build
++release-heltec_w_paper:
++ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 $(COMMON_BUILD_FLAGS) --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_heltecwpaper.boot_app0
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.bin build/rnode_firmware_heltecwpaper.bin
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_heltecwpaper.bootloader
++ cp build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_heltecwpaper.partitions
++ zip --junk-paths ./Release/rnode_firmware_heltecwpaper.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_heltecwpaper.boot_app0 build/rnode_firmware_heltecwpaper.bin build/rnode_firmware_heltecwpaper.bootloader build/rnode_firmware_heltecwpaper.partitions
++ rm -r build
++
release-heltec32_v2_extled: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\""
+ arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0
-- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bin build/rnode_firmware_heltec32v2.bin
-- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_heltec32v2.bootloader
-- cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.partitions.bin build/rnode_firmware_heltec32v2.partitions
++ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.bin build/rnode_firmware_heltec32v2.bin
++ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_heltec32v2.bootloader
++ cp build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_heltec32v2.partitions
zip --junk-paths ./Release/rnode_firmware_heltec32v2.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_heltec32v2.boot_app0 build/rnode_firmware_heltec32v2.bin build/rnode_firmware_heltec32v2.bootloader build/rnode_firmware_heltec32v2.partitions
rm -r build
release-rnode_ng_20: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_ng20.boot_app0
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_ng20.bin
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_ng20.bootloader
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_ng20.partitions
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_ng20.bin
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_ng20.bootloader
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_ng20.partitions
zip --junk-paths ./Release/rnode_firmware_ng20.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_ng20.boot_app0 build/rnode_firmware_ng20.bin build/rnode_firmware_ng20.bootloader build/rnode_firmware_ng20.partitions
rm -r build
release-rnode_ng_21: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\""
+ arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_ng21.boot_app0
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_ng21.bin
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_ng21.bootloader
-- cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_ng21.partitions
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_ng21.bin
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_ng21.bootloader
++ cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_ng21.partitions
zip --junk-paths ./Release/rnode_firmware_ng21.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_ng21.boot_app0 build/rnode_firmware_ng21.bin build/rnode_firmware_ng21.bootloader build/rnode_firmware_ng21.partitions
rm -r build
- release-techo: release-techo4 release-techo9
-
- release-techo4:
- arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\" \"-DBOARD_VARIANT=0x16\""
- cp build/adafruit.nrf52.pca10056/RNode_Firmware_CE.ino.hex build/rnode_firmware_techo4.hex
- adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_techo4.hex Release/rnode_firmware_techo4.zip
- rm -r build
-
- release-techo9:
- arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\" \"-DBOARD_VARIANT=0x17\""
- cp build/adafruit.nrf52.pca10056/RNode_Firmware_CE.ino.hex build/rnode_firmware_techo9.hex
- adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_techo9.hex Release/rnode_firmware_techo9.zip
++release-techo:
++ arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
++ cp build/adafruit.nrf52.pca10056/RNode_Firmware_CE.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
+ rm -r build
+
release-t3s3:
- arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x03\""
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_MODEL=0xAB\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3.boot_app0
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3.partitions
+ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_t3s3.bin
+ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_t3s3.bootloader
+ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_t3s3.partitions
zip --junk-paths ./Release/rnode_firmware_t3s3.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3.boot_app0 build/rnode_firmware_t3s3.bin build/rnode_firmware_t3s3.bootloader build/rnode_firmware_t3s3.partitions
rm -r build
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3_sx126x.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3_sx126x.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3_sx126x.partitions
+release-e22_esp32:
+ arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
+ cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_esp32_e22.boot_app0
+ cp build/esp32.esp32.esp32/RNode_Firmware_CE.ino.bin build/rnode_firmware_esp32_e22.bin
+ cp build/esp32.esp32.esp32/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_esp32_e22.bootloader
+ cp build/esp32.esp32.esp32/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_esp32_e22.partitions
+ zip --junk-paths ./Release/rnode_firmware_esp32_e22.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_esp32_e22.boot_app0 build/rnode_firmware_esp32_e22.bin build/rnode_firmware_esp32_e22.bootloader build/rnode_firmware_esp32_e22.partitions
+ rm -r build
+
+release-t3s3_sx126x:
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_MODEL=0xA1\""
+ cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx126x.boot_app0
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_tdeck.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tdeck.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tdeck.partitions
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_t3s3_sx126x.bin
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_t3s3_sx126x.bootloader
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_t3s3_sx126x.partitions
+ zip --junk-paths ./Release/rnode_firmware_t3s3_sx126x.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx126x.boot_app0 build/rnode_firmware_t3s3_sx126x.bin build/rnode_firmware_t3s3_sx126x.bootloader build/rnode_firmware_t3s3_sx126x.partitions
+ rm -r build
+
+release-tdeck:
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3B\""
+ cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tdeck.boot_app0
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3_sx1280_pa.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3_sx1280_pa.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3_sx1280_pa.partitions
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_tdeck.bin
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_tdeck.bootloader
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_tdeck.partitions
+ zip --junk-paths ./Release/rnode_firmware_tdeck.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tdeck.boot_app0 build/rnode_firmware_tdeck.bin build/rnode_firmware_tdeck.bootloader build/rnode_firmware_tdeck.partitions
+ rm -r build
+
+ release-t3s3_sx1280_pa:
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x04\""
+ cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx1280_pa.boot_app0
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3_sx127x.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3_sx127x.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3_sx127x.partitions
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_t3s3_sx1280_pa.bin
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_t3s3_sx1280_pa.bootloader
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_t3s3_sx1280_pa.partitions
+ zip --junk-paths ./Release/rnode_firmware_t3s3_sx1280_pa.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx1280_pa.boot_app0 build/rnode_firmware_t3s3_sx1280_pa.bin build/rnode_firmware_t3s3_sx1280_pa.bootloader build/rnode_firmware_t3s3_sx1280_pa.partitions
+ rm -r build
+
+ release-t3s3_sx127x:
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x01\""
+ cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx127x.boot_app0
-release-tdeck:
- arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3B\""
- cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tdeck.boot_app0
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_tdeck.bin
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tdeck.bootloader
- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tdeck.partitions
- zip --junk-paths ./Release/rnode_firmware_tdeck.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tdeck.boot_app0 build/rnode_firmware_tdeck.bin build/rnode_firmware_tdeck.bootloader build/rnode_firmware_tdeck.partitions
- rm -r build
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_t3s3_sx127x.bin
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_t3s3_sx127x.bootloader
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_t3s3_sx127x.partitions
+ zip --junk-paths ./Release/rnode_firmware_t3s3_sx127x.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx127x.boot_app0 build/rnode_firmware_t3s3_sx127x.bin build/rnode_firmware_t3s3_sx127x.bootloader build/rnode_firmware_t3s3_sx127x.partitions
+ rm -r build
+
+
release-tbeam_supreme:
- arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3D\""
+ arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3D\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam_supreme.boot_app0
-- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_tbeam_supreme.bin
-- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tbeam_supreme.bootloader
-- cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tbeam_supreme.partitions
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin build/rnode_firmware_tbeam_supreme.bin
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_tbeam_supreme.bootloader
++ cp build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_tbeam_supreme.partitions
zip --junk-paths ./Release/rnode_firmware_tbeam_supreme.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tbeam_supreme.boot_app0 build/rnode_firmware_tbeam_supreme.bin build/rnode_firmware_tbeam_supreme.bootloader build/rnode_firmware_tbeam_supreme.partitions
rm -r build
-
release-featheresp32: check_bt_buffers
- arduino-cli compile --fqbn esp32:esp32:featheresp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
+ arduino-cli compile --fqbn esp32:esp32:featheresp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_featheresp32.boot_app0
- cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bin build/rnode_firmware_featheresp32.bin
- cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_featheresp32.bootloader
- cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_featheresp32.partitions
+ cp build/esp32.esp32.featheresp32/RNode_Firmware_CE.ino.bin build/rnode_firmware_featheresp32.bin
+ cp build/esp32.esp32.featheresp32/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_featheresp32.bootloader
+ cp build/esp32.esp32.featheresp32/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_featheresp32.partitions
zip --junk-paths ./Release/rnode_firmware_featheresp32.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_featheresp32.boot_app0 build/rnode_firmware_featheresp32.bin build/rnode_firmware_featheresp32.bootloader build/rnode_firmware_featheresp32.partitions
rm -r build
zip --junk-paths ./Release/rnode_firmware_esp32_generic.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_esp32_generic.boot_app0 build/rnode_firmware_esp32_generic.bin build/rnode_firmware_esp32_generic.bootloader build/rnode_firmware_esp32_generic.partitions
rm -r build
-release-mega2560:
- 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
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x12\""
+ cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.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
- arduino-cli compile --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\""
- cp build/Heltec_nRF52.Heltec_nRF52.HT-n5262/RNode_Firmware.ino.hex build/rnode_firmware_heltec_t114.hex
+ rm -r build
+
+release-rak4631_sx1280:
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x14\""
+ cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.ino.hex build/rnode_firmware_rak4631_sx1280.hex
+ adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_rak4631_sx1280.hex Release/rnode_firmware_rak4631_sx1280.zip
+ rm -r build
+
+release-opencom-xl:
+ arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x52\" \"-DBOARD_VARIANT=0x21\""
+ cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.ino.hex build/rnode_firmware_opencom_xl.hex
+ adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_opencom_xl.hex Release/rnode_firmware_opencom_xl.zip
+ rm -r build
+
+ release-heltec_t114:
-
-release-techo:
- arduino-cli compile --log --fqbn adafruit:nrf52:pca10056 -e --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
- 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
++ arduino-cli compile --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\""
++ cp build/Heltec_nRF52.Heltec_nRF52.HT-n5262/RNode_Firmware_CE.ino.hex build/rnode_firmware_heltec_t114.hex
+ adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_heltec_t114.hex Release/rnode_firmware_heltec_t114.zip
bool bat_voltage_dropping = false;
float bat_delay_v = 0;
float bat_state_change_v = 0;
+#elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
+ #include "nrfx_power.h"
+ #define BAT_C_SAMPLES 7
+ #define BAT_D_SAMPLES 2
+ #define BAT_V_MIN 2.75
+ #define BAT_V_MAX 4.2
+ #define BAT_V_FLOAT 4.22
+ #define BAT_SAMPLES 5
+ #define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12 - bit ADC resolution = 3000mV / 4096
+ #define VBAT_DIVIDER_COMP (1.73) // Compensation factor for the VBAT divider
+ #define VBAT_MV_PER_LSB_FIN (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
+ #define PIN_VBAT WB_A0
+ 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;
+ #elif BOARD_MODEL == BOARD_T3S3
+ #define BAT_V_MIN 3.15
+ #define BAT_V_MAX 4.217
+ #define BAT_V_CHG 4.48
+ #define BAT_V_FLOAT 4.33
+ #define BAT_SAMPLES 5
+ const uint8_t pin_vbat = 1;
+ 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;
#elif BOARD_MODEL == BOARD_TDECK
#define BAT_V_MIN 3.15
#define BAT_V_MAX 4.3
## Latest Release
-The latest release, installable through `rnodeconf`, is version `1.81`. You must have at least version `2.4.0` of `rnodeconf` installed to update the RNode Firmware to version `1.81`. Get it by updating the `rns` package to at least version `0.9.0`.
-
-## A Self-Replicating System
+The latest release, installable through `rnodeconf`, is version `1.73`. This release brings the following changes:
+
+- Added multiple interface support - for supported boards you may now use more than one radio modem at once! Currently the only supported board is the RAK4631 with the right hardware.
+- Fixes for various issues with the SX1280 - data rates are now at expected speeds overall.
+- Added PA calculations for the LoRa1280F27 - this allows for the TX power to be set accurately for this modem.
+- Fixes for OLED compilation errors as a result of commit 055083f.
+- Added switching graphics to the display - shows statistics from up to 2 modems simultaneously.
+- Fix TNC EEPROM settings not being saved - courtesy of @attermann
+- Fix ESP32 linker errors - BSP version is now fixed at 2.0.17, using the older crosstool-ng linker from previous versions (2021r1)
+
- You must have at least version `2.1.3` of `rnodeconf` installed to update the RNode Firmware to version `1.73`. Get it by updating the `rns` package to at least version `0.6.4`.
++You must have at least version `2.1.3` of `rnodeconf` installed to update your RNode Firmware install to version `1.73`. Get it by updating the `rns` package to at least version `0.6.4`.
+
+## Supported products and boards
+
+### Products
+| Name | Manufacturer | Link | Transceiver | MCU | Description |
+| :---: | :---: | :---: | :---: | :---: | :---: |
+| Handheld v2.x RNodes | [Mark Qvist](https://unsigned.io) | [Buy here](https://unsigned.io/shop/product/handheld-rnode) | SX1276 | ESP32 |
+| openCom XL | [Liberated Embedded Systems](https://liberatedsystems.co.uk) | [Buy here](https://store.liberatedsystems.co.uk/product/opencom-xl/) | SX1262 & SX1280 | nRF52 | Supports utilisation of both modems at once |
+
+### Homebrew devices
+| Board name | Link | Transceiver | MCU | Description |
+| :--- | :---: | :---: | :---: | :---: |
+| RAK4631 | [Buy here](https://store.rakwireless.com/products/rak4631-lpwan-node?m=5&h=wisblock-core) | SX1262 | nRF52 |
+| LilyGO T-BEAM v1.1 | [Buy here](https://www.lilygo.cc/products/t-beam-v1-1-esp32-lora-module) | SX1276/8 or SX1262 | ESP32 |
++| LilyGO T-Beam Supreme | [Buy here](https://lilygo.cc/products/t-beam-supreme) | SX1262 | ESP32-S3 |
+| LilyGO LoRa32 v1.0 | [Buy here](https://www.lilygo.cc/products/lora32-v1-0) | SX1276/8 | ESP32 |
+| LilyGO LoRa32 v2.0 | No link | SX1276/8 | ESP32 | Discontinued? |
+| LilyGO LoRa32 v2.1 | [Buy here](https://www.lilygo.cc/products/lora3) | SX1276/8 | ESP32 | With and without TCXO |
++| LilyGO T-Deck | [Buy here](https://lilygo.cc/products/t-deck) | SX1262 | ESP32-S3 |
+| Heltec LoRa32 v2 | No link | SX1276/8 | ESP32 | Discontinued? |
+| Heltec LoRa32 v3 | [Buy here](https://heltec.org/project/wifi-lora-32-v3/) | SX1262 | ESP32 |
++| Heltec Wireless Paper | [Buy here](https://heltec.org/project/wireless-paper/) | SX1262 | ESP32-S3 |
++| Heltec T114 | [Buy here](https://heltec.org/project/mesh-node-t114/) | SX1262 | nRF52 |
+| Homebrew ESP32 boards | | Any supported | ESP32 | This can be any board with an Adafruit Feather (or generic) ESP32 chip |
-If you notice the presence of a circularity in the naming of the system as a whole, and the physical devices, it is no coincidence. Every RNode contains the seeds necessary to reproduce the system, the [RNode Bootstrap Console](https://unsigned.io/rnode_bootstrap_console), which is hosted locally on every RNode, and can be activated and accesses at any time - no Internet required.
-
-The designs, guides and software stored within allows users to create more RNodes, and even to bootstrap entire communications networks, completely independently of existing infrastructure, or in situations where infrastructure has become unreliable or is broken.
++- LilyGO LoRa32 v1.0 devices
++- LilyGO LoRa32 v2.0 devices
++- LilyGO LoRa32 v2.1 devices (with and without TCXO)
++- LilyGO T3S3 devices with SX1276/8 LoRa chips
++- LilyGO T3S3 devices with SX1262/8 LoRa chips
++- LilyGO T3S3 devices with SX1280 LoRa chips
++- LilyGO T-Echo devices
++- Heltec LoRa32 v2 devices
++- Heltec LoRa32 v3 devices
++- Heltec T114 devices
++- RAK4631 devices
++- Homebrew RNodes based on ATmega1284p boards
++- Homebrew RNodes based on ATmega2560 boards
++- Homebrew RNodes based on Adafruit Feather ESP32 boards
++- Homebrew RNodes based on generic ESP32 boards
+
-<img src="Documentation/images/126dcfe92fb7.webp" width="100%"/>
+It's easy to create your own RNodes from one of the supported development boards and devices. If a device or board you want to use is not yet supported, you are welcome to [join the effort](Documentation/CONTRIBUTING.md) and help create a board definition and pin mapping for it!
-<center><i>Where there is no Internet, RNodes will still communicate</i></center><br/><br/>
+<!--<img src="Documentation/images/devboards_1.webp" width="100%"/>-->
-The production of one particular RNode device is not an end, but the potential starting point of a new branch of devices on the tree of the RNode system as a whole.
+## Supported Transceiver Modules
+The RNode Firmware supports all transceiver modules based on the following chips:
+* Semtech SX1262
+* Semtech SX1276
+* Semtech SX1278
+* Semtech SX1280
-This tree fits into the larger biome of Free & Open Communications Systems, which I hope that you - by using communications tools like RNode - will help grow and prosper.
+These also must have an **SPI interface** and **DIO_0 (sometimes called DIO_1)** pin connected to the MCU directly.
## One Tool, Many Uses
--- /dev/null
- #endif
+// Copyright (C) 2024, Mark Qvist
+
+// 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 <https://www.gnu.org/licenses/>.
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Utilities.h"
+
+#if MCU_VARIANT == MCU_NRF52
+ #define INTERFACE_SPI
+ #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
+ // Required because on RAK4631, non-default SPI pins must be initialised when class is declared.
+ SPIClass interface_spi[1] = {
+ // SX1262
+ SPIClass(
+ NRF_SPIM2,
+ interface_pins[0][3],
+ interface_pins[0][1],
+ interface_pins[0][2]
+ )
+ };
+ #elif BOARD_MODEL == BOARD_TECHO
+ SPIClass interface_spi[1] = {
+ // SX1262
+ SPIClass(
+ NRF_SPIM3,
+ interface_pins[0][3],
+ interface_pins[0][1],
+ interface_pins[0][2]
+ )
+ };
+ #endif
+#endif
+
+#ifndef INTERFACE_SPI
+// INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method.
+// Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors.
+#define INTERFACE_SPI
+SPIClass interface_spi[1];
+#endif
+
+FIFOBuffer serialFIFO;
+uint8_t serialBuffer[CONFIG_UART_BUFFER_SIZE+1];
+
+uint16_t packet_starts_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
+
+uint16_t packet_lengths_buf[(CONFIG_QUEUE_MAX_LENGTH)+1];
+
+FIFOBuffer16 packet_starts[INTERFACE_COUNT];
+FIFOBuffer16 packet_lengths[INTERFACE_COUNT];
+
+volatile uint8_t queue_height[INTERFACE_COUNT] = {0};
+volatile uint16_t queued_bytes[INTERFACE_COUNT] = {0};
+
+volatile uint16_t queue_cursor[INTERFACE_COUNT] = {0};
+volatile uint16_t current_packet_start[INTERFACE_COUNT] = {0};
+volatile bool serial_buffering = false;
+#if HAS_BLUETOOTH || HAS_BLE == true
+ bool bt_init_ran = false;
+#endif
+
+#if HAS_CONSOLE
+ #include "Console.h"
+#endif
+
+#define MODEM_QUEUE_SIZE 4*INTERFACE_COUNT
+typedef struct {
+ size_t len;
+ int rssi;
+ int snr_raw;
+ uint8_t interface;
+ uint8_t data[];
+} modem_packet_t;
+static xQueueHandle modem_packet_queue = NULL;
+
+char sbuf[128];
+
+uint8_t *packet_queue[INTERFACE_COUNT];
+
+void setup() {
+ #if MCU_VARIANT == MCU_ESP32
+ boot_seq();
+ EEPROM.begin(EEPROM_SIZE);
+ Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
+
+ #if BOARD_MODEL == BOARD_TDECK
+ pinMode(pin_poweron, OUTPUT);
+ digitalWrite(pin_poweron, HIGH);
+
+ pinMode(SD_CS, OUTPUT);
+ pinMode(DISPLAY_CS, OUTPUT);
+ digitalWrite(SD_CS, HIGH);
+ digitalWrite(DISPLAY_CS, HIGH);
+
+ pinMode(DISPLAY_BL_PIN, OUTPUT);
+ #endif
- #if MCU_VARIANT == MCU_NRF52
- if (!eeprom_begin()) {
- Serial.write("EEPROM initialisation failed.\r\n");
- }
+
- #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_RNODE_NG_22 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_OPENCOM_XL
++ #elif MCU_VARIANT == MCU_NRF52
++ #if BOARD_MODEL == BOARD_TECHO
++ delay(200);
++ pinMode(PIN_VEXT_EN, OUTPUT);
++ digitalWrite(PIN_VEXT_EN, HIGH);
++ pinMode(pin_btn_usr1, INPUT_PULLUP);
++ pinMode(pin_btn_touch, INPUT_PULLUP);
++ pinMode(PIN_LED_RED, OUTPUT);
++ pinMode(PIN_LED_GREEN, OUTPUT);
++ pinMode(PIN_LED_BLUE, OUTPUT);
++ delay(200);
++ #endif
++
++ if (!eeprom_begin()) { Serial.write("EEPROM initialisation failed.\r\n"); }
+ #endif
+
+ // Seed the PRNG for CSMA R-value selection
+ # if MCU_VARIANT == MCU_ESP32
+ // On ESP32, get the seed value from the
+ // hardware RNG
+ int seed_val = (int)esp_random();
+ #else
+ // Otherwise, get a pseudo-random seed
+ // value from an unconnected analog pin
+ int seed_val = analogRead(0);
+ #endif
+ randomSeed(seed_val);
+
+ // Initialise serial communication
+ memset(serialBuffer, 0, sizeof(serialBuffer));
+ fifo_init(&serialFIFO, serialBuffer, CONFIG_UART_BUFFER_SIZE);
+
+ Serial.begin(serial_baudrate);
+
+ #if HAS_NP
+ led_init();
+ #endif
+
- case SX126X:
++ #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_OPENCOM_XL
+ // Some boards need to wait until the hardware UART is set up before booting
+ // the full firmware. In the case of the RAK4631/TECHO, 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
+
+ // Configure input and output pins
+ #if HAS_INPUT
+ input_init();
+ #endif
+
+ #if HAS_NP == false
+ pinMode(pin_led_rx, OUTPUT);
+ pinMode(pin_led_tx, OUTPUT);
+ #endif
+
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ if (interface_pins[i][9] != -1) {
+ pinMode(interface_pins[i][9], OUTPUT);
+ digitalWrite(interface_pins[i][9], HIGH);
+ }
+ }
+
+ // Initialise buffers
+ memset(pbuf, 0, sizeof(pbuf));
+ memset(cmdbuf, 0, sizeof(cmdbuf));
+
+ memset(packet_starts_buf, 0, sizeof(packet_starts_buf));
+ memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
+
+ memset(seq, 0xFF, sizeof(seq));
+ memset(read_len, 0, sizeof(read_len));
+
+ modem_packet_queue = xQueueCreate(MODEM_QUEUE_SIZE, sizeof(modem_packet_t*));
+
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ fifo16_init(&packet_starts[i], packet_starts_buf, CONFIG_QUEUE_MAX_LENGTH+1);
+ fifo16_init(&packet_lengths[i], packet_lengths_buf, CONFIG_QUEUE_MAX_LENGTH+1);
+ packet_queue[i] = (uint8_t*)malloc(getQueueSize(i)+1);
+ }
+
+ memset(packet_rdy_interfaces_buf, 0, sizeof(packet_rdy_interfaces_buf));
+
+ fifo_init(&packet_rdy_interfaces, packet_rdy_interfaces_buf, MAX_INTERFACES);
+
+ // add call to init_channel_stats here? \todo
+
+ // Create and configure interface objects
+ for (uint8_t i = 0; i < INTERFACE_COUNT; i++) {
+ switch (interfaces[i]) {
- case SX127X:
+ case SX1262:
+ {
+ sx126x* obj;
+ // if default spi enabled
+ if (interface_cfg[i][0]) {
+ obj = new sx126x(i, &SPI, interface_cfg[i][1],
+ interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
+ interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
+ interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
+ }
+ else {
+ obj = new sx126x(i, &interface_spi[i], interface_cfg[i][1],
+ interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
+ interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
+ interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
+ }
+ interface_obj[i] = obj;
+ interface_obj_sorted[i] = obj;
+ break;
+ }
+
- case SX128X:
+ case SX1276:
+ case SX1278:
+ {
+ sx127x* obj;
+ // if default spi enabled
+ if (interface_cfg[i][0]) {
+ obj = new sx127x(i, &SPI, interface_pins[i][0],
+ interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
+ interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
+ }
+ else {
+ obj = new sx127x(i, &interface_spi[i], interface_pins[i][0],
+ interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
+ interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
+ }
+ interface_obj[i] = obj;
+ interface_obj_sorted[i] = obj;
+ break;
+ }
+
- case SX126X:
+ case SX1280:
+ {
+ sx128x* obj;
+ // if default spi enabled
+ if (interface_cfg[i][0]) {
+ obj = new sx128x(i, &SPI, interface_cfg[i][1],
+ interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
+ interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
+ interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
+ }
+ else {
+ obj = new sx128x(i, &interface_spi[i], interface_cfg[i][1],
+ interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
+ interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
+ interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
+ }
+ interface_obj[i] = obj;
+ interface_obj_sorted[i] = obj;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
++ #if BOARD_MODEL == BOARD_T3S3 && BOARD_VARIANT == MODEL_AC
++ // Fix weird radio not found bug on T3S3 SX1280
++ delay(300);
++ interface_obj[0]->reset();
++ delay(100);
++ #endif
++
+ // Check installed transceiver chip(s) and probe boot parameters. If any of
+ // the configured modems cannot be initialised, do not boot
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ switch (interfaces[i]) {
- case SX127X:
+ case SX1262:
- case SX128X:
+ case SX1276:
+ case SX1278:
- uint32_t lfr = selected_radio->getFrequency();
- if (lfr == 0) {
- // Normal boot
- } else if (lfr == M_FRQ_R) {
- // Quick reboot
- #if HAS_CONSOLE
- if (rtc_get_reset_reason(0) == POWERON_RESET) {
- console_active = true;
+ case SX1280:
+ selected_radio = interface_obj[i];
+ break;
+
+ default:
+ modems_installed = false;
+ break;
+ }
+ if (selected_radio->preInit()) {
+ modems_installed = true;
- #endif
- } else {
- // Unknown boot
- }
- selected_radio->setFrequency(M_FRQ_S);
++ #if HAS_INPUT
++ // Skip quick-reset console activation
++ #else
++ uint32_t lfr = selected_radio->getFrequency();
++ if (lfr == 0) {
++ // Normal boot
++ } else if (lfr == M_FRQ_R) {
++ // Quick reboot
++ #if HAS_CONSOLE
++ if (rtc_get_reset_reason(0) == POWERON_RESET) {
++ console_active = true;
++ }
++ #endif
++ } else {
++ // Unknown boot
+ }
- eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF);
++ selected_radio->setFrequency(M_FRQ_S);
++ #endif
+ } else {
+ modems_installed = false;
+ }
+ if (!modems_installed) {
+ break;
+ }
+ }
+
+ #if HAS_DISPLAY
+ #if HAS_EEPROM
+ if (EEPROM.read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
+ #elif MCU_VARIANT == MCU_NRF52
+ if (eeprom_read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) {
+ #endif
+ eeprom_update(eeprom_addr(ADDR_CONF_DSET), CONF_OK_BYTE);
- #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
- // Poll and process incoming serial commands whilst e-ink display is
- // refreshing to make device still seem responsive
- display_add_callback(process_serial);
++ #if BOARD_MODEL == BOARD_TECHO
++ eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0x03);
++ #else
++ eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF);
++ #endif
+ }
-
++ #if BOARD_MODEL == BOARD_OPENCOM_XL && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
++ // On this board it isn't possible to run the main loop whilst the
++ // display is updating as the SPI pins are shared between the display and
++ // secondary modem. Because running the main loop causes a lockup, we
++ // just run the serial poll loop instead.
++ display_add_callback(process_serial);
++ #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
++ display_add_callback(work_while_waiting);
+ #endif
++
++ display_unblank();
+ disp_ready = display_init();
+ update_display();
+ #endif
+
+ #if HAS_PMU == true
+ pmu_ready = init_pmu();
+ #endif
+
+ #if HAS_BLUETOOTH || HAS_BLE == true
+ bt_init();
+ bt_init_ran = true;
+ #endif
+
+ if (console_active) {
+ #if HAS_CONSOLE
+ console_start();
+ #else
+ kiss_indicate_reset();
+ #endif
+ } else {
+ kiss_indicate_reset();
+ }
+
++ for (int i = 0; i < INTERFACE_COUNT; i++) {
++ selected_radio = interface_obj[i];
++ if (interfaces[i] == SX1280) {
++ selected_radio->setAvdInterference(false);
++ }
++ if (selected_radio->getAvdInterference()) {
++ #if HAS_EEPROM
++ uint8_t ia_conf = EEPROM.read(eeprom_addr(ADDR_CONF_DIA));
++ if (ia_conf == 0x00) { selected_radio->setAvdInterference(true); }
++ else { selected_radio->setAvdInterference(false); }
++ #elif MCU_VARIANT == MCU_NRF52
++ uint8_t ia_conf = eeprom_read(eeprom_addr(ADDR_CONF_DIA));
++ if (ia_conf == 0x00) { selected_radio->setAvdInterference(true); }
++ else { selected_radio->setAvdInterference(false); }
++ #endif
++ }
++ }
++
++
+ // Validate board health, EEPROM and config
+ validate_status();
+}
+
+void lora_receive(RadioInterface* radio) {
+ if (!implicit) {
+ radio->receive();
+ } else {
+ radio->receive(implicit_l);
+ }
+}
+
+inline void kiss_write_packet(int index) {
- if (!radio->getRadioLock() && hw_ready) {
- if (!radio->begin()) {
- // The radio could not be started.
- // Indicate this failure over both the
- // serial port and with the onboard LEDs
- kiss_indicate_error(ERROR_INITRADIO);
- led_indicate_error(0);
- return false;
- } else {
- radio->enableCrc();
+ // Print index of interface the packet came from
+ serial_write(FEND);
+ serial_write(CMD_SEL_INT);
+ serial_write(index);
+ serial_write(FEND);
+
+ serial_write(FEND);
+ serial_write(CMD_DATA);
+
+ for (uint16_t i = 0; i < read_len[index]; i++) {
+ #if MCU_VARIANT == MCU_NRF52
+ portENTER_CRITICAL();
+ uint8_t byte = pbuf[i];
+ portEXIT_CRITICAL();
+ #else
+ uint8_t byte = pbuf[i];
+ #endif
+
+ if (byte == FEND) { serial_write(FESC); byte = TFEND; }
+ if (byte == FESC) { serial_write(FESC); byte = TFESC; }
+ serial_write(byte);
+ }
+
+ serial_write(FEND);
+ read_len[index] = 0;
+
+ #if MCU_VARIANT == MCU_ESP32 && HAS_BLE
+ bt_flush();
+ #endif
+}
+
+inline void getPacketData(RadioInterface* radio, uint16_t len) {
+ uint8_t index = radio->getIndex();
+ #if MCU_VARIANT != MCU_NRF52
+ while (len-- && read_len[index] < MTU) {
+ pbuf[read_len[index]++] = radio->read();
+ }
+ #else
+ BaseType_t int_mask = taskENTER_CRITICAL_FROM_ISR();
+ while (len-- && read_len[index] < MTU) {
+ pbuf[read_len[index]++] = radio->read();
+ }
+ taskEXIT_CRITICAL_FROM_ISR(int_mask);
+ #endif
+}
+
+inline bool queue_packet(RadioInterface* radio, uint8_t index) {
+ // Allocate packet struct, but abort if there
+ // is not enough memory available.
+ modem_packet_t *modem_packet = (modem_packet_t*)malloc(sizeof(modem_packet_t) + read_len[index]);
+ if(!modem_packet) { memory_low = true; return false; }
+
+ // Get packet RSSI and SNR
+ modem_packet->snr_raw = radio->packetSnrRaw();
+
+ // Pass raw SNR to get RSSI as SX127X driver requires it for calculations
+ modem_packet->rssi = radio->packetRssi(modem_packet->snr_raw);
+
+ modem_packet->interface = index;
+
+ // Send packet to event queue, but free the
+ // allocated memory again if the queue is
+ // unable to receive the packet.
+ modem_packet->len = read_len[index];
+ memcpy(modem_packet->data, pbuf, read_len[index]);
+ if (!modem_packet_queue || xQueueSendFromISR(modem_packet_queue, &modem_packet, NULL) != pdPASS) {
+ free(modem_packet);
+ return false;
+ }
+ return true;
+}
+
+void ISR_VECT receive_callback(uint8_t index, int packet_size) {
+ selected_radio = interface_obj[index];
+ bool ready = false;
+
+ BaseType_t int_mask;
+ if (!promisc) {
+ // The standard operating mode allows large
+ // packets with a payload up to 500 bytes,
+ // by combining two raw LoRa packets.
+ // We read the 1-byte header and extract
+ // packet sequence number and split flags
+
+ uint8_t header = selected_radio->read(); packet_size--;
+ uint8_t sequence = packetSequence(header);
+
+ if (isSplitPacket(header) && seq[index] == SEQ_UNSET) {
+ // This is the first part of a split
+ // packet, so we set the seq variable
+ // and add the data to the buffer
+ #if MCU_VARIANT == MCU_NRF52
+ int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+ #else
+ read_len[index] = 0;
+ #endif
+
+ seq[index] = sequence;
+
+ getPacketData(selected_radio, packet_size);
+
+ } else if (isSplitPacket(header) && seq[index] == sequence) {
+ // This is the second part of a split
+ // packet, so we add it to the buffer
+ // and set the ready flag.
+
+ getPacketData(selected_radio, packet_size);
+
+ seq[index] = SEQ_UNSET;
+ ready = true;
+
+ } else if (isSplitPacket(header) && seq[index] != sequence) {
+ // This split packet does not carry the
+ // same sequence id, so we must assume
+ // that we are seeing the first part of
+ // a new split packet.
+ #if MCU_VARIANT == MCU_NRF52
+ int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+ #else
+ read_len[index] = 0;
+ #endif
+ seq[index] = sequence;
+
+ getPacketData(selected_radio, packet_size);
+
+ } else if (!isSplitPacket(header)) {
+ // This is not a split packet, so we
+ // just read it and set the ready
+ // flag to true.
+
+ if (seq[index] != SEQ_UNSET) {
+ // If we already had part of a split
+ // packet in the buffer, we clear it.
+ #if MCU_VARIANT == MCU_NRF52
+ int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len[index] = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+ #else
+ read_len[index] = 0;
+ #endif
+ seq[index] = SEQ_UNSET;
+ }
+
+ getPacketData(selected_radio, packet_size);
+
+ ready = true;
+ }
+ } else {
+ // In promiscuous mode, raw packets are
+ // output directly to the host
+ read_len[index] = 0;
+
+ getPacketData(selected_radio, packet_size);
+
+ ready = true;
+ }
+
+ if (ready) {
+ queue_packet(selected_radio, index);
+ }
+
+ last_rx = millis();
+}
+
+bool startRadio(RadioInterface* radio) {
+ update_radio_lock(radio);
+
+ if (modems_installed && !console_active) {
- radio->onReceive(receive_callback);
++ if (!radio->getRadioOnline()) {
++ if (!radio->getRadioLock() && hw_ready) {
++ if (!radio->begin()) {
++ // The radio could not be started.
++ // Indicate this failure over both the
++ // serial port and with the onboard LEDs
++ kiss_indicate_error(ERROR_INITRADIO);
++ led_indicate_error(0);
++ return false;
++ } else {
++ radio->enableCrc();
+
- radio->updateBitrate();
- sort_interfaces();
- kiss_indicate_phy_stats(radio);
++ radio->onReceive(receive_callback);
+
- lora_receive(radio);
++ radio->updateBitrate();
++ sort_interfaces();
++ kiss_indicate_phy_stats(radio);
++
++ lora_receive(radio);
+
- // Flash an info pattern to indicate
- // that the radio is now on
++ // Flash an info pattern to indicate
++ // that the radio is now on
++ kiss_indicate_radiostate(radio);
++ led_indicate_info(3);
++ return true;
++ }
+
- led_indicate_info(3);
++ } else {
++ // Flash a warning pattern to indicate
++ // that the radio was locked, and thus
++ // not started
++ kiss_indicate_radiostate(radio);
++ led_indicate_warning(3);
++ return false;
++ }
++ } else {
++ // If radio is already on, we silently
++ // ignore the request.
+ kiss_indicate_radiostate(radio);
-
- } else {
- // Flash a warning pattern to indicate
- // that the radio was locked, and thus
- // not started
- kiss_indicate_radiostate(radio);
- led_indicate_warning(3);
- return false;
- }
- } else {
- // If radio is already on, we silently
- // ignore the request.
- kiss_indicate_radiostate(radio);
- return true;
+ return true;
+ }
- radio->end();
- sort_interfaces();
- kiss_indicate_radiostate(radio);
+ }
+}
+
+void stopRadio(RadioInterface* radio) {
- bool queueFull(RadioInterface* radio) {
++ if (radio->getRadioOnline()) {
++ radio->end();
++ sort_interfaces();
++ kiss_indicate_radiostate(radio);
++ }
+}
+
+void update_radio_lock(RadioInterface* radio) {
+ if (radio->getFrequency() != 0 && radio->getSignalBandwidth() != 0 && radio->getTxPower() != 0xFF && radio->getSpreadingFactor() != 0) {
+ radio->setRadioLock(false);
+ } else {
+ radio->setRadioLock(true);
+ }
+}
+
+// Check if the queue is full for the selected radio.
+// Returns true if full, false if not
- void flushQueue(RadioInterface* radio) {
++bool queue_full(RadioInterface* radio) {
+ return (queue_height[radio->getIndex()] >= (CONFIG_QUEUE_MAX_LENGTH) || queued_bytes[radio->getIndex()] >= (getQueueSize(radio->getIndex())));
+}
+
+volatile bool queue_flushing = false;
+
+// Flushes all packets for the interface
- radio->setPostTxYieldTimeout(millis()+(lora_post_tx_yield_slots*selected_radio->getCSMASlotMS()));
++void flush_queue(RadioInterface* radio) {
+ uint8_t index = radio->getIndex();
+ if (!queue_flushing) {
+ queue_flushing = true;
+
+ led_tx_on();
+ uint16_t processed = 0;
+ uint8_t data_byte;
+
+ while (!fifo16_isempty(&packet_starts[index])) {
+ uint16_t start = fifo16_pop(&packet_starts[index]);
+ uint16_t length = fifo16_pop(&packet_lengths[index]);
+
+ if (length >= MIN_L && length <= MTU) {
+ for (uint16_t i = 0; i < length; i++) {
+ uint16_t pos = (start+i)%(getQueueSize(index));
+ tbuf[i] = packet_queue[index][pos];
+ }
+ transmit(radio, length);
+ processed++;
+ }
+ }
+
+ lora_receive(radio);
+ led_tx_off();
+
- radio->endPacket(); radio->addAirtime(written);
+ }
+
+ queue_height[index] = 0;
+ queued_bytes[index] = 0;
+ selected_radio->updateAirtime();
+ queue_flushing = false;
++}
++
++void pop_queue(RadioInterface* radio) {
++ uint8_t index = radio->getIndex();
++ if (!queue_flushing) {
++ queue_flushing = true;
++ led_tx_on(); uint16_t processed = 0;
++
++ #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
++ if (!fifo16_isempty(&packet_starts[index])) {
++ #else
++ if (!fifo16_isempty_locked(&packet_starts[index])) {
++ #endif
++
++ uint16_t start = fifo16_pop(&packet_starts[index]);
++ uint16_t length = fifo16_pop(&packet_lengths[index]);
++ if (length >= MIN_L && length <= MTU) {
++ for (uint16_t i = 0; i < length; i++) {
++ uint16_t pos = (start+i)%getQueueSize(index);
++ tbuf[i] = packet_queue[index][pos];
++ }
++
++ transmit(radio, length); processed++;
++ }
++ queue_height[index] -= processed;
++ queued_bytes[index] -= length;
++ }
++
++ lora_receive(radio); led_tx_off();
++ }
++
++ radio->updateAirtime();
++
++ queue_flushing = false;
++
+ #if HAS_DISPLAY
+ display_tx = true;
+ #endif
+}
+
+void transmit(RadioInterface* radio, uint16_t size) {
+ if (radio->getRadioOnline()) {
+ if (!promisc) {
+ uint16_t written = 0;
+ uint8_t header = random(256) & 0xF0;
+
+ if (size > SINGLE_MTU - HEADER_L) {
+ header = header | FLAG_SPLIT;
+ }
+
+
+ radio->beginPacket();
+ radio->write(header); written++;
+
+ for (uint16_t i=0; i < size; i++) {
+ radio->write(tbuf[i]);
+
+ written++;
+
+ // Only start a new packet if this is a split packet and it has
+ // exceeded the length of a single packet
+ if (written == 255 && header & 0x0F) {
- radio->addAirtime(written);
++ radio->endPacket(); radio->addAirtime();
+ radio->beginPacket();
+ radio->write(header);
+ written = 1;
+ }
+ }
+
+ if (!radio->endPacket()) {
+ kiss_indicate_error(ERROR_MODEM_TIMEOUT);
+ kiss_indicate_error(ERROR_TXFAILED);
+ led_indicate_error(5);
+ hard_reset();
+ }
- radio->endPacket(); radio->addAirtime(written);
++ radio->addAirtime();
+
+ } else {
+ // In promiscuous mode, we only send out
+ // plain raw LoRa packets with a maximum
+ // payload of 255 bytes
+ led_tx_on();
+ uint16_t written = 0;
+
+ // Cap packets at 255 bytes
+ if (size > SINGLE_MTU) {
+ size = SINGLE_MTU;
+ }
+
+ // If implicit header mode has been set,
+ // set packet length to payload data length
+ if (!implicit) {
+ radio->beginPacket();
+ } else {
+ radio->beginPacket(size);
+ }
+
+ for (uint16_t i=0; i < size; i++) {
+ radio->write(tbuf[i]);
+
+ written++;
+ }
- void serialCallback(uint8_t sbyte) {
++ radio->endPacket(); radio->addAirtime();
+ }
+ last_tx = millis();
+ } else {
+ kiss_indicate_error(ERROR_TXFAILED);
+ led_indicate_error(5);
+ }
+}
+
- selected_radio = interface_obj[interface];
++void serial_callback(uint8_t sbyte) {
+ if (IN_FRAME && sbyte == FEND &&
+ command == CMD_DATA) {
+ IN_FRAME = false;
+
+ if (interface < INTERFACE_COUNT) {
+ if (!fifo16_isfull(&packet_starts[interface]) && (queued_bytes[interface] < (getQueueSize(interface)))) {
+ uint16_t s = current_packet_start[interface];
+ int32_t e = queue_cursor[interface]-1; if (e == -1) e = (getQueueSize(interface))-1;
+ uint16_t l;
+
+ if (s != e) {
+ l = (s < e) ? e - s + 1: (getQueueSize(interface)) - s + e + 1;
+ } else {
+ l = 1;
+ }
+
+ if (l >= MIN_L) {
+ queue_height[interface]++;
+
+ fifo16_push(&packet_starts[interface], s);
+ fifo16_push(&packet_lengths[interface], l);
+ current_packet_start[interface] = queue_cursor[interface];
+ }
+
+ }
+ }
+
+ } else if (sbyte == FEND) {
+ IN_FRAME = true;
+ command = CMD_UNKNOWN;
+ frame_len = 0;
+ } else if (IN_FRAME && frame_len < MTU) {
+ // Have a look at the command byte first
+ if (frame_len == 0 && command == CMD_UNKNOWN) {
+ command = sbyte;
+
+ } else if (command == CMD_SEL_INT) {
+ interface = sbyte;
+ } else if (command == CMD_DATA) {
+ if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+
+ if (interface < INTERFACE_COUNT) {
+ if (queue_height[interface] < CONFIG_QUEUE_MAX_LENGTH && queued_bytes[interface] < (getQueueSize(interface))) {
+ queued_bytes[interface]++;
+ packet_queue[interface][queue_cursor[interface]++] = sbyte;
+ if (queue_cursor[interface] == (getQueueSize(interface))) queue_cursor[interface] = 0;
+ }
+ }
+ }
+ } else if (command == CMD_INTERFACES) {
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ kiss_indicate_interface(i);
+ }
+ } else if (command == CMD_FREQUENCY) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == 4) {
++ selected_radio = interface_obj[interface];
+ uint32_t freq = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
+
- uint32_t bw = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
-
+ if (freq == 0) {
+ kiss_indicate_frequency(selected_radio);
+ } else {
+ if (op_mode == MODE_HOST) selected_radio->setFrequency(freq);
+ kiss_indicate_frequency(selected_radio);
+ }
+ interface = 0;
+ }
+ } else if (command == CMD_BANDWIDTH) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == 4) {
- selected_radio = interface_obj[interface];
+ selected_radio = interface_obj[interface];
++ uint32_t bw = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
+
+ if (bw == 0) {
+ kiss_indicate_bandwidth(selected_radio);
+ } else {
+ if (op_mode == MODE_HOST) selected_radio->setSignalBandwidth(bw);
+ selected_radio->updateBitrate();
+ sort_interfaces();
+ kiss_indicate_bandwidth(selected_radio);
+ kiss_indicate_phy_stats(selected_radio);
+ }
+ interface = 0;
+ }
+ } else if (command == CMD_TXPOWER) {
+ selected_radio = interface_obj[interface];
+
+ if (sbyte == 0xFF) {
+ kiss_indicate_txpower(selected_radio);
+ } else {
+ int8_t txp = (int8_t)sbyte;
+
+ if (op_mode == MODE_HOST) setTXPower(selected_radio, txp);
+ kiss_indicate_txpower(selected_radio);
+ }
+ interface = 0;
+ } else if (command == CMD_SF) {
+ selected_radio = interface_obj[interface];
+
+ if (sbyte == 0xFF) {
+ kiss_indicate_spreadingfactor(selected_radio);
+ } else {
+ int sf = sbyte;
+ if (sf < 5) sf = 5;
+ if (sf > 12) sf = 12;
+
+ if (op_mode == MODE_HOST) selected_radio->setSpreadingFactor(sf);
+ selected_radio->updateBitrate();
+ sort_interfaces();
+ kiss_indicate_spreadingfactor(selected_radio);
+ kiss_indicate_phy_stats(selected_radio);
+ }
+ interface = 0;
+ } else if (command == CMD_CR) {
+ selected_radio = interface_obj[interface];
+ if (sbyte == 0xFF) {
+ kiss_indicate_codingrate(selected_radio);
+ } else {
+ int cr = sbyte;
+ if (cr < 5) cr = 5;
+ if (cr > 8) cr = 8;
+
+ if (op_mode == MODE_HOST) selected_radio->setCodingRate4(cr);
+ selected_radio->updateBitrate();
+ sort_interfaces();
+ kiss_indicate_codingrate(selected_radio);
+ kiss_indicate_phy_stats(selected_radio);
+ }
+ interface = 0;
+ } else if (command == CMD_IMPLICIT) {
+ set_implicit_length(sbyte);
+ kiss_indicate_implicit_length();
+ } else if (command == CMD_LEAVE) {
+ if (sbyte == 0xFF) {
+ //display_unblank();
+ cable_state = CABLE_STATE_DISCONNECTED;
+ //current_rssi = -292;
+ last_rssi = -292;
+ last_rssi_raw = 0x00;
+ last_snr_raw = 0x80;
+ }
+ } else if (command == CMD_RADIO_STATE) {
+ selected_radio = interface_obj[interface];
+ if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
+ if (sbyte == 0xFF) {
+ kiss_indicate_radiostate(selected_radio);
+ } else if (sbyte == 0x00) {
+ stopRadio(selected_radio);
+ } else if (sbyte == 0x01) {
+ startRadio(selected_radio);
+ }
+ interface = 0;
+ } else if (command == CMD_ST_ALOCK) {
- interface = 0;
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == 2) {
++ selected_radio = interface_obj[interface];
+ uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
+
+ if (at == 0) {
+ selected_radio->setSTALock(0.0);
+ } else {
+ int st_airtime_limit = (float)at/(100.0*100.0);
+ if (st_airtime_limit >= 1.0) { st_airtime_limit = 0.0; }
+ selected_radio->setSTALock(st_airtime_limit);
+ }
+ kiss_indicate_st_alock(selected_radio);
++ interface = 0;
+ }
- selected_radio = interface_obj[interface];
+ } else if (command == CMD_LT_ALOCK) {
- interface = 0;
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == 2) {
++ selected_radio = interface_obj[interface];
+ uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
+
+ if (at == 0) {
+ selected_radio->setLTALock(0.0);
+ } else {
+ int lt_airtime_limit = (float)at/(100.0*100.0);
+ if (lt_airtime_limit >= 1.0) { lt_airtime_limit = 0.0; }
+ selected_radio->setLTALock(lt_airtime_limit);
+ }
+ kiss_indicate_lt_alock(selected_radio);
++ interface = 0;
+ }
- if (!queueFull(selected_radio)) {
+ } else if (command == CMD_STAT_RX) {
+ kiss_indicate_stat_rx();
+ } else if (command == CMD_STAT_TX) {
+ kiss_indicate_stat_tx();
+ } else if (command == CMD_STAT_RSSI) {
+ kiss_indicate_stat_rssi(interface_obj[interface]);
+ } else if (command == CMD_RADIO_LOCK) {
+ selected_radio = interface_obj[interface];
+ update_radio_lock(selected_radio);
+ kiss_indicate_radio_lock(selected_radio);
+ interface = 0;
+ } else if (command == CMD_BLINK) {
+ led_indicate_info(sbyte);
+ } else if (command == CMD_RANDOM) {
+ // pick an interface at random to get data from
+ int int_index = random(INTERFACE_COUNT);
+ selected_radio = interface_obj[int_index];
+ kiss_indicate_random(getRandom(selected_radio));
+ interface = 0;
+ } else if (command == CMD_DETECT) {
+ if (sbyte == DETECT_REQ) {
+ if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
+ kiss_indicate_detect();
+ }
+ } else if (command == CMD_PROMISC) {
+ if (sbyte == 0x01) {
+ promisc_enable();
+ } else if (sbyte == 0x00) {
+ promisc_disable();
+ }
+ kiss_indicate_promisc();
+ } else if (command == CMD_READY) {
+ selected_radio = interface_obj[interface];
- //display_unblank();
++ if (!queue_full(selected_radio)) {
+ kiss_indicate_ready();
+ } else {
+ kiss_indicate_not_ready();
+ }
+ } else if (command == CMD_UNLOCK_ROM) {
+ if (sbyte == ROM_UNLOCK_BYTE) {
+ unlock_rom();
+ }
+ } else if (command == CMD_RESET) {
+ if (sbyte == CMD_RESET_BYTE) {
+ hard_reset();
+ }
+ } else if (command == CMD_ROM_READ) {
+ kiss_dump_eeprom();
+ } else if (command == CMD_ROM_WRITE) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == 2) {
+ eeprom_write(cmdbuf[0], cmdbuf[1]);
+ }
+ } else if (command == CMD_FW_VERSION) {
+ kiss_indicate_version();
+ } else if (command == CMD_PLATFORM) {
+ kiss_indicate_platform();
+ } else if (command == CMD_MCU) {
+ kiss_indicate_mcu();
+ } else if (command == CMD_BOARD) {
+ kiss_indicate_board();
+ } else if (command == CMD_CONF_SAVE) {
+ // todo: add extra space in EEPROM so this isn't hardcoded
+ eeprom_conf_save(interface_obj[0]);
+ } else if (command == CMD_CONF_DELETE) {
+ eeprom_conf_delete();
+ } else if (command == CMD_FB_EXT) {
+ #if HAS_DISPLAY == true
+ if (sbyte == 0xFF) {
+ kiss_indicate_fbstate();
+ } else if (sbyte == 0x00) {
+ ext_fb_disable();
+ kiss_indicate_fbstate();
+ } else if (sbyte == 0x01) {
+ ext_fb_enable();
+ kiss_indicate_fbstate();
+ }
+ #endif
+ } else if (command == CMD_FB_WRITE) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+ #if HAS_DISPLAY
+ if (frame_len == 9) {
+ uint8_t line = cmdbuf[0];
+ if (line > 63) line = 63;
+ int fb_o = line*8;
+ memcpy(fb+fb_o, cmdbuf+1, 8);
+ }
+ #endif
+ } else if (command == CMD_FB_READ) {
+ if (sbyte != 0x00) {
+ kiss_indicate_fb();
+ }
++ } else if (command == CMD_DISP_READ) {
++ if (sbyte != 0x00) { kiss_indicate_disp(); }
+ } else if (command == CMD_DEV_HASH) {
+ if (sbyte != 0x00) {
+ kiss_indicate_device_hash();
+ }
+ } else if (command == CMD_DEV_SIG) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == DEV_SIG_LEN) {
+ memcpy(dev_sig, cmdbuf, DEV_SIG_LEN);
+ device_save_signature();
+ }
+ } else if (command == CMD_FW_UPD) {
+ if (sbyte == 0x01) {
+ firmware_update_mode = true;
+ } else {
+ firmware_update_mode = false;
+ }
+ } else if (command == CMD_HASHES) {
+ if (sbyte == 0x01) {
+ kiss_indicate_target_fw_hash();
+ } else if (sbyte == 0x02) {
+ kiss_indicate_fw_hash();
+ } else if (sbyte == 0x03) {
+ kiss_indicate_bootloader_hash();
+ } else if (sbyte == 0x04) {
+ kiss_indicate_partition_table_hash();
+ }
+ } else if (command == CMD_FW_HASH) {
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+ }
+
+ if (frame_len == DEV_HASH_LEN) {
+ memcpy(dev_firmware_hash_target, cmdbuf, DEV_HASH_LEN);
+ device_save_firmware_hash();
+ }
+ } else if (command == CMD_BT_CTRL) {
+ #if HAS_BLUETOOTH || HAS_BLE
+ if (sbyte == 0x00) {
+ bt_stop();
+ bt_conf_save(false);
+ } else if (sbyte == 0x01) {
+ bt_start();
+ bt_conf_save(true);
+ } else if (sbyte == 0x02) {
+ if (bt_state == BT_STATE_OFF) {
+ bt_start();
+ bt_conf_save(true);
+ }
+ if (bt_state != BT_STATE_CONNECTED) {
+ bt_enable_pairing();
+ }
+ }
+ #endif
+ } else if (command == CMD_DISP_INT) {
+ #if HAS_DISPLAY
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ display_intensity = sbyte;
+ di_conf_save(display_intensity);
- //display_unblank();
++ display_unblank();
+ }
+
+ #endif
+ } else if (command == CMD_DISP_ADDR) {
+ #if HAS_DISPLAY
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ display_addr = sbyte;
+ da_conf_save(display_addr);
+ }
+
+ #endif
+ } else if (command == CMD_DISP_BLNK) {
+ #if HAS_DISPLAY
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ db_conf_save(sbyte);
- kiss_indicate_stat_rssi(packet_interface);
- kiss_indicate_stat_snr(packet_interface);
++ display_unblank();
+ }
+
+ #endif
++ } else if (command == CMD_DISP_ROT) {
++ #if HAS_DISPLAY
++ if (sbyte == FESC) {
++ ESCAPE = true;
++ } else {
++ if (ESCAPE) {
++ if (sbyte == TFEND) sbyte = FEND;
++ if (sbyte == TFESC) sbyte = FESC;
++ ESCAPE = false;
++ }
++ drot_conf_save(sbyte);
++ display_unblank();
++ }
++ #endif
++ } else if (command == CMD_DIS_IA) {
++ if (sbyte == FESC) {
++ ESCAPE = true;
++ } else {
++ if (ESCAPE) {
++ if (sbyte == TFEND) sbyte = FEND;
++ if (sbyte == TFESC) sbyte = FESC;
++ ESCAPE = false;
++ }
++ dia_conf_save(sbyte);
++ }
++ } else if (command == CMD_DISP_RCND) {
++ #if HAS_DISPLAY
++ if (sbyte == FESC) {
++ ESCAPE = true;
++ } else {
++ if (ESCAPE) {
++ if (sbyte == TFEND) sbyte = FEND;
++ if (sbyte == TFESC) sbyte = FESC;
++ ESCAPE = false;
++ }
++ if (sbyte > 0x00) recondition_display = true;
++ }
++ #endif
+ } else if (command == CMD_NP_INT) {
+ #if HAS_NP
+ if (sbyte == FESC) {
+ ESCAPE = true;
+ } else {
+ if (ESCAPE) {
+ if (sbyte == TFEND) sbyte = FEND;
+ if (sbyte == TFESC) sbyte = FESC;
+ ESCAPE = false;
+ }
+ sbyte;
+ led_set_intensity(sbyte);
+ np_int_conf_save(sbyte);
+ }
+
+ #endif
+ }
+ }
+}
+
+#if MCU_VARIANT == MCU_ESP32
+ portMUX_TYPE update_lock = portMUX_INITIALIZER_UNLOCKED;
+#endif
+
++bool medium_free(RadioInterface* radio) {
++ radio->updateModemStatus();
++ if (radio->getAvdInterference() && radio->getInterference()) { return false; }
++ return !radio->getDCD();
++}
++
+void validate_status() {
+ #if MCU_VARIANT == MCU_ESP32
+ // TODO: Get ESP32 boot flags
+ uint8_t boot_flags = 0x02;
+ uint8_t F_POR = 0x00;
+ uint8_t F_BOR = 0x00;
+ uint8_t F_WDR = 0x01;
+ #elif MCU_VARIANT == MCU_NRF52
+ // TODO: Get NRF52 boot flags
+ uint8_t boot_flags = 0x02;
+ uint8_t F_POR = 0x00;
+ uint8_t F_BOR = 0x00;
+ uint8_t F_WDR = 0x01;
+ #endif
+
+ if (hw_ready || device_init_done) {
+ hw_ready = false;
+ Serial.write("Error, invalid hardware check state\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ led_indicate_boot_error();
+ }
+
+ if (boot_flags & (1<<F_POR)) {
+ boot_vector = START_FROM_POWERON;
+ } else if (boot_flags & (1<<F_BOR)) {
+ boot_vector = START_FROM_BROWNOUT;
+ } else if (boot_flags & (1<<F_WDR)) {
+ boot_vector = START_FROM_BOOTLOADER;
+ } else {
+ Serial.write("Error, indeterminate boot vector\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ led_indicate_boot_error();
+ }
+
+ if (boot_vector == START_FROM_BOOTLOADER || boot_vector == START_FROM_POWERON) {
+ if (eeprom_lock_set()) {
+ if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
+ if (eeprom_checksum_valid()) {
+ eeprom_ok = true;
+ if (modems_installed) {
+ if (device_init()) {
+ hw_ready = true;
+ } else {
+ hw_ready = false;
+ }
+ } else {
+ hw_ready = false;
+ Serial.write("No radio module found\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ }
+ } else {
+ hw_ready = false;
+ Serial.write("Invalid EEPROM checksum\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ }
+ } else {
+ hw_ready = false;
+ Serial.write("Invalid EEPROM configuration\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ }
+ } else {
+ hw_ready = false;
+ Serial.write("Device unprovisioned, no device configuration found in EEPROM\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ }
+ } else {
+ hw_ready = false;
+ Serial.write("Error, incorrect boot vector\r\n");
+ #if HAS_DISPLAY
+ if (disp_ready) {
+ device_init_done = true;
+ update_display();
+ }
+ #endif
+ led_indicate_boot_error();
+ }
+}
+
++void tx_queue_handler(RadioInterface* radio) {
++ if (queue_height[radio->getIndex()] > 0) {
++ if (radio->getCW() == -1) {
++ radio->setCW(random(radio->getCWMin(), radio->getCWMax()));
++ radio->setCWWaitTarget(radio->getCW() * radio->getCSMASlotMS());
++ }
++
++ if (radio->getDifsWaitStart() == 0) { // DIFS wait not yet started
++ if (medium_free(radio)) { radio->setDifsWaitStart(millis()); return; } // Set DIFS wait start time
++ else { return; } } // Medium not yet free, continue waiting
++
++ else { // We are waiting for DIFS or CW to pass
++ if (!medium_free(radio)) { radio->setDifsWaitStart(0); radio->setCWWaitStart(0); return; } // Medium became occupied while in DIFS wait, restart waiting when free again
++ else { // Medium is free, so continue waiting
++ if (millis() < radio->getDifsWaitStart()+radio->getDifsMS()) { return; } // DIFS has not yet passed, continue waiting
++ else { // DIFS has passed, and we are now in CW wait
++ if (radio->getCWWaitStart() == 0) { radio->setCWWaitStart(millis()); return; } // If we haven't started counting CW wait time, do it from now
++ else { // If we are already counting CW wait time, add it to the counter
++ radio->addCWWaitPassed(millis()-radio->getCWWaitStart()); radio->setCWWaitStart(millis());
++ if (radio->getCWWaitStatus()) { return; } // Contention window wait time has not yet passed, continue waiting
++ else { // Wait time has passed, flush the queue
++ if (!radio->getLimitRate()) { flush_queue(radio); } else { pop_queue(radio); }
++ radio->resetCWWaitPassed(); radio->setCW(-1); radio->setDifsWaitStart(0); }
++ }
++ }
++ }
++ }
++ }
++}
++
++void work_while_waiting() { loop(); }
++
+void loop() {
+ #if MCU_VARIANT == MCU_ESP32
+ modem_packet_t *modem_packet = NULL;
+ if(modem_packet_queue && xQueueReceive(modem_packet_queue, &modem_packet, 0) == pdTRUE && modem_packet) {
+ uint8_t packet_interface = modem_packet->interface;
+ read_len[packet_interface] = modem_packet->len;
+ last_rssi = modem_packet->rssi;
+ last_snr_raw = modem_packet->snr_raw;
+ memcpy(&pbuf, modem_packet->data, modem_packet->len);
+ free(modem_packet);
+ modem_packet = NULL;
+
+ kiss_indicate_stat_rssi(interface_obj[packet_interface]);
+ kiss_indicate_stat_snr(interface_obj[packet_interface]);
+ kiss_write_packet(packet_interface);
+ }
+
+ #elif MCU_VARIANT == MCU_NRF52
+ modem_packet_t *modem_packet = NULL;
+ if(modem_packet_queue && xQueueReceive(modem_packet_queue, &modem_packet, 0) == pdTRUE && modem_packet) {
+ uint8_t packet_interface = modem_packet->interface;
+ read_len[packet_interface] = modem_packet->len;
+ last_rssi = modem_packet->rssi;
+ last_snr_raw = modem_packet->snr_raw;
+ memcpy(&pbuf, modem_packet->data, modem_packet->len);
+ free(modem_packet);
+ modem_packet = NULL;
+
- selected_radio->checkModemStatus();
++ kiss_indicate_stat_rssi(interface_obj[packet_interface]);
++ kiss_indicate_stat_snr(interface_obj[packet_interface]);
+ kiss_write_packet(packet_interface);
+ }
+ #endif
+
+ bool ready = false;
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ selected_radio = interface_obj[i];
+ if (selected_radio->getRadioOnline()) {
- if (queue_height[selected_radio->getIndex()] > 0) {
- uint32_t check_time = millis();
- if (check_time > selected_radio->getPostTxYieldTimeout()) {
- if (selected_radio->getDCDWaiting() && (check_time >= selected_radio->getDCDWaitUntil())) { selected_radio->setDCDWaiting(false); }
- if (!selected_radio->getDCDWaiting()) {
- // todo, will the delay here slow down transmission with
- // multiple interfaces? needs investigation
- for (uint8_t dcd_i = 0; dcd_i < DCD_THRESHOLD*2; dcd_i++) {
- delay(STATUS_INTERVAL_MS); selected_radio->updateModemStatus();
- }
-
- if (!selected_radio->getDCD()) {
- uint8_t csma_r = (uint8_t)random(256);
- if (selected_radio->getCSMAp() >= csma_r) {
- flushQueue(selected_radio);
- } else {
- selected_radio->setDCDWaiting(true);
- selected_radio->setDCDWaitUntil(millis()+selected_radio->getCSMASlotMS());
- }
- }
- }
- }
- }
+ ready = true;
+ }
+ }
+
+
+ // If at least one radio is online then we can continue
+ if (ready) {
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ selected_radio = interface_obj_sorted[i];
+
+ if (selected_radio->calculateALock() || !selected_radio->getRadioOnline()) {
+ // skip this interface
+ continue;
+ }
++ tx_queue_handler(selected_radio);
++ selected_radio->checkModemStatus();
+
- buffer_serial();
- if (!fifo_isempty(&serialFIFO)) serial_poll();
++ // debug
++ Serial.write("Bitrate :"); Serial.print(selected_radio->getBitrate()); Serial.write("\n");
+ }
+
+ } else {
+ if (hw_ready) {
+ if (console_active) {
+ #if HAS_CONSOLE
+ console_loop();
+ #endif
+ } else {
+ led_indicate_standby();
+ }
+ } else {
+ led_indicate_not_ready();
+ // shut down all radio interfaces
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ stopRadio(interface_obj[i]);
+ }
+ }
+ }
+
- #if BOARD_MODEL == BOARD_T3S3
- display_intensity = 0;
- update_display(true);
- #endif
- #if PIN_DISP_SLEEP >= 0
- pinMode(PIN_DISP_SLEEP, OUTPUT);
- digitalWrite(PIN_DISP_SLEEP, DISP_SLEEP_LEVEL);
- #endif
- #if HAS_BLUETOOTH
- if (bt_state == BT_STATE_CONNECTED) {
- bt_stop();
++ process_serial();
+
+ #if HAS_DISPLAY
+ #if DISPLAY == OLED
+ if (disp_ready) update_display();
+ #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
+ // Display refreshes take so long on e-paper displays that they can disrupt
+ // the regular operation of the device. To combat this the time it is
+ // chosen to do so must be strategically chosen. Particularly on the
+ // RAK4631, the display and the potentially installed SX1280 modem share
+ // the same SPI bus. Thus it is not possible to solve this by utilising the
+ // callback functionality to poll the modem in this case. todo, this may be
+ // able to be improved in the future.
+ if (disp_ready) {
+ if (millis() - last_tx >= 4000) {
+ if (millis() - last_rx >= 1000) {
+ update_display();
+ }
+ }
+ }
+ #endif
+ #endif
+
+ #if HAS_PMU
+ if (pmu_ready) update_pmu();
+ #endif
+
+ #if HAS_BLUETOOTH || HAS_BLE == true
+ if (!console_active && bt_ready) update_bt();
+ #endif
+
+ #if HAS_INPUT
+ input_read();
+ #endif
+
+ if (memory_low) {
+ #if PLATFORM == PLATFORM_ESP32
+ if (esp_get_free_heap_size() < 8192) {
+ kiss_indicate_error(ERROR_MEMORY_LOW); memory_low = false;
+ } else {
+ memory_low = false;
+ }
+ #else
+ kiss_indicate_error(ERROR_MEMORY_LOW); memory_low = false;
+ #endif
+ }
+}
+
+void process_serial() {
+ buffer_serial();
+ if (!fifo_isempty(&serialFIFO)) serial_poll();
+}
+
+void sleep_now() {
+ #if HAS_SLEEP == true
- }
++ #if PLATFORM == PLATFORM_ESP32
++ #if BOARD_MODEL == BOARD_T3S3
++ display_intensity = 0;
++ update_display(true);
++ #endif
++ #if PIN_DISP_SLEEP >= 0
++ pinMode(PIN_DISP_SLEEP, OUTPUT);
++ digitalWrite(PIN_DISP_SLEEP, DISP_SLEEP_LEVEL);
++ #endif
++ #if HAS_BLUETOOTH
++ if (bt_state == BT_STATE_CONNECTED) {
++ bt_stop();
++ delay(100);
++ }
++ #endif
++ esp_sleep_enable_ext0_wakeup(PIN_WAKEUP, WAKEUP_LEVEL);
++ esp_deep_sleep_start();
++ #elif PLATFORM == PLATFORM_NRF52
++ #if BOARD_MODEL == BOARD_HELTEC_T114
++ npset(0,0,0);
++ digitalWrite(PIN_VEXT_EN, LOW);
++ digitalWrite(PIN_T114_TFT_BLGT, HIGH);
++ digitalWrite(PIN_T114_TFT_EN, HIGH);
++ #elif BOARD_MODEL == BOARD_TECHO
++ for (uint8_t i = display_intensity; i > 0; i--) { analogWrite(pin_backlight, i-1); delay(1); }
++ epd_black(true); delay(300); epd_black(true); delay(300); epd_black(false);
++ delay(2000);
++ analogWrite(PIN_VEXT_EN, 0);
+ delay(100);
- esp_sleep_enable_ext0_wakeup(PIN_WAKEUP, WAKEUP_LEVEL);
- esp_deep_sleep_start();
++ #endif
++ sd_power_gpregret_set(0, 0x6d);
++ nrf_gpio_cfg_sense_input(pin_btn_usr1, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
++ NRF_POWER->SYSTEMOFF = 1;
+ #endif
- //if (display_blanked) {
- // display_unblank();
- //} else {
+ #endif
+}
+
+void button_event(uint8_t event, unsigned long duration) {
- //}
++ if (display_blanked) {
++ display_unblank();
++ } else {
+ if (duration > 10000) {
+ #if HAS_CONSOLE
+ #if HAS_BLUETOOTH || HAS_BLE
+ bt_stop();
+ #endif
+ console_active = true;
+ console_start();
+ #endif
+ } else if (duration > 5000) {
+ #if HAS_BLUETOOTH || HAS_BLE
+ if (bt_state != BT_STATE_CONNECTED) { bt_enable_pairing(); }
+ #endif
+ } else if (duration > 700) {
+ #if HAS_SLEEP
+ sleep_now();
+ #endif
+ } else {
+ #if HAS_BLUETOOTH || HAS_BLE
+ if (bt_state != BT_STATE_CONNECTED) {
+ if (bt_state == BT_STATE_OFF) {
+ bt_start();
+ bt_conf_save(true);
+ } else {
+ bt_stop();
+ bt_conf_save(false);
+ }
+ }
+ #endif
+ }
- serialCallback(sbyte);
++ }
+}
+
+void poll_buffers() {
+ process_serial();
+}
+
+volatile bool serial_polling = false;
+void serial_poll() {
+ serial_polling = true;
+
+ while (!fifo_isempty(&serialFIFO)) {
+ char sbyte = fifo_pop(&serialFIFO);
++ serial_callback(sbyte);
+ }
+
+ serial_polling = false;
+}
+
+#define MAX_CYCLES 20
+
+void buffer_serial() {
+ if (!serial_buffering) {
+ serial_buffering = true;
+
+ uint8_t c = 0;
+
+ #if HAS_BLUETOOTH || HAS_BLE == true
+ while (
+ c < MAX_CYCLES &&
+ ( (bt_state != BT_STATE_CONNECTED && Serial.available()) || (bt_state == BT_STATE_CONNECTED && SerialBT.available()) )
+ )
+ #else
+ while (c < MAX_CYCLES && Serial.available())
+ #endif
+ {
+ c++;
+
+ #if HAS_BLUETOOTH || HAS_BLE == true
+ if (bt_state == BT_STATE_CONNECTED) {
+ if (!fifo_isfull(&serialFIFO)) {
+ fifo_push(&serialFIFO, SerialBT.read());
+ }
+ } else {
+ if (!fifo_isfull(&serialFIFO)) {
+ fifo_push(&serialFIFO, Serial.read());
+ }
+ }
+ #else
+ if (!fifo_isfull(&serialFIFO)) {
+ fifo_push(&serialFIFO, Serial.read());
+ }
+ #endif
+ }
+
+ serial_buffering = false;
+ }
+}
#define ADDR_CONF_DINT 0xB2
#define ADDR_CONF_DADR 0xB3
#define ADDR_CONF_DBLK 0xB4
+ #define ADDR_CONF_PSET 0xB5
+ #define ADDR_CONF_PINT 0xB6
+ #define ADDR_CONF_BSET 0xB7
+ #define ADDR_CONF_DROT 0xB8
+ #define ADDR_CONF_PSET 0xB5
+ #define ADDR_CONF_PINT 0xB6
+ #define ADDR_CONF_BSET 0xB7
+ #define ADDR_CONF_DIA 0xB9
#define INFO_LOCK_BYTE 0x73
#define CONF_OK_BYTE 0x73
--- /dev/null
- if ((interfaces[i] == SX128X) || (interfaces[i] == SX1280)) {
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
+// Obviously still under the MIT license.
+
+#include "Radio.hpp"
+
+#if PLATFORM == PLATFORM_ESP32
+ #if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3)
+ #include "soc/rtc_wdt.h"
+ #endif
+ #define ISR_VECT IRAM_ATTR
+#else
+ #define ISR_VECT
+#endif
+
+// SX126x registers
+#define OP_RF_FREQ_6X 0x86
+#define OP_SLEEP_6X 0x84
+#define OP_STANDBY_6X 0x80
+#define OP_TX_6X 0x83
+#define OP_RX_6X 0x82
+#define OP_PA_CONFIG_6X 0x95
+#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as
+ // preamble detection, etc for
+ // knowing when it's safe to switch
+ // antenna modes
+#define OP_CLEAR_IRQ_STATUS_6X 0x02
+#define OP_GET_IRQ_STATUS_6X 0x12
+#define OP_RX_BUFFER_STATUS_6X 0x13
+#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet
+#define OP_CURRENT_RSSI_6X 0x15
+#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc.
+#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc.
+#define OP_STATUS_6X 0xC0
+#define OP_TX_PARAMS_6X 0x8E // set dbm, etc
+#define OP_PACKET_TYPE_6X 0x8A
+#define OP_BUFFER_BASE_ADDR_6X 0x8F
+#define OP_READ_REGISTER_6X 0x1D
+#define OP_WRITE_REGISTER_6X 0x0D
+#define OP_DIO3_TCXO_CTRL_6X 0x97
+#define OP_DIO2_RF_CTRL_6X 0x9D
+#define OP_CAD_PARAMS 0x88
+#define OP_CALIBRATE_6X 0x89
+#define OP_RX_TX_FALLBACK_MODE_6X 0x93
+#define OP_REGULATOR_MODE_6X 0x96
+#define OP_CALIBRATE_IMAGE_6X 0x98
+
+#define MASK_CALIBRATE_ALL 0x7f
+
+#define IRQ_TX_DONE_MASK_6X 0x01
+#define IRQ_RX_DONE_MASK_6X 0x02
+#define IRQ_HEADER_DET_MASK_6X 0x10
+#define IRQ_PREAMBLE_DET_MASK_6X 0x04
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
+#define IRQ_ALL_MASK_6X 0b0100001111111111
+
+#define MODE_LONG_RANGE_MODE_6X 0x01
+
+#define OP_FIFO_WRITE_6X 0x0E
+#define OP_FIFO_READ_6X 0x1E
+#define REG_OCP_6X 0x08E7
+#define REG_LNA_6X 0x08AC // no agc in sx1262
+#define REG_SYNC_WORD_MSB_6X 0x0740
+#define REG_SYNC_WORD_LSB_6X 0x0741
+#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
+#define REG_RANDOM_GEN_6X 0x0819
+
+#define MODE_TCXO_3_3V_6X 0x07
+#define MODE_TCXO_3_0V_6X 0x06
+#define MODE_TCXO_2_7V_6X 0x06
+#define MODE_TCXO_2_4V_6X 0x06
+#define MODE_TCXO_2_2V_6X 0x03
+#define MODE_TCXO_1_8V_6X 0x02
+#define MODE_TCXO_1_7V_6X 0x01
+#define MODE_TCXO_1_6V_6X 0x00
+
+#define MODE_STDBY_RC_6X 0x00
+#define MODE_STDBY_XOSC_6X 0x01
+#define MODE_FALLBACK_STDBY_RC_6X 0x20
+#define MODE_IMPLICIT_HEADER 0x01
+#define MODE_EXPLICIT_HEADER 0x00
+
+#define SYNC_WORD_6X 0x1424
+
+#define XTAL_FREQ_6X (double)32000000
+#define FREQ_DIV_6X (double)pow(2.0, 25.0)
+#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X)
+
+extern FIFOBuffer packet_rdy_interfaces;
+extern RadioInterface* interface_obj[];
+
+// ISRs cannot provide parameters to the functions they call. Since we have
+// multiple interfaces, we have to read each dio0 pin for each one and see
+// which one is high. We can then use the index of this pin in the 2D array to
+// signal the correct interface to the main loop
+void ISR_VECT onDio0Rise() {
+ BaseType_t int_status = taskENTER_CRITICAL_FROM_ISR();
+ for (int i = 0; i < INTERFACE_COUNT; i++) {
+ if (digitalRead(interface_pins[i][5]) == HIGH) {
+ if (interface_obj[i]->getPacketValidity()) {
+ interface_obj[i]->handleDio0Rise();
+ }
- _busy(busy), _rxen(rxen), _frequency(0), _sf(0x07), _bw(0x04),
- _cr(0x01), _ldro(0x00), _packetIndex(0), _implicitHeaderMode(0),
++ if (interfaces[i] == SX1280) {
+ // On the SX1280, there is a bug which can cause the busy line
+ // to remain high if a high amount of packets are received when
+ // in continuous RX mode. This is documented as Errata 16.1 in
+ // the SX1280 datasheet v3.2 (page 149)
+ // Therefore, the modem is set into receive mode each time a packet is received.
+ interface_obj[i]->receive();
+ }
+ break;
+ }
+ }
+ taskEXIT_CRITICAL_FROM_ISR(int_status);
+}
+
+sx126x::sx126x(uint8_t index, SPIClass* spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen) :
+ RadioInterface(index),
+ _spiSettings(8E6, MSBFIRST, SPI_MODE0), _spiModem(spi), _ss(ss),
+ _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
- while (digitalRead(_busy) == HIGH)
- {
- if (millis() >= (time + 100)) {
- break;
++ _busy(busy), _rxen(rxen), _frequency(0), _bw(0x04),
++ _cr(0x01), _packetIndex(0), _implicitHeaderMode(0),
+ _payloadLength(255), _crcMode(1), _fifo_tx_addr_ptr(0),
+ _fifo_rx_addr_ptr(0), _preinit_done(false), _tcxo(tcxo),
+ _dio2_as_rf_switch(dio2_as_rf_switch)
+{
+ // overide Stream timeout value
+ setTimeout(0);
+ // TODO, figure out why this has to be done. Using the index to reference the
+ // interface_obj list causes a crash otherwise
+ //_index = getIndex();
+}
+
+bool sx126x::preInit() {
+ pinMode(_ss, OUTPUT);
+ digitalWrite(_ss, HIGH);
+
+ // todo: check if this change causes issues on any platforms
+ #if MCU_VARIANT == MCU_ESP32
+ if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+ _spiModem->begin(_sclk, _miso, _mosi, _ss);
+ } else {
+ _spiModem->begin();
+ }
+ #else
+ _spiModem->begin();
+ #endif
+
+ // check version (retry for up to 2 seconds)
+ // TODO: Actually read version registers, not syncwords
+ long start = millis();
+ uint8_t syncmsb;
+ uint8_t synclsb;
+ while (((millis() - start) < 2000) && (millis() >= start)) {
+ syncmsb = readRegister(REG_SYNC_WORD_MSB_6X);
+ synclsb = readRegister(REG_SYNC_WORD_LSB_6X);
+ if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
+ break;
+ }
+ delay(100);
+ }
+ if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
+ return false;
+ }
+
+ _preinit_done = true;
+ return true;
+}
+
+uint8_t ISR_VECT sx126x::readRegister(uint16_t address)
+{
+ return singleTransfer(OP_READ_REGISTER_6X, address, 0x00);
+}
+
+void sx126x::writeRegister(uint16_t address, uint8_t value)
+{
+ singleTransfer(OP_WRITE_REGISTER_6X, address, value);
+}
+
+uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
+{
+ waitOnBusy();
+
+ uint8_t response;
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+ _spiModem->transfer((address & 0xFF00) >> 8);
+ _spiModem->transfer(address & 0x00FF);
+ if (opcode == OP_READ_REGISTER_6X) {
+ _spiModem->transfer(0x00);
+ }
+ response = _spiModem->transfer(value);
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+
+ return response;
+}
+
+void sx126x::rxAntEnable()
+{
+ if (_rxen != -1) {
+ digitalWrite(_rxen, HIGH);
+ }
+}
+
+void sx126x::loraMode() {
+ // enable lora mode on the SX1262 chip
+ uint8_t mode = MODE_LONG_RANGE_MODE_6X;
+ executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
+}
+
+void sx126x::waitOnBusy() {
+ unsigned long time = millis();
- // do nothing
++ if (_busy != -1) {
++ while (digitalRead(_busy) == HIGH)
++ {
++ if (millis() >= (time + 100)) { break; }
+ }
- for (int i = 0; i < size; i++)
- {
- _spiModem->transfer(buffer[i]);
- _fifo_tx_addr_ptr++;
- }
+ }
+}
+
+void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+
+ for (int i = 0; i < size; i++)
+ {
+ _spiModem->transfer(buffer[i]);
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+ _spiModem->transfer(0x00);
+
+ for (int i = 0; i < size; i++)
+ {
+ buffer[i] = _spiModem->transfer(0x00);
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(OP_FIFO_WRITE_6X);
+ _spiModem->transfer(_fifo_tx_addr_ptr);
+
- for (int i = 0; i < size; i++)
- {
- buffer[i] = _spiModem->transfer(0x00);
- }
++ for (int i = 0; i < size; i++) {_spiModem->transfer(buffer[i]); _fifo_tx_addr_ptr++;}
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx126x::readBuffer(uint8_t* buffer, size_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(OP_FIFO_READ_6X);
+ _spiModem->transfer(_fifo_rx_addr_ptr);
+ _spiModem->transfer(0x00);
+
- // because there is no access to these registers on the sx1262, we have
++ for (int i = 0; i < size; i++) {buffer[i] = _spiModem->transfer(0x00);}
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
- // because there is no access to these registers on the sx1262, we have
++ // Because there is no access to these registers on the sx1262, we have
+ // to set all these parameters at once or not at all.
+ uint8_t buf[8];
+
+ buf[0] = sf;
+ buf[1] = bw;
+ buf[2] = cr;
+ // low data rate toggle
+ buf[3] = ldro;
+ // unused params in LoRa mode
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8);
+}
+
+void sx126x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
- if (frequency >= 430E6 && frequency <= 440E6) {
- image_freq[0] = 0x6B;
- image_freq[1] = 0x6F;
- }
- else if (frequency >= 470E6 && frequency <= 510E6) {
- image_freq[0] = 0x75;
- image_freq[1] = 0x81;
- }
- else if (frequency >= 779E6 && frequency <= 787E6) {
- image_freq[0] = 0xC1;
- image_freq[1] = 0xC5;
- }
- else if (frequency >= 863E6 && frequency <= 870E6) {
- image_freq[0] = 0xD7;
- image_freq[1] = 0xDB;
- }
- else if (frequency >= 902E6 && frequency <= 928E6) {
- image_freq[0] = 0xE1;
- image_freq[1] = 0xE9;
- }
-
++ // Because there is no access to these registers on the sx1262, we have
+ // to set all these parameters at once or not at all.
+ uint8_t buf[9];
+
+ buf[0] = uint8_t((preamble & 0xFF00) >> 8);
+ buf[1] = uint8_t((preamble & 0x00FF));
+ buf[2] = headermode;
+ buf[3] = length;
+ buf[4] = crc;
+ // standard IQ setting (no inversion)
+ buf[5] = 0x00;
+ // unused params
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+
+ executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
+}
+
+void sx126x::reset(void) {
+ if (_reset != -1) {
+ pinMode(_reset, OUTPUT);
+
+ // perform reset
+ digitalWrite(_reset, LOW);
+ delay(10);
+ digitalWrite(_reset, HIGH);
+ delay(10);
+ }
+}
+
+void sx126x::calibrate(void) {
+ // Put in STDBY_RC mode before calibration
+ uint8_t mode_byte = MODE_STDBY_RC_6X;
+ executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
+
+ // calibrate RC64k, RC13M, PLL, ADC and image
+ uint8_t calibrate = MASK_CALIBRATE_ALL;
+ executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
+
+ delay(5);
+ waitOnBusy();
+}
+
+void sx126x::calibrate_image(uint32_t frequency) {
+ uint8_t image_freq[2] = {0};
+
- if (_busy != -1) {
- pinMode(_busy, INPUT);
- }
++ if (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; }
++ else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; }
++ else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; }
++ else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; }
++ else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; }
+ executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
+ waitOnBusy();
+}
+
+int sx126x::begin()
+{
+ reset();
+
- if (_rxen != -1) {
- pinMode(_rxen, OUTPUT);
- }
++ if (_busy != -1) { pinMode(_busy, INPUT); }
+
+ if (!_preinit_done) {
+ if (!preInit()) {
+ return false;
+ }
+ }
+
- // wait for TX done
- while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) {
++ if (_rxen != -1) { pinMode(_rxen, OUTPUT); }
+
+ calibrate();
+ calibrate_image(_frequency);
+
+ enableTCXO();
+
+ loraMode();
+ standby();
+
+ // Set sync word
+ setSyncWord(SYNC_WORD_6X);
+
+ if (_dio2_as_rf_switch) {
+ // enable dio2 rf switch
+ uint8_t byte = 0x01;
+ executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
+ }
+
+ rxAntEnable();
+
+ setFrequency(_frequency);
+
+ setTxPower(_txp);
+ enableCrc();
+
+ // set LNA boost
+ writeRegister(REG_LNA_6X, 0x96);
+
+ // set base addresses
+ uint8_t basebuf[2] = {0};
+ executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
+
+ setModulationParams(_sf, _bw, _cr, _ldro);
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ _radio_online = true;
+ return 1;
+}
+
+void sx126x::end()
+{
+ // put in sleep mode
+ sleep();
+
+ // stop SPI
+ _spiModem->end();
+
+ _bitrate = 0;
+
+ _radio_online = false;
+
+ _preinit_done = false;
+}
+
+int sx126x::beginPacket(int implicitHeader)
+{
+ standby();
+
+ if (implicitHeader) {
+ implicitHeaderMode();
+ } else {
+ explicitHeaderMode();
+ }
+
+ _payloadLength = 0;
+ _fifo_tx_addr_ptr = 0;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ return 1;
+}
+
+int sx126x::endPacket()
+{
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ // put in single TX mode
+ uint8_t timeout[3] = {0};
+ executeOpcode(OP_TX_6X, timeout, 3);
+
+ uint8_t buf[2];
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+
+ executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+
- return 1;
++ // Wait for TX done
++ bool timed_out = false;
++ uint32_t w_timeout = millis()+(getAirtime(_payloadLength)* MODEM_TIMEOUT_MULT);
++ while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0)) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+ yield();
+ }
+
++ if (millis() > w_timeout) { timed_out = true; }
++
+ // clear IRQ's
+
+ uint8_t mask[2];
+ mask[0] = 0x00;
+ mask[1] = IRQ_TX_DONE_MASK_6X;
+ executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
- uint8_t sx126x::modemStatus() {
- // imitate the register status from the sx1276 / 78
- uint8_t buf[2] = {0};
++ return !timed_out;
+}
+
- executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
- uint8_t clearbuf[2] = {0};
- uint8_t byte = 0x00;
+
- if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) {
- byte = byte | 0x01 | 0x04;
- // clear register after reading
- clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;
- }
++bool sx126x::dcd() {
++ bool false_preamble_detected = false;
++ uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
++ uint32_t now = millis();
+
- if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) {
- byte = byte | 0x02 | 0x04;
- }
++ bool header_detected = false;
++ bool carrier_detected = false;
+
- executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
++ if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { header_detected = true; carrier_detected = true; }
++ else { header_detected = false; }
+
- return byte;
++ if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) {
++ carrier_detected = true;
++ if (_preamble_detected_at == 0) { _preamble_detected_at = now; }
++ if (now - _preamble_detected_at > _lora_preamble_time_ms + _lora_header_time_ms) {
++ _preamble_detected_at = 0;
++ if (!header_detected) { false_preamble_detected = true; }
++ uint8_t clearbuf[2] = {0};
++ clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;
++ executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
++ }
++ }
+
-
++ // TODO: Maybe there's a way of unlatching the RSSI
++ // status without re-activating receive mode?
++ if (false_preamble_detected) { receive(); false_preamble_detected = false; }
++ return carrier_detected;
+}
+
- if (!available()) {
- return -1;
- }
+uint8_t sx126x::currentRssiRaw() {
+ uint8_t byte = 0;
+ executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
+ return byte;
+}
+
+int ISR_VECT sx126x::currentRssi() {
+ uint8_t byte = 0;
+ executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
+ int rssi = -(int(byte)) / 2;
+ return rssi;
+}
+
+uint8_t sx126x::packetRssiRaw() {
+ uint8_t buf[3] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+ return buf[2];
+}
+
+int ISR_VECT sx126x::packetRssi(uint8_t pkt_snr_raw) {
+ // may need more calculations here
+ uint8_t buf[3] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+ int pkt_rssi = -buf[0] / 2;
+ return pkt_rssi;
+}
+
+uint8_t ISR_VECT sx126x::packetSnrRaw() {
+ uint8_t buf[3] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+ return buf[1];
+}
+
+float ISR_VECT sx126x::packetSnr() {
+ uint8_t buf[3] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+ return float(buf[1]) * 0.25;
+}
+
+long sx126x::packetFrequencyError()
+{
+ // todo: implement this, no idea how to check it on the sx1262
+ const float fError = 0.0;
+ return static_cast<long>(fError);
+}
+
+size_t sx126x::write(uint8_t byte)
+{
+ return write(&byte, sizeof(byte));
+}
+
+size_t sx126x::write(const uint8_t *buffer, size_t size)
+{
+ if ((_payloadLength + size) > MAX_PKT_LENGTH) {
+ size = MAX_PKT_LENGTH - _payloadLength;
+ }
+
+ // write data
+ writeBuffer(buffer, size);
+ _payloadLength = _payloadLength + size;
+ return size;
+}
+
+int ISR_VECT sx126x::available()
+{
+ uint8_t buf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2);
+ return buf[0] - _packetIndex;
+}
+
+int ISR_VECT sx126x::read()
+{
- writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
++ if (!available()) { return -1; }
+
+ // if received new packet
+ if (_packetIndex == 0) {
+ uint8_t rxbuf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+ int size = rxbuf[0];
+ _fifo_rx_addr_ptr = rxbuf[1];
+
+ readBuffer(_packet, size);
+ }
+
+ uint8_t byte = _packet[_packetIndex];
+ _packetIndex++;
+ return byte;
+}
+
+int sx126x::peek()
+{
+ if (!available()) {
+ return -1;
+ }
+
+ // if received new packet
+ if (_packetIndex == 0) {
+ uint8_t rxbuf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+ int size = rxbuf[0];
+ _fifo_rx_addr_ptr = rxbuf[1];
+
+ readBuffer(_packet, size);
+ }
+
+ uint8_t b = _packet[_packetIndex];
+ return b;
+}
+
+void sx126x::flush()
+{
+}
+
+void sx126x::onReceive(void(*callback)(uint8_t, int))
+{
+ _onReceive = callback;
+
+ if (callback) {
+ pinMode(_dio0, INPUT);
+
+ // set preamble and header detection irqs, plus dio0 mask
+ uint8_t buf[8];
+
+ // set irq masks, enable all
+ buf[0] = 0xFF;
+ buf[1] = 0xFF;
+
+ // set dio0 masks
+ buf[2] = 0x00;
+ buf[3] = IRQ_RX_DONE_MASK_6X;
+
+ // set dio1 masks
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+
+ // set dio2 masks
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+ // make function available
+ extern void onDio0Rise();
+
+ attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+ } else {
+ detachInterrupt(digitalPinToInterrupt(_dio0));
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+ }
+}
+
+void sx126x::receive(int size)
+{
+ if (size > 0) {
+ implicitHeaderMode();
+
+ // tell radio payload length
+ _payloadLength = size;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+ } else {
+ explicitHeaderMode();
+ }
+
+ if (_rxen != -1) {
+ rxAntEnable();
+ }
+
+ uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
+ executeOpcode(OP_RX_6X, mode, 3);
+}
+
+void sx126x::standby()
+{
+ uint8_t byte;
+ if (_tcxo) {
+ // STDBY_XOSC
+ byte = MODE_STDBY_XOSC_6X;
+ } else {
+ // STDBY_RC
+ byte = MODE_STDBY_RC_6X;
+ }
+ executeOpcode(OP_STANDBY_6X, &byte, 1);
+}
+
+void sx126x::sleep()
+{
+ uint8_t byte = 0x00;
+ executeOpcode(OP_SLEEP_6X, &byte, 1);
+}
+
+void sx126x::enableTCXO() {
+ if (_tcxo) {
+ #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL || 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};
+ #elif BOARD_MODEL == BOARD_TECHO
+ uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+ #elif BOARD_MODEL == BOARD_T3S3
+ uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+ #elif BOARD_MODEL == BOARD_TDECK
+ uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+ #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+ uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
++ #elif BOARD_MODEL == BOARD_HELTEC_T114
++ uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+ #else
+ uint8_t buf[4] = {0};
+ #endif
+ executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
+ }
+}
+
+// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
+void sx126x::disableTCXO() { }
+
+void sx126x::setTxPower(int level, int outputPin) {
+ // currently no low power mode for SX1262 implemented, assuming PA boost
+
+ // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
+ // RegTxClampConfig = @address 0x08D8
+ writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
+
+ uint8_t pa_buf[4];
+
+ pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
+ pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
+ pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
+ pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
+
+ executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
+
+ if (level > 22) { level = 22; }
+ else if (level < -9) { level = -9; }
+
+ _txp = level;
+
- // TODO: Fix
++ writeRegister(REG_OCP_6X, OCP_TUNED); // 160mA limit, overcurrent protection
+
+ uint8_t tx_buf[2];
+
+ tx_buf[0] = level;
+ tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
+
+ executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
+}
+
+int8_t sx126x::getTxPower() {
+ return _txp;
+}
+
+void sx126x::setFrequency(uint32_t frequency) {
+ _frequency = frequency;
+
+ uint8_t buf[4];
+
+ uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
+
+ buf[0] = ((freq >> 24) & 0xFF);
+ buf[1] = ((freq >> 16) & 0xFF);
+ buf[2] = ((freq >> 8) & 0xFF);
+ buf[3] = (freq & 0xFF);
+
+ executeOpcode(OP_RF_FREQ_6X, buf, 4);
+}
+
+uint32_t sx126x::getFrequency() {
+ // we can't read the frequency on the sx1262 / 80
+ uint32_t frequency = _frequency;
+
+ return frequency;
+}
+
+void sx126x::setSpreadingFactor(int sf)
+{
+ if (sf < 5) {
+ sf = 5;
+ } else if (sf > 12) {
+ sf = 12;
+ }
+
+ _sf = sf;
+
+ handleLowDataRate();
+ setModulationParams(sf, _bw, _cr, _ldro);
+}
+
+uint8_t sx126x::getSpreadingFactor()
+{
+ return _sf;
+}
+
+uint32_t sx126x::getSignalBandwidth()
+{
+ int bw = _bw;
+ switch (bw) {
+ case 0x00: return 7.8E3;
+ case 0x01: return 15.6E3;
+ case 0x02: return 31.25E3;
+ case 0x03: return 62.5E3;
+ case 0x04: return 125E3;
+ case 0x05: return 250E3;
+ case 0x06: return 500E3;
+ case 0x08: return 10.4E3;
+ case 0x09: return 20.8E3;
+ case 0x0A: return 41.7E3;
+ }
+ return 0;
+}
+
+void sx126x::handleLowDataRate(){
+ if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) {
+ _ldro = 0x01;
+ } else {
+ _ldro = 0x00;
+ }
+}
+
+void sx126x::optimizeModemSensitivity(){
+ // todo: check if there's anything the sx1262 can do here
+}
+
+void sx126x::setSignalBandwidth(uint32_t sbw)
+{
+ if (sbw <= 7.8E3) {
+ _bw = 0x00;
+ } else if (sbw <= 10.4E3) {
+ _bw = 0x08;
+ } else if (sbw <= 15.6E3) {
+ _bw = 0x01;
+ } else if (sbw <= 20.8E3) {
+ _bw = 0x09;
+ } else if (sbw <= 31.25E3) {
+ _bw = 0x02;
+ } else if (sbw <= 41.7E3) {
+ _bw = 0x0A;
+ } else if (sbw <= 62.5E3) {
+ _bw = 0x03;
+ } else if (sbw <= 125E3) {
+ _bw = 0x04;
+ } else if (sbw <= 250E3) {
+ _bw = 0x05;
+ } else /*if (sbw <= 250E3)*/ {
+ _bw = 0x06;
+ }
+
+ handleLowDataRate();
+ setModulationParams(_sf, _bw, _cr, _ldro);
+
+ optimizeModemSensitivity();
+}
+
+void sx126x::setCodingRate4(int denominator)
+{
+ if (denominator < 5) {
+ denominator = 5;
+ } else if (denominator > 8) {
+ denominator = 8;
+ }
+
+ int cr = denominator - 4;
+
+ _cr = cr;
+
+ setModulationParams(_sf, _bw, cr, _ldro);
+}
+
+uint8_t sx126x::getCodingRate4()
+{
+ return _cr + 4;
+}
+
+void sx126x::setPreambleLength(long length)
+{
+ _preambleLength = length;
+ setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::setSyncWord(uint16_t sw)
+{
- byte sx126x::random()
++ // TODO: Why was this hardcoded instead of using the config value?
+ // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
+ // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
+ writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
+ writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
+}
+
+void sx126x::enableCrc()
+{
+ _crcMode = 1;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::disableCrc()
+{
+ _crcMode = 0;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
- void sx126x::updateBitrate() {
- if (_radio_online) {
- _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
- _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
- _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
- _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
- _csma_slot_ms = _lora_symbol_time_ms*12;
- if (_csma_slot_ms > CSMA_SLOT_MAX_MS) { _csma_slot_ms = CSMA_SLOT_MAX_MS; }
- if (_csma_slot_ms < CSMA_SLOT_MIN_MS) { _csma_slot_ms = CSMA_SLOT_MIN_MS; }
- float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
- if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
- target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
- } else {
- target_preamble_symbols = ceil(target_preamble_symbols);
- }
- _preambleLength = (long)target_preamble_symbols;
- setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
- } else {
- _bitrate = 0;
- }
- }
-
++uint8_t sx126x::random()
+{
+ return readRegister(REG_RANDOM_GEN_6X);
+}
+
+void sx126x::setSPIFrequency(uint32_t frequency)
+{
+ _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
+}
+
+void sx126x::dumpRegisters(Stream& out)
+{
+ for (int i = 0; i < 128; i++) {
+ out.print("0x");
+ out.print(i, HEX);
+ out.print(": 0x");
+ out.println(readRegister(i), HEX);
+ }
+}
+
+void sx126x::explicitHeaderMode()
+{
+ _implicitHeaderMode = 0;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::implicitHeaderMode()
+{
+ _implicitHeaderMode = 1;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+
+void sx126x::handleDio0Rise()
+{
+ // received a packet
+ _packetIndex = 0;
+
+ // read packet length
+ uint8_t rxbuf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+ int packetLength = rxbuf[0];
+
+ if (_onReceive) {
+ _onReceive(_index, packetLength);
+ }
+}
+
- {
- setTimeout(0);
- // TODO, figure out why this has to be done. Using the index to reference the
- // interface_obj list causes a crash otherwise
- //_index = getIndex();
- }
+bool ISR_VECT sx126x::getPacketValidity() {
+ uint8_t buf[2];
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+
+ executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+
+ executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2);
+
+ if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// SX127x registers
+#define REG_FIFO_7X 0x00
+#define REG_OP_MODE_7X 0x01
+#define REG_FRF_MSB_7X 0x06
+#define REG_FRF_MID_7X 0x07
+#define REG_FRF_LSB_7X 0x08
+#define REG_PA_CONFIG_7X 0x09
+#define REG_OCP_7X 0x0b
+#define REG_LNA_7X 0x0c
+#define REG_FIFO_ADDR_PTR_7X 0x0d
+#define REG_FIFO_TX_BASE_ADDR_7X 0x0e
+#define REG_FIFO_RX_BASE_ADDR_7X 0x0f
+#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10
+#define REG_IRQ_FLAGS_7X 0x12
+#define REG_RX_NB_BYTES_7X 0x13
+#define REG_MODEM_STAT_7X 0x18
+#define REG_PKT_SNR_VALUE_7X 0x19
+#define REG_PKT_RSSI_VALUE_7X 0x1a
+#define REG_RSSI_VALUE_7X 0x1b
+#define REG_MODEM_CONFIG_1_7X 0x1d
+#define REG_MODEM_CONFIG_2_7X 0x1e
+#define REG_PREAMBLE_MSB_7X 0x20
+#define REG_PREAMBLE_LSB_7X 0x21
+#define REG_PAYLOAD_LENGTH_7X 0x22
+#define REG_MODEM_CONFIG_3_7X 0x26
+#define REG_FREQ_ERROR_MSB_7X 0x28
+#define REG_FREQ_ERROR_MID_7X 0x29
+#define REG_FREQ_ERROR_LSB_7X 0x2a
+#define REG_RSSI_WIDEBAND_7X 0x2c
+#define REG_DETECTION_OPTIMIZE_7X 0x31
+#define REG_HIGH_BW_OPTIMIZE_1_7X 0x36
+#define REG_DETECTION_THRESHOLD_7X 0x37
+#define REG_SYNC_WORD_7X 0x39
+#define REG_HIGH_BW_OPTIMIZE_2_7X 0x3a
+#define REG_DIO_MAPPING_1_7X 0x40
+#define REG_VERSION_7X 0x42
+#define REG_TCXO_7X 0x4b
+#define REG_PA_DAC_7X 0x4d
+
+// Modes
+#define MODE_LONG_RANGE_MODE_7X 0x80
+#define MODE_SLEEP_7X 0x00
+#define MODE_STDBY_7X 0x01
+#define MODE_TX_7X 0x03
+#define MODE_RX_CONTINUOUS_7X 0x05
+#define MODE_RX_SINGLE_7X 0x06
+
+// PA config
+#define PA_BOOST_7X 0x80
+
+// IRQ masks
+#define IRQ_TX_DONE_MASK_7X 0x08
+#define IRQ_RX_DONE_MASK_7X 0x40
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
+
+#define SYNC_WORD_7X 0x12
+
+sx127x::sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy) :
+ RadioInterface(index),
+ _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+ _spiModem(spi),
+ _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
+ _busy(busy), _frequency(0), _packetIndex(0), _preinit_done(false), _bw(0)
- uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); }
++{ setTimeout(0); }
+
+void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
+uint8_t ISR_VECT sx127x::readRegister(uint8_t address) { return singleTransfer(address & 0x7f, 0x00); }
+void sx127x::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); }
+void sx127x::standby() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); }
+void sx127x::sleep() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); }
- byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
+void sx127x::setSyncWord(uint8_t sw) { writeRegister(REG_SYNC_WORD_7X, sw); }
+void sx127x::enableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); }
+void sx127x::disableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); }
+void sx127x::enableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); }
+void sx127x::disableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); }
+void sx127x::explicitHeaderMode() { _implicitHeaderMode = 0; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); }
+void sx127x::implicitHeaderMode() { _implicitHeaderMode = 1; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); }
- // set base addresses
++uint8_t sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
+void sx127x::flush() { }
+
+bool sx127x::preInit() {
+ pinMode(_ss, OUTPUT);
+ digitalWrite(_ss, HIGH);
+ // todo: check if this change causes issues on any platforms
+ #if MCU_VARIANT == MCU_ESP32
+ if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+ _spiModem->begin(_sclk, _miso, _mosi, _ss);
+ } else {
+ _spiModem->begin();
+ }
+ #else
+ _spiModem->begin();
+ #endif
+
+ // Check modem version
+ uint8_t version;
+ long start = millis();
+ while (((millis() - start) < 500) && (millis() >= start)) {
+ version = readRegister(REG_VERSION_7X);
+ if (version == 0x12) { break; }
+ delay(100);
+ }
+
+ if (version != 0x12) { return false; }
+
+ _preinit_done = true;
+ return true;
+}
+
+uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) {
+ uint8_t response;
+
+ digitalWrite(_ss, LOW);
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(address);
+ response = _spiModem->transfer(value);
+ _spiModem->endTransaction();
+ digitalWrite(_ss, HIGH);
+
+ return response;
+}
+
+int sx127x::begin() {
+ if (_reset != -1) {
+ pinMode(_reset, OUTPUT);
+
+ // Perform reset
+ digitalWrite(_reset, LOW);
+ delay(10);
+ digitalWrite(_reset, HIGH);
+ delay(10);
+ }
+
+ sleep();
+
+ if (_busy != -1) { pinMode(_busy, INPUT); }
+
+ if (!_preinit_done) {
+ if (!preInit()) { return false; }
+ }
+
+ setFrequency(_frequency);
+ setSignalBandwidth(_bw);
+ setSpreadingFactor(_sf);
+ setCodingRate4(_cr);
+ setTxPower(_txp);
+
- // set LNA boost and auto AGC
++ // Set base addresses
+ writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
+ writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
+
- int pkt_snr;
- if (pkt_snr_raw == 0xFF) {
- pkt_snr = packetSnr();
- } else {
- pkt_snr = ((int8_t)pkt_snr_raw)*0.25;
- }
++ // Set LNA boost and auto AGC
+ writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
+ writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
+
+ setSyncWord(SYNC_WORD_7X);
+ enableCrc();
+
+ standby();
+
+ _radio_online = true;
+
+ return 1;
+}
+
+void sx127x::end() {
+ sleep();
+ _bitrate = 0;
+ _radio_online = false;
+}
+
+int sx127x::beginPacket(int implicitHeader) {
+ standby();
+
+ if (implicitHeader) {
+ implicitHeaderMode();
+ } else {
+ explicitHeaderMode();
+ }
+
+ // Reset FIFO address and payload length
+ writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
+ writeRegister(REG_PAYLOAD_LENGTH_7X, 0);
+
+ return 1;
+}
+
+int sx127x::endPacket() {
+ // Enter TX mode
+ writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
+
+ // Wait for TX completion
+ while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
+ yield();
+ }
+
+ // Clear TX complete IRQ
+ writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
+ return 1;
+}
+
++
++bool sx127x::dcd() {
++ bool carrier_detected = false;
++ uint8_t status = readRegister(REG_MODEM_STAT_7X);
++ if ((status & SIG_DETECT) == SIG_DETECT) { carrier_detected = true; }
++ if ((status & SIG_SYNCED) == SIG_SYNCED) { carrier_detected = true; }
++ return carrier_detected;
++}
++
+uint8_t sx127x::currentRssiRaw() {
+ uint8_t rssi = readRegister(REG_RSSI_VALUE_7X);
+ return rssi;
+}
+
+int ISR_VECT sx127x::currentRssi() {
+ int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET;
+ if (_frequency < 820E6) rssi -= 7;
+ return rssi;
+}
+
+uint8_t sx127x::packetRssiRaw() {
+ uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X);
+ return pkt_rssi_value;
+}
+
+int ISR_VECT sx127x::packetRssi(uint8_t pkt_snr_raw) {
++ int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
++ int pkt_snr = ((int8_t)pkt_snr_raw)*0.25;
++
++ if (_frequency < 820E6) pkt_rssi -= 7;
++
++ if (pkt_snr < 0) {
++ pkt_rssi += pkt_snr;
++ } else {
++ // Slope correction is (16/15)*pkt_rssi,
++ // this estimation looses one floating point
++ // operation, and should be precise enough.
++ pkt_rssi = (int)(1.066 * pkt_rssi);
++ }
++ return pkt_rssi;
++}
++
++int ISR_VECT sx127x::packetRssi() {
+ int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
- if (pkt_snr < 0) {
- pkt_rssi += pkt_snr;
- } else {
++ int pkt_snr = packetSnr();
++
+ if (_frequency < 820E6) pkt_rssi -= 7;
- uint8_t ISR_VECT sx127x::packetSnrRaw() {
- return readRegister(REG_PKT_SNR_VALUE_7X);
- }
++
++ if (pkt_snr < 0) { pkt_rssi += pkt_snr; }
++ else {
+ // Slope correction is (16/15)*pkt_rssi,
+ // this estimation looses one floating point
+ // operation, and should be precise enough.
+ pkt_rssi = (int)(1.066 * pkt_rssi);
+ }
+ return pkt_rssi;
+}
+
+
- float ISR_VECT sx127x::packetSnr() {
- return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25;
- }
++uint8_t ISR_VECT sx127x::packetSnrRaw() { return readRegister(REG_PKT_SNR_VALUE_7X); }
+
- if ((currentLength + size) > MAX_PKT_LENGTH) {
- size = MAX_PKT_LENGTH - currentLength;
- }
++float ISR_VECT sx127x::packetSnr() { return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; }
+
+long sx127x::packetFrequencyError() {
+ int32_t freqError = 0;
+ freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
+ freqError <<= 8L;
+ freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X));
+ freqError <<= 8L;
+ freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X));
+
+ if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
+ freqError -= 524288; // B1000'0000'0000'0000'0000
+ }
+
+ const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
+ const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f);
+
+ return static_cast<long>(fError);
+}
+
+size_t sx127x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
+
+size_t sx127x::write(const uint8_t *buffer, size_t size) {
+ int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X);
- for (size_t i = 0; i < size; i++) {
- writeRegister(REG_FIFO_7X, buffer[i]);
- }
++ if ((currentLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - currentLength; }
+
- _packetIndex = 0;
- int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
- writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
- if (_onReceive) {
- _onReceive(_index, packetLength);
- }
- writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
- }
-
- void sx127x::updateBitrate() {
- if (_radio_online) {
- _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
- _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
- _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
- _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
- _csma_slot_ms = _lora_symbol_time_ms*12;
- if (_csma_slot_ms > CSMA_SLOT_MAX_MS) { _csma_slot_ms = CSMA_SLOT_MAX_MS; }
- if (_csma_slot_ms < CSMA_SLOT_MIN_MS) { _csma_slot_ms = CSMA_SLOT_MIN_MS; }
- float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
- if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
- target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
- } else {
- target_preamble_symbols = ceil(target_preamble_symbols);
- }
- _preambleLength = (long)target_preamble_symbols;
- } else {
- _bitrate = 0;
++ for (size_t i = 0; i < size; i++) { writeRegister(REG_FIFO_7X, buffer[i]); }
+
+ writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size);
+ return size;
+}
+
+int ISR_VECT sx127x::available() { return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); }
+
+int ISR_VECT sx127x::read() {
+ if (!available()) { return -1; }
+ _packetIndex++;
+ return readRegister(REG_FIFO_7X);
+}
+
+int sx127x::peek() {
+ if (!available()) { return -1; }
+
+ // Remember current FIFO address, read, and then reset address
+ int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
+ uint8_t b = readRegister(REG_FIFO_7X);
+ writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
+
+ return b;
+}
+
+void sx127x::onReceive(void(*callback)(uint8_t, int)) {
+ _onReceive = callback;
+
+ if (callback) {
+ pinMode(_dio0, INPUT);
+ writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
+
+ #ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
+ #endif
+
+ // make function available
+ extern void onDio0Rise();
+
+ attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+
+ } else {
+ detachInterrupt(digitalPinToInterrupt(_dio0));
+
+ #ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
+ #endif
+ }
+}
+
+void sx127x::receive(int size) {
+ if (size > 0) {
+ implicitHeaderMode();
+ writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff);
+ } else { explicitHeaderMode(); }
+
+ writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X);
+}
+
+void sx127x::setTxPower(int level, int outputPin) {
+ // Setup according to RFO or PA_BOOST output pin
+ if (PA_OUTPUT_RFO_PIN == outputPin) {
+ if (level < 0) { level = 0; }
+ else if (level > 14) { level = 14; }
+
+ writeRegister(REG_PA_DAC_7X, 0x84);
+ writeRegister(REG_PA_CONFIG_7X, 0x70 | level);
+ _txp = level;
+ } else {
+ if (level < 2) { level = 2; }
+ else if (level > 17) { level = 17; }
+
+ writeRegister(REG_PA_DAC_7X, 0x84);
+ writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2));
+ _txp = level;
+ }
+}
+
+int8_t sx127x::getTxPower() { return readRegister(REG_PA_CONFIG_7X) - 126; }
+
+void sx127x::setFrequency(uint32_t frequency) {
+ _frequency = frequency;
+
+ uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
+
+ writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16));
+ writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8));
+ writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0));
+
+ optimizeModemSensitivity();
+}
+
+uint32_t sx127x::getFrequency() {
+ return _frequency;
+}
+
+void sx127x::setSpreadingFactor(int sf) {
+ if (sf < 6) { sf = 6; }
+ else if (sf > 12) { sf = 12; }
+
+ _sf = sf;
+
+ if (sf == 6) {
+ writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5);
+ writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c);
+ } else {
+ writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3);
+ writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a);
+ }
+
+
+ writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
+ handleLowDataRate();
+}
+
+uint8_t sx127x::getSpreadingFactor()
+{
+ return _sf;
+}
+
+uint32_t sx127x::getSignalBandwidth() {
+ return _bw;
+}
+
+void sx127x::setSignalBandwidth(uint32_t sbw) {
+ int bw;
+ if (sbw <= 7.8E3) {
+ bw = 0;
+ } else if (sbw <= 10.4E3) {
+ bw = 1;
+ } else if (sbw <= 15.6E3) {
+ bw = 2;
+ } else if (sbw <= 20.8E3) {
+ bw = 3;
+ } else if (sbw <= 31.25E3) {
+ bw = 4;
+ } else if (sbw <= 41.7E3) {
+ bw = 5;
+ } else if (sbw <= 62.5E3) {
+ bw = 6;
+ } else if (sbw <= 125E3) {
+ bw = 7;
+ } else if (sbw <= 250E3) {
+ bw = 8;
+ } else /*if (sbw <= 250E3)*/ {
+ bw = 9;
+ }
+
+ writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4));
+ _bw = sbw;
+ handleLowDataRate();
+ optimizeModemSensitivity();
+}
+
+void sx127x::setCodingRate4(int denominator) {
+ if (denominator < 5) { denominator = 5; }
+ else if (denominator > 8) { denominator = 8; }
+ int cr = denominator - 4;
+ _cr = cr;
+ writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
+}
+
+uint8_t sx127x::getCodingRate4()
+{
+ return _cr + 4;
+}
+
+void sx127x::setPreambleLength(long length) {
+ _preambleLength = length;
+ writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8));
+ writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0));
+}
+
+void sx127x::handleLowDataRate() {
+ int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4);
+ if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
+ // Set auto AGC and LowDataRateOptimize
+ writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2));
++ _ldro = true;
+ } else {
+ // Only set auto AGC
+ writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2));
++ _ldro = false;
+ }
+}
+
+void sx127x::optimizeModemSensitivity() {
+ byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
+ uint32_t freq = getFrequency();
+
+ if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
+ writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
+ writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f);
+ } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
+ writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
+ writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64);
+ } else {
+ writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03);
+ }
+}
+
+void sx127x::handleDio0Rise() {
- _busy(busy), _rxen(rxen), _txen(txen), _frequency(0), _sf(0x05),
++ int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
++
++ // Clear IRQs
++ writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
++ if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
++ _packetIndex = 0;
++ int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
++ writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
++ if (_onReceive) { _onReceive(_index, packetLength); }
++ writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
+ }
+}
+
+bool ISR_VECT sx127x::getPacketValidity() {
+ int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
+
+ // Clear IRQs
+ writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
+
+ if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// SX128x registers
+#define OP_RF_FREQ_8X 0x86
+#define OP_SLEEP_8X 0x84
+#define OP_STANDBY_8X 0x80
+#define OP_TX_8X 0x83
+#define OP_RX_8X 0x82
+#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as
+ // preamble detection, etc for
+ // knowing when it's safe to switch
+ // antenna modes
+#define OP_CLEAR_IRQ_STATUS_8X 0x97
+#define OP_GET_IRQ_STATUS_8X 0x15
+#define OP_RX_BUFFER_STATUS_8X 0x17
+#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet
+#define OP_CURRENT_RSSI_8X 0x1F
+#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc.
+#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc.
+#define OP_STATUS_8X 0xC0
+#define OP_TX_PARAMS_8X 0x8E // set dbm, etc
+#define OP_PACKET_TYPE_8X 0x8A
+#define OP_BUFFER_BASE_ADDR_8X 0x8F
+#define OP_READ_REGISTER_8X 0x19
+#define OP_WRITE_REGISTER_8X 0x18
+#define IRQ_TX_DONE_MASK_8X 0x01
+#define IRQ_RX_DONE_MASK_8X 0x02
+#define IRQ_HEADER_DET_MASK_8X 0x10
+#define IRQ_HEADER_ERROR_MASK_8X 0x20
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40
+
+#define MODE_LONG_RANGE_MODE_8X 0x01
+
+#define OP_FIFO_WRITE_8X 0x1A
+#define OP_FIFO_READ_8X 0x1B
+#define IRQ_PREAMBLE_DET_MASK_8X 0x80
+
+#define REG_PACKET_SIZE 0x901
+#define REG_FIRM_VER_MSB 0x154
+#define REG_FIRM_VER_LSB 0x153
+
+#define XTAL_FREQ_8X (double)52000000
+#define FREQ_DIV_8X (double)pow(2.0, 18.0)
+#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X)
+
+sx128x::sx128x(uint8_t index, SPIClass* spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen) :
+ RadioInterface(index),
+ _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+ _spiModem(spi),
+ _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
- _tcxo(tcxo)
++ _busy(busy), _rxen(rxen), _txen(txen), _frequency(0),
+ _bw(0x34), _cr(0x01), _packetIndex(0), _implicitHeaderMode(0),
+ _payloadLength(255), _crcMode(0), _fifo_tx_addr_ptr(0), _fifo_rx_addr_ptr(0),
+ _rxPacketLength(0), _preinit_done(false),
- // check version (retry for up to 2 seconds)
++ _tcxo(tcxo), _preamble_e(1), _preamble_m(1), _last_preamble(0)
+{
+ // overide Stream timeout value
+ setTimeout(0);
+ // TODO, figure out why this has to be done. Using the index to reference the
+ // interface_obj list causes a crash otherwise
+ //_index = getIndex();
+}
+
+bool sx128x::preInit() {
+ // setup pins
+ pinMode(_ss, OUTPUT);
+ // set SS high
+ digitalWrite(_ss, HIGH);
+
+ // todo: check if this change causes issues on any platforms
+ #if MCU_VARIANT == MCU_ESP32
+ if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+ _spiModem->begin(_sclk, _miso, _mosi, _ss);
+ } else {
+ _spiModem->begin();
+ }
+ #else
+ _spiModem->begin();
+ #endif
+
- while (((millis() - start) < 2000) && (millis() >= start)) {
++ // check version (retry for up to 500 ms)
+ long start = millis();
+
+ uint8_t version_msb;
+ uint8_t version_lsb;
+
- if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) {
- break;
- }
++ while (((millis() - start) < 500) && (millis() >= start)) {
+
+ version_msb = readRegister(REG_FIRM_VER_MSB);
+ version_lsb = readRegister(REG_FIRM_VER_LSB);
+
- if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) {
- return false;
- }
++ if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { break; }
+ delay(100);
+ }
- if (opcode == OP_READ_REGISTER_8X) {
- _spiModem->transfer(0x00);
- }
++ if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { return false; }
+
+ _preinit_done = true;
+ return true;
+}
+
+uint8_t ISR_VECT sx128x::readRegister(uint16_t address)
+{
+ return singleTransfer(OP_READ_REGISTER_8X, address, 0x00);
+}
+
+void sx128x::writeRegister(uint16_t address, uint8_t value)
+{
+ singleTransfer(OP_WRITE_REGISTER_8X, address, value);
+}
+
+uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
+{
+ waitOnBusy();
+
+ uint8_t response;
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+ _spiModem->transfer((address & 0xFF00) >> 8);
+ _spiModem->transfer(address & 0x00FF);
- if (_txen != -1) {
- digitalWrite(_txen, LOW);
- }
- if (_rxen != -1) {
- digitalWrite(_rxen, HIGH);
- }
++ if (opcode == OP_READ_REGISTER_8X) { _spiModem->transfer(0x00); }
+ response = _spiModem->transfer(value);
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+
+ return response;
+}
+
+void sx128x::rxAntEnable()
+{
- if (_txen != -1) {
- digitalWrite(_txen, HIGH);
- }
- if (_rxen != -1) {
- digitalWrite(_rxen, LOW);
- }
++ if (_txen != -1) { digitalWrite(_txen, LOW); }
++ if (_rxen != -1) { digitalWrite(_rxen, HIGH); }
+}
+
+void sx128x::txAntEnable()
+{
- // enable lora mode on the SX1262 chip
++ if (_txen != -1) { digitalWrite(_txen, HIGH); }
++ if (_rxen != -1) { digitalWrite(_rxen, LOW); }
+}
+
+void sx128x::loraMode() {
- if (millis() >= (time + 100)) {
- break;
- }
- // do nothing
+ uint8_t mode = MODE_LONG_RANGE_MODE_8X;
+ executeOpcode(OP_PACKET_TYPE_8X, &mode, 1);
+}
+
+void sx128x::waitOnBusy() {
+ unsigned long time = millis();
+ while (digitalRead(_busy) == HIGH)
+ {
- if (sf <= 6) {
- writeRegister(0x925, 0x1E);
- } else if (sf <= 8) {
- writeRegister(0x925, 0x37);
- } else if (sf >= 9) {
- writeRegister(0x925, 0x32);
- }
++ if (millis() >= (time + 100)) { break; }
+ }
+}
+
+void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+
+ for (int i = 0; i < size; i++)
+ {
+ _spiModem->transfer(buffer[i]);
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(opcode);
+ _spiModem->transfer(0x00);
+
+ for (int i = 0; i < size; i++)
+ {
+ buffer[i] = _spiModem->transfer(0x00);
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(OP_FIFO_WRITE_8X);
+ _spiModem->transfer(_fifo_tx_addr_ptr);
+
+ for (int i = 0; i < size; i++)
+ {
+ _spiModem->transfer(buffer[i]);
+ _fifo_tx_addr_ptr++;
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx128x::readBuffer(uint8_t* buffer, size_t size)
+{
+ waitOnBusy();
+
+ digitalWrite(_ss, LOW);
+
+ _spiModem->beginTransaction(_spiSettings);
+ _spiModem->transfer(OP_FIFO_READ_8X);
+ _spiModem->transfer(_fifo_rx_addr_ptr);
+ _spiModem->transfer(0x00);
+
+ for (int i = 0; i < size; i++)
+ {
+ buffer[i] = _spiModem->transfer(0x00);
+ }
+
+ _spiModem->endTransaction();
+
+ digitalWrite(_ss, HIGH);
+}
+
+void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) {
+ // because there is no access to these registers on the sx1280, we have
+ // to set all these parameters at once or not at all.
+ uint8_t buf[3];
+
+ buf[0] = sf << 4;
+ buf[1] = bw;
+ buf[2] = cr;
+ executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3);
+
- void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
++ if (sf <= 6) { writeRegister(0x925, 0x1E); }
++ else if (sf <= 8) { writeRegister(0x925, 0x37); }
++ else if (sf >= 9) { writeRegister(0x925, 0x32); }
+ writeRegister(0x093C, 0x1);
+}
+
- // calculate exponent and mantissa values for modem
- uint8_t e = 1;
- uint8_t m = 1;
- uint32_t preamblelen;
-
- while (e <= 15) {
- while (m <= 15) {
- preamblelen = m * (pow(2,e));
- if (preamblelen >= preamble) break;
- m++;
++void sx128x::setPacketParams(uint32_t target_preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
+ // because there is no access to these registers on the sx1280, we have
+ // to set all these parameters at once or not at all.
+ uint8_t buf[7];
- if (preamblelen >= preamble) break;
- m = 0;
- e++;
++ uint32_t calc_preamble;
++
++ // Cap max preamble length
++ if (target_preamble >= 0xF000) target_preamble = 0xF000;
++
++ if (_last_preamble != target_preamble) {
++ _preamble_e = 1;
++ _preamble_m = 1;
++ // calculate exponent and mantissa values for modem
++ while (_preamble_e <= 15) {
++ while (_preamble_m <= 15) {
++ calc_preamble = _preamble_m * (pow(2,_preamble_e));
++ if (calc_preamble >= target_preamble - 4) break;
++ _preamble_m++;
++ }
++ if (calc_preamble >= target_preamble - 4) break;
++ _preamble_m = 1;
++ _preamble_e++;
+ }
- buf[0] = (e << 4) | m;
+ }
+
- if (implicitHeader) {
- implicitHeaderMode();
- } else {
- explicitHeaderMode();
- }
++ buf[0] = (_preamble_e << 4) | _preamble_m;
+ buf[1] = headermode;
+ buf[2] = length;
+ buf[3] = crc;
+ // standard IQ setting (no inversion)
+ buf[4] = 0x40;
+ // unused params
+ buf[5] = 0x00;
+ buf[6] = 0x00;
+
+ executeOpcode(OP_PACKET_PARAMS_8X, buf, 7);
++
++ _last_preamble = target_preamble;
+}
+
+int sx128x::begin()
+{
+ if (_reset != -1) {
+ pinMode(_reset, OUTPUT);
+
+ // perform reset
+ digitalWrite(_reset, LOW);
+ delay(10);
+ digitalWrite(_reset, HIGH);
+ delay(10);
+ }
+
+ if (_rxen != -1) {
+ pinMode(_rxen, OUTPUT);
+ }
+
+ if (_txen != -1) {
+ pinMode(_txen, OUTPUT);
+ }
+
+ if (_busy != -1) {
+ pinMode(_busy, INPUT);
+ }
+
+ if (!_preinit_done) {
+ if (!preInit()) {
+ return false;
+ }
+ }
+
+ standby();
+ loraMode();
+ rxAntEnable();
+
+ setFrequency(_frequency);
+
+ // set LNA boost
+ // todo: implement this
+ //writeRegister(REG_LNA, 0x96);
+
+ setModulationParams(_sf, _bw, _cr);
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ setTxPower(_txp);
+
+ // set base addresses
+ uint8_t basebuf[2] = {0};
+ executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
+
+ _radio_online = true;
+ return 1;
+}
+
+void sx128x::end()
+{
+ // put in sleep mode
+ sleep();
+
+ // stop SPI
+ _spiModem->end();
+
+ _bitrate = 0;
+
+ _radio_online = false;
+ _preinit_done = false;
+}
+
+int sx128x::beginPacket(int implicitHeader)
+{
+ // put in standby mode
+ standby();
+
- // wait for TX done
- while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) {
++ if (implicitHeader) { implicitHeaderMode(); }
++ else { explicitHeaderMode(); }
+
+ _payloadLength = 0;
+ _fifo_tx_addr_ptr = 0;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ return 1;
+}
+
+int sx128x::endPacket()
+{
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+ txAntEnable();
+
+ // put in single TX mode
+ uint8_t timeout[3] = {0};
+ executeOpcode(OP_TX_8X, timeout, 3);
+
+ uint8_t buf[2];
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+
+ executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+
- return 1;
++ // Wait for TX done
++ bool timed_out = false;
++ uint32_t w_timeout = millis()+(getAirtime(_payloadLength)* MODEM_TIMEOUT_MULT);
++ while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0)) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+ yield();
+ }
+
++ if (millis() > w_timeout) { timed_out = true; }
+
+ // clear IRQ's
+
+ uint8_t mask[2];
+ mask[0] = 0x00;
+ mask[1] = IRQ_TX_DONE_MASK_8X;
+ executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2);
- uint8_t sx128x::modemStatus() {
- // imitate the register status from the sx1276 / 78
- uint8_t buf[2] = {0};
-
- executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
++ return !timed_out;
+}
+
- uint8_t clearbuf[2] = {0};
++bool sx128x::dcd() {
++ bool false_preamble_detected = false;
++ uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
++ uint32_t now = millis();
+
- uint8_t byte = 0x00;
++ bool header_detected = false;
++ bool carrier_detected = false;
+
- byte = byte | 0x01 | 0x04;
- // clear register after reading
- clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X;
- }
-
- if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) {
- byte = byte | 0x02 | 0x04;
++ if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { header_detected = true; carrier_detected = true; }
++ else { header_detected = false; }
+
+ if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) {
- executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2);
-
- return byte;
++ carrier_detected = true;
++ if (_preamble_detected_at == 0) { _preamble_detected_at = now; }
++ if (now - _preamble_detected_at > _lora_preamble_time_ms + _lora_header_time_ms) {
++ _preamble_detected_at = 0;
++ if (!header_detected) { false_preamble_detected = true; }
++ uint8_t clearbuf[2] = {0}; clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X;
++ executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2);
++ }
+ }
+
-
++ // TODO: Maybe there's a way of unlatching the RSSI
++ // status without re-activating receive mode?
++ if (false_preamble_detected) { receive(); }
++ return carrier_detected;
+}
+
- if (size > 255) {
- size = 255;
- }
+uint8_t sx128x::currentRssiRaw() {
+ uint8_t byte = 0;
+ executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
+ return byte;
+}
+
+int ISR_VECT sx128x::currentRssi() {
+ uint8_t byte = 0;
+ executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
+ int rssi = -byte / 2;
+ return rssi;
+}
+
+uint8_t sx128x::packetRssiRaw() {
+ uint8_t buf[5] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+ return buf[0];
+}
+
+int ISR_VECT sx128x::packetRssi(uint8_t pkt_snr_raw) {
+ // may need more calculations here
+ uint8_t buf[5] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+ int pkt_rssi = -buf[0] / 2;
+ return pkt_rssi;
+}
+
+uint8_t ISR_VECT sx128x::packetSnrRaw() {
+ uint8_t buf[5] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+ return buf[1];
+}
+
+float ISR_VECT sx128x::packetSnr() {
+ uint8_t buf[5] = {0};
+ executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+ return float(buf[1]) * 0.25;
+}
+
+long sx128x::packetFrequencyError()
+{
+ int32_t freqError = 0;
+ // todo: implement this, page 120 of sx1280 datasheet
+ const float fError = 0.0;
+ return static_cast<long>(fError);
+}
+
+size_t sx128x::write(uint8_t byte)
+{
+ return write(&byte, sizeof(byte));
+}
+
+size_t sx128x::write(const uint8_t *buffer, size_t size)
+{
+ if ((_payloadLength + size) > MAX_PKT_LENGTH) {
+ size = MAX_PKT_LENGTH - _payloadLength;
+ }
+
+ // write data
+ writeBuffer(buffer, size);
+ _payloadLength = _payloadLength + size;
+ return size;
+}
+
+int ISR_VECT sx128x::available()
+{
+ return _rxPacketLength - _packetIndex;
+}
+
+int ISR_VECT sx128x::read()
+{
+ if (!available()) {
+ return -1;
+ }
+
+ // if received new packet
+ if (_packetIndex == 0) {
+ uint8_t rxbuf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
+ int size;
+ // If implicit header mode is enabled, read packet length as payload length instead.
+ // See SX1280 datasheet v3.2, page 92
+ if (_implicitHeaderMode == 0x80) {
+ size = _payloadLength;
+ } else {
+ size = rxbuf[0];
+ }
+ _fifo_rx_addr_ptr = rxbuf[1];
+
- if (!available()) {
- return -1;
- }
++ if (size > 255) { size = 255; }
+
+ readBuffer(_packet, size);
+ }
+
+ uint8_t byte = _packet[_packetIndex];
+ _packetIndex++;
+ return byte;
+}
+
+int sx128x::peek()
+{
- if (level > 27) {
- level = 27;
- } else if (level < 0) {
- level = 0;
- }
++ if (!available()) { return -1; }
+
+ uint8_t b = _packet[_packetIndex];
+ return b;
+}
+
+void sx128x::flush()
+{
+}
+
+void sx128x::onReceive(void(*callback)(uint8_t, int))
+{
+ _onReceive = callback;
+
+ if (callback) {
+ pinMode(_dio0, INPUT);
+
+ // set preamble and header detection irqs, plus dio0 mask
+ uint8_t buf[8];
+
+ // set irq masks, enable all
+ buf[0] = 0xFF;
+ buf[1] = 0xFF;
+
+ // On the SX1280, no RxDone IRQ is generated if a packet is received with
+ // an invalid header, but the modem will be taken out of single RX mode.
+ // This can cause the modem to not receive packets until it is reset
+ // again. This is documented as Errata 16.2 in the SX1280 datasheet v3.2
+ // (page 150) Below, the header error IRQ is mapped to dio0 so that the
+ // modem can be set into RX mode again on reception of a corrupted
+ // header.
+ // set dio0 masks
+ buf[2] = 0x00;
+ buf[3] = IRQ_RX_DONE_MASK_8X | IRQ_HEADER_ERROR_MASK_8X;
+
+ // set dio1 masks
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+
+ // set dio2 masks
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+
+ executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8);
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+
+ // make function available
+ extern void onDio0Rise();
+
+ attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+ } else {
+ detachInterrupt(digitalPinToInterrupt(_dio0));
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+ _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+ }
+}
+
+void sx128x::receive(int size)
+{
+ if (size > 0) {
+ implicitHeaderMode();
+
+ // tell radio payload length
+ //_rxPacketLength = size;
+ //_payloadLength = size;
+ //setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+ } else {
+ explicitHeaderMode();
+ }
+
+ rxAntEnable();
+
+ // On the SX1280, there is a bug which can cause the busy line
+ // to remain high if a high amount of packets are received when
+ // in continuous RX mode. This is documented as Errata 16.1 in
+ // the SX1280 datasheet v3.2 (page 149)
+ // Therefore, the modem is set to single RX mode below instead.
+ uint8_t mode[3] = {0}; // single RX mode
+ executeOpcode(OP_RX_8X, mode, 3);
+}
+
+void sx128x::standby()
+{
+ uint8_t byte;
+ if (_tcxo) {
+ // STDBY_XOSC
+ byte = 0x01;
+ } else {
+ // STDBY_RC
+ byte = 0x00;
+ }
+ executeOpcode(OP_STANDBY_8X, &byte, 1);
+}
+
+void sx128x::sleep()
+{
+ uint8_t byte = 0x00;
+ executeOpcode(OP_SLEEP_8X, &byte, 1);
+}
+
+void sx128x::enableTCXO() {
+ // todo: need to check how to implement on sx1280
+}
+
+void sx128x::disableTCXO() {
+ // todo: need to check how to implement on sx1280
+}
+
+void sx128x::setTxPower(int level, int outputPin) {
+ uint8_t tx_buf[2];
+ #if BOARD_VARIANT == MODEL_13 || BOARD_VARIANT == MODEL_21
+ // RAK4631 with WisBlock SX1280 module (LIBSYS002)
- if (level > 20) {
- level = 20;
- } else if (level < 0) {
- level = 0;
- }
++ if (level > 27) { level = 27; }
++ else if (level < 0) { level = 0; }
+
+ _txp = level;
+
+ int reg_value;
+
+ switch (level) {
+ case 0:
+ reg_value = -18;
+ break;
+ case 1:
+ reg_value = -16;
+ break;
+ case 2:
+ reg_value = -15;
+ break;
+ case 3:
+ reg_value = -14;
+ break;
+ case 4:
+ reg_value = -13;
+ break;
+ case 5:
+ reg_value = -12;
+ break;
+ case 6:
+ reg_value = -11;
+ break;
+ case 7:
+ reg_value = -9;
+ break;
+ case 8:
+ reg_value = -8;
+ break;
+ case 9:
+ reg_value = -7;
+ break;
+ case 10:
+ reg_value = -6;
+ break;
+ case 11:
+ reg_value = -5;
+ break;
+ case 12:
+ reg_value = -4;
+ break;
+ case 13:
+ reg_value = -3;
+ break;
+ case 14:
+ reg_value = -2;
+ break;
+ case 15:
+ reg_value = -1;
+ break;
+ case 16:
+ reg_value = 0;
+ break;
+ case 17:
+ reg_value = 1;
+ break;
+ case 18:
+ reg_value = 2;
+ break;
+ case 19:
+ reg_value = 3;
+ break;
+ case 20:
+ reg_value = 4;
+ break;
+ case 21:
+ reg_value = 5;
+ break;
+ case 22:
+ reg_value = 6;
+ break;
+ case 23:
+ reg_value = 7;
+ break;
+ case 24:
+ reg_value = 8;
+ break;
+ case 25:
+ reg_value = 9;
+ break;
+ case 26:
+ reg_value = 12;
+ break;
+ case 27:
+ reg_value = 13;
+ break;
+ default:
+ reg_value = 0;
+ break;
+ }
+
+ tx_buf[0] = reg_value + 18;
+ tx_buf[1] = 0xE0; // ramping time - 20 microseconds
+
+ executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
+
+ #elif BOARD_VARIANT == MODEL_AC
+ // T3S3 SX1280 PA
- /*case 0:
++ if (level > 20) { level = 20; }
++ else if (level < 0) { level = 0; }
+
+ _txp = level;
+
+ int reg_value;
+
+ switch (level) {
- break;*/
++ case 0:
+ reg_value = -18;
+ break;
+ case 1:
+ reg_value = -17;
+ break;
+ case 2:
+ reg_value = -16;
+ break;
+ case 3:
+ reg_value = -15;
+ break;
+ case 4:
+ reg_value = -14;
+ break;
+ case 5:
+ reg_value = -13;
+ break;
+ case 6:
+ reg_value = -12;
+ break;
+ case 7:
+ reg_value = -10;
+ break;
+ case 8:
+ reg_value = -9;
+ break;
+ case 9:
+ reg_value = -8;
+ break;
+ case 10:
+ reg_value = -7;
+ break;
+ case 11:
+ reg_value = -6;
+ break;
+ case 12:
+ reg_value = -5;
+ break;
+ case 13:
+ reg_value = -4;
+ break;
+ case 14:
+ reg_value = -3;
+ break;
+ case 15:
+ reg_value = -2;
+ break;
+ case 16:
+ reg_value = -1;
+ break;
+ case 17:
+ reg_value = 0;
+ break;
+ case 18:
+ reg_value = 1;
+ break;
+ case 19:
+ reg_value = 2;
- // todo: do i need this??
++ break;
+ case 20:
+ reg_value = 3;
+ break;
+ default:
+ reg_value = 0;
+ break;
+ }
+
+ tx_buf[0] = reg_value;
+ tx_buf[1] = 0xE0; // ramping time - 20 microseconds
+ #else
+ // For SX1280 boards with no specific PA requirements
+ if (level > 13) {
+ level = 13;
+ } else if (level < -18) {
+ level = -18;
+ }
+
+ _txp = level;
+
+ tx_buf[0] = level + 18;
+ tx_buf[1] = 0xE0; // ramping time - 20 microseconds
+ #endif
+ executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
+}
+
+int8_t sx128x::getTxPower() {
+ return _txp;
+}
+
+void sx128x::setFrequency(uint32_t frequency) {
+ _frequency = frequency;
+
+ uint8_t buf[3];
+
+ uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X);
+
+ buf[0] = ((freq >> 16) & 0xFF);
+ buf[1] = ((freq >> 8) & 0xFF);
+ buf[2] = (freq & 0xFF);
+
+ executeOpcode(OP_RF_FREQ_8X, buf, 3);
+}
+
+uint32_t sx128x::getFrequency() {
+ // we can't read the frequency on the sx1280
+ uint32_t frequency = _frequency;
+
+ return frequency;
+}
+
+void sx128x::setSpreadingFactor(int sf)
+{
+ if (sf < 5) {
+ sf = 5;
+ } else if (sf > 12) {
+ sf = 12;
+ }
+
+ _sf = sf;
+
+ setModulationParams(sf, _bw, _cr);
+ handleLowDataRate();
+}
+
+uint8_t sx128x::getSpreadingFactor()
+{
+ return _sf;
+}
+
+uint32_t sx128x::getSignalBandwidth()
+{
+ int bw = _bw;
+ switch (bw) {
+ case 0x34: return 203.125E3;
+ case 0x26: return 406.25E3;
+ case 0x18: return 812.5E3;
+ case 0x0A: return 1625E3;
+ }
+
+ return 0;
+}
+
+void sx128x::handleLowDataRate(){
- if (sbw <= 203.125E3) {
- _bw = 0x34;
- } else if (sbw <= 406.25E3) {
- _bw = 0x26;
- } else if (sbw <= 812.5E3) {
- _bw = 0x18;
- } else {
- _bw = 0x0A;
- }
++ if (_sf > 10) { _ldro = true; }
++ else { _ldro = false; }
+}
+
+void sx128x::optimizeModemSensitivity(){
+ // todo: check if there's anything the sx1280 can do here
+}
+
+void sx128x::setSignalBandwidth(uint32_t sbw)
+{
- void sx128x::setCodingRate4(int denominator)
- {
++ if (sbw <= 203.125E3) { _bw = 0x34; }
++ else if (sbw <= 406.25E3) { _bw = 0x26; }
++ else if (sbw <= 812.5E3) { _bw = 0x18; }
++ else { _bw = 0x0A; }
+
+ setModulationParams(_sf, _bw, _cr);
+
+ handleLowDataRate();
+ optimizeModemSensitivity();
+}
+
- void sx128x::updateBitrate() {
- if (_radio_online) {
- _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
- _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
- _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
- _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
- _csma_slot_ms = _lora_symbol_time_ms*12;
- if (_csma_slot_ms > CSMA_SLOT_MAX_MS) { _csma_slot_ms = CSMA_SLOT_MAX_MS; }
- if (_csma_slot_ms < CSMA_SLOT_MIN_MS) { _csma_slot_ms = CSMA_SLOT_MIN_MS; }
-
- float target_preamble_symbols;
- if (_bitrate <= LORA_FAST_BITRATE_THRESHOLD) {
- target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
- } else {
- target_preamble_symbols = (LORA_PREAMBLE_FAST_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
- }
- if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
- target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
- } else {
- target_preamble_symbols = ceil(target_preamble_symbols);
- }
- _preambleLength = (long)target_preamble_symbols;
- setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
- } else {
- _bitrate = 0;
- }
- }
-
++void sx128x::setCodingRate4(int denominator) {
++ // TODO: add support for new interleaving scheme, see page 117 of sx1280 datasheet
+ if (denominator < 5) {
+ denominator = 5;
+ } else if (denominator > 8) {
+ denominator = 8;
+ }
+
+ _cr = denominator - 4;
+
+ // todo: add support for new interleaving scheme, see page 117 of sx1280
+ // datasheet
+
+ // update cr values for sx1280's use
+
+ setModulationParams(_sf, _bw, _cr);
+}
+
+uint8_t sx128x::getCodingRate4()
+{
+ return _cr + 4;
+}
+
+void sx128x::setPreambleLength(long length)
+{
+ _preambleLength = length;
+ setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::setSyncWord(int sw)
+{
+ // not implemented
+}
+
+void sx128x::enableCrc()
+{
+ _crcMode = 0x20;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::disableCrc()
+{
+ _crcMode = 0;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+uint8_t sx128x::random()
+{
+ // todo: implement
+ return 0x4; //chosen by fair die roll
+ //guarenteed to be random
+ //https://xkcd.com/221/
+}
+
+void sx128x::setSPIFrequency(uint32_t frequency)
+{
+ _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
+}
+
+void sx128x::dumpRegisters(Stream& out)
+{
+ for (int i = 0; i < 128; i++) {
+ out.print("0x");
+ out.print(i, HEX);
+ out.print(": 0x");
+ out.println(readRegister(i), HEX);
+ }
+}
+
+void sx128x::explicitHeaderMode()
+{
+ _implicitHeaderMode = 0;
+
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::implicitHeaderMode()
+{
+ _implicitHeaderMode = 0x80;
+ setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+
+void sx128x::handleDio0Rise()
+{
+ // received a packet
+ _packetIndex = 0;
+
+ uint8_t rxbuf[2] = {0};
+ executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
+
+ // If implicit header mode is enabled, read packet length as payload length instead.
+ // See SX1280 datasheet v3.2, page 92
+ if (_implicitHeaderMode == 0x80) {
+ _rxPacketLength = _payloadLength;
+ } else {
+ _rxPacketLength = rxbuf[0];
+ }
+
+ if (_onReceive) {
+ _onReceive(_index, _rxPacketLength);
+ }
+}
+
+bool ISR_VECT sx128x::getPacketValidity() {
+ uint8_t buf[2];
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+
+ executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+
+ executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2);
+
+ if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
--- /dev/null
- #define DCD_THRESHOLD 2
- #define DCD_LED_STEP_D 3
- #define LORA_PREAMBLE_SYMBOLS_HW 4
- #define LORA_PREAMBLE_SYMBOLS_MIN 18
- #define LORA_PREAMBLE_TARGET_MS 15
- #define LORA_PREAMBLE_FAST_TARGET_MS 4
- #define LORA_FAST_BITRATE_THRESHOLD 40000
- #define CSMA_SLOT_MAX_MS 100
- #define CSMA_SLOT_MIN_MS 24
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
+// Obviously still under the MIT license.
+
+#ifndef RADIO_H
+#define RADIO_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Interfaces.h"
+#include "Boards.h"
+#include "src/misc/FIFOBuffer.h"
+
+#define MAX_PKT_LENGTH 255
+
+// TX
+#define PA_OUTPUT_RFO_PIN 0
+#define PA_OUTPUT_PA_BOOST_PIN 1
+
++// Default LoRa settings
++#define PHY_HEADER_LORA_SYMBOLS 20
++#define PHY_CRC_LORA_BITS 16
++#define LORA_PREAMBLE_SYMBOLS_MIN 18
++#define LORA_PREAMBLE_TARGET_MS 24
++#define LORA_PREAMBLE_FAST_DELTA 18
++#define LORA_FAST_THRESHOLD_BPS 30E3
++#define LORA_LIMIT_THRESHOLD_BPS 60E3
++
+// DCD
+#define STATUS_INTERVAL_MS 3
+#define DCD_SAMPLES 2500
+#define UTIL_UPDATE_INTERVAL_MS 1000
+#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
+#define AIRTIME_LONGTERM 3600
+#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
+#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
+#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
+#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
- #define _e 2.71828183
- #define _S 12.5
++
++
++// CSMA Parameters
++#define CSMA_SIFS_MS 0
++#define CSMA_POST_TX_YIELD_SLOTS 3
++#define CSMA_SLOT_MAX_MS 100
++#define CSMA_SLOT_MIN_MS 24
++#define CSMA_SLOT_MIN_FAST_DELTA 18
++#define CSMA_SLOT_SYMBOLS 12
++#define CSMA_CW_BANDS 4
++#define CSMA_CW_MIN 0
++#define CSMA_CW_PER_BAND_WINDOWS 15
++#define CSMA_BAND_1_MAX_AIRTIME 7
++#define CSMA_BAND_N_MIN_AIRTIME 85
++#define CSMA_INFR_THRESHOLD_DB 12
++
++#define LED_ID_TRIG 16
++
++#define NOISE_FLOOR_SAMPLES 64
+
+#define RSSI_OFFSET 157
+
+#define PHY_HEADER_LORA_SYMBOLS 8
+
-
++#define MODEM_TIMEOUT_MULT 1.1
+
+// Status flags
+const uint8_t SIG_DETECT = 0x01;
+const uint8_t SIG_SYNCED = 0x02;
+const uint8_t RX_ONGOING = 0x04;
+
+// forward declare Utilities.h LED functions
+void led_rx_on();
- RadioInterface(uint8_t index) : _index(index), _radio_locked(false),
+void led_rx_off();
++void led_id_on();
++void led_id_off();
+void led_indicate_airtime_lock();
+
++void kiss_indicate_channel_stats(uint8_t index);
++void kiss_indicate_csma_stats(uint8_t index);
++
+#if PLATFORM == PLATFORM_ESP32
+// get update_lock for ESP32
+extern portMUX_TYPE update_lock;
+#endif
+
+class RadioInterface : public Stream {
+public:
+ // todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes
- _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0),
++ RadioInterface(uint8_t index) : _index(index), _sf(0x07), _radio_locked(false),
+ _radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0),
- _dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0),
- _post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p(85), _csma_p_min(0.15),
- _csma_p_max(0.333), _csma_b_speed(0.15), _preambleLength(6), _lora_symbol_time_ms(0.0),
- _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0),
- _packet{0}, _onReceive(NULL), _txp(0) {};
++ _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0), _last_packet_cost(0.0),
+ _local_channel_util(0.0), _total_channel_util(0.0),
+ _longterm_channel_util(0.0), _last_status_update(0),
+ _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0),
+ _dcd_count(0), _dcd(false), _dcd_led(false),
- virtual uint8_t modemStatus() = 0;
++ _dcd_waiting(false), _dcd_sample(0),
++ _csma_slot_ms(CSMA_SLOT_MIN_MS),
++ _preambleLength(LORA_PREAMBLE_SYMBOLS_MIN), _lora_symbol_time_ms(0.0),
++ _lora_preamble_time_ms(0), _lora_header_time_ms(0), _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0),
++ _packet{0}, _onReceive(NULL), _txp(0), _ldro(false), _limit_rate(false), _interference_detected(false), _avoid_interference(true), _difs_ms(CSMA_SIFS_MS + 2 * _csma_slot_ms), _difs_wait_start(0), _cw_wait_start(0), _cw_wait_target(0), _cw_wait_passed(0), _csma_cw(-1), _cw_band(1), _cw_min(0), _cw_max(CSMA_CW_PER_BAND_WINDOWS), _noise_floor_sampled(false), _noise_floor_sample(0), _noise_floor_buffer({0}), _noise_floor(-292), _led_id_filter(0), _preamble_detected_at(0) {};
++
+ virtual int begin() = 0;
+ virtual void end() = 0;
+
+ virtual int beginPacket(int implicitHeader = false) = 0;
+ virtual int endPacket() = 0;
+
+ virtual int packetRssi(uint8_t pkt_snr_raw = 0xFF) = 0;
+ virtual int currentRssi() = 0;
+ virtual uint8_t packetRssiRaw() = 0;
+ virtual uint8_t currentRssiRaw() = 0;
+ virtual uint8_t packetSnrRaw() = 0;
+ virtual float packetSnr() = 0;
+ virtual long packetFrequencyError() = 0;
+
+ // from Print
+ virtual size_t write(uint8_t byte) = 0;
+ virtual size_t write(const uint8_t *buffer, size_t size) = 0;
+
+ // from Stream
+ virtual int available() = 0;
+ virtual int read() = 0;
+ virtual int peek() = 0;
+ virtual void flush() = 0;
+
+ virtual void onReceive(void(*callback)(uint8_t, int)) = 0;
+
+ virtual void receive(int size = 0) = 0;
+ virtual void standby() = 0;
+ virtual void sleep() = 0;
+
+ virtual bool preInit() = 0;
+ virtual int8_t getTxPower() = 0;
+ virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0;
+ virtual uint32_t getFrequency() = 0;
+ virtual void setFrequency(uint32_t frequency) = 0;
+ virtual void setSpreadingFactor(int sf) = 0;
+ virtual uint8_t getSpreadingFactor() = 0;
+ virtual uint32_t getSignalBandwidth() = 0;
+ virtual void setSignalBandwidth(uint32_t sbw) = 0;
+ virtual void setCodingRate4(int denominator) = 0;
+ virtual uint8_t getCodingRate4() = 0;
+ virtual void setPreambleLength(long length) = 0;
- virtual byte random() = 0;
++ virtual bool dcd() = 0;
+ virtual void enableCrc() = 0;
+ virtual void disableCrc() = 0;
+ virtual void enableTCXO() = 0;
+ virtual void disableTCXO() = 0;
+
- virtual void updateBitrate() = 0;
++ virtual uint8_t random() = 0;
+
+ virtual void setSPIFrequency(uint32_t frequency) = 0;
+
- updateCSMAp();
-
- //kiss_indicate_channel_stats(); // todo: enable me!
++ void updateBitrate() {
++ if (!_radio_online) { _bitrate = 0; }
++ else {
++
++ _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
++ _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
++ _bitrate = (uint32_t)(_sf * ( (4.0/(float)getCodingRate4()) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
++ _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
++
++ bool fast_rate = _bitrate > LORA_FAST_THRESHOLD_BPS;
++ _limit_rate = _bitrate > LORA_LIMIT_THRESHOLD_BPS;
++
++ int csma_slot_min_ms = CSMA_SLOT_MIN_MS;
++ float lora_preamble_target_ms = LORA_PREAMBLE_TARGET_MS;
++ if (fast_rate) { csma_slot_min_ms -= CSMA_SLOT_MIN_FAST_DELTA;
++ lora_preamble_target_ms -= LORA_PREAMBLE_FAST_DELTA; }
++
++ _csma_slot_ms = _lora_symbol_time_ms*CSMA_SLOT_SYMBOLS;
++ if (_csma_slot_ms > CSMA_SLOT_MAX_MS) { _csma_slot_ms = CSMA_SLOT_MAX_MS; }
++ if (_csma_slot_ms < CSMA_SLOT_MIN_MS) { _csma_slot_ms = csma_slot_min_ms; }
++ _difs_ms = CSMA_SIFS_MS + 2*_csma_slot_ms;
++
++ float target_preamble_symbols = lora_preamble_target_ms/_lora_symbol_time_ms;
++ if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) { target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN; }
++ else { target_preamble_symbols = (ceil)(target_preamble_symbols); }
++
++ setPreambleLength(target_preamble_symbols);
++ _lora_preamble_time_ms = (ceil)(_preambleLength * _lora_symbol_time_ms);
++ _lora_header_time_ms = (ceil)(PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms);
++
++ }
++ }
+ virtual void handleDio0Rise() = 0;
+ virtual bool getPacketValidity() = 0;
+ uint32_t getBitrate() { return _bitrate; };
+ uint8_t getIndex() { return _index; };
+ void setRadioLock(bool lock) { _radio_locked = lock; };
+ bool getRadioLock() { return _radio_locked; };
+ void setRadioOnline(bool online) { _radio_online = online; };
+ bool getRadioOnline() { return _radio_online; };
+ void setSTALock(float at) { _st_airtime_limit = at; };
+ float getSTALock() { return _st_airtime_limit; };
+ void setLTALock(float at) { _lt_airtime_limit = at; };
+ float getLTALock() { return _lt_airtime_limit; };
+ bool calculateALock() {
+ _airtime_lock = false;
+ if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) {
+ _airtime_lock = true;
+ }
+ if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) {
+ _airtime_lock = true;
+ }
+ return _airtime_lock;
+ };
+ void updateAirtime() {
+ uint16_t cb = current_airtime_bin();
+ uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; }
+ uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
+ _airtime_bins[nb] = 0;
+ _airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS);
+
+ uint32_t longterm_airtime_sum = 0;
+ for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
+ longterm_airtime_sum += _airtime_bins[bin];
+ }
+ _longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS;
+
+ float longterm_channel_util_sum = 0.0;
+ for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
+ longterm_channel_util_sum += _longterm_bins[bin];
+ }
+ _longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
+
- void addAirtime(uint16_t written) {
++ updateCSMAParameters();
++ kiss_indicate_channel_stats(_index);
+ };
- float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0;
- packet_cost_ms += payload_cost_ms;
- packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms;
- packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms;
++ float getAirtime(uint16_t written) {
++ float lora_symbols = 0;
+ float packet_cost_ms = 0.0;
- _airtime_bins[cb] += packet_cost_ms;
++
++ if (interfaces[_index] == SX1276 || interfaces[_index] == SX1278) {
++ lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + 8 + PHY_HEADER_LORA_SYMBOLS);
++ lora_symbols /= 4*(_sf-2*_ldro);
++ lora_symbols *= getCodingRate4();
++ lora_symbols += _preambleLength + 0.25 + 8;
++ packet_cost_ms += lora_symbols * _lora_symbol_time_ms;
++ }
++ else if (interfaces[_index] == SX1262 || interfaces[_index] == SX1280) {
++ if (_sf < 7) {
++ lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + PHY_HEADER_LORA_SYMBOLS);
++ lora_symbols /= 4*_sf;
++ lora_symbols *= getCodingRate4();
++ lora_symbols += _preambleLength + 2.25 + 8;
++ packet_cost_ms += lora_symbols * _lora_symbol_time_ms;
++
++ } else {
++ lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + 8 + PHY_HEADER_LORA_SYMBOLS);
++ lora_symbols /= 4*(_sf-2*_ldro);
++ lora_symbols *= getCodingRate4();
++ lora_symbols += _preambleLength + 0.25 + 8;
++ packet_cost_ms += lora_symbols * _lora_symbol_time_ms;
++ }
++ }
++ _last_packet_cost = packet_cost_ms;
++ return packet_cost_ms;
++ }
++ void addAirtime() {
+ uint16_t cb = current_airtime_bin();
+ uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
-
- _util_samples[_dcd_sample] = _dcd;
- _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES;
- if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
++ _airtime_bins[cb] += _last_packet_cost;
+ _airtime_bins[nb] = 0;
+ };
++ void updateModemStatus() {
++ #if MCU_VARIANT == MCU_ESP32
++ portENTER_CRITICAL(&update_lock);
++ #elif MCU_VARIANT == MCU_NRF52
++ portENTER_CRITICAL();
++ #endif
++
++ bool carrier_detected = dcd();
++ int current_rssi = currentRssi();
++ _last_status_update = millis();
++
++ #if MCU_VARIANT == MCU_ESP32
++ portEXIT_CRITICAL(&update_lock);
++ #elif MCU_VARIANT == MCU_NRF52
++ portEXIT_CRITICAL();
++ #endif
++
++ _interference_detected = !carrier_detected && (current_rssi > (_noise_floor+CSMA_INFR_THRESHOLD_DB));
++ if (_interference_detected) { if (_led_id_filter < LED_ID_TRIG) { _led_id_filter += 1; } }
++ else { if (_led_id_filter > 0) {_led_id_filter -= 1; } }
++
++ if (carrier_detected) { _dcd = true; } else { _dcd = false; }
++
++ _dcd_led = _dcd;
++ if (_dcd_led) { led_rx_on(); }
++ else {
++ if (_interference_detected) {
++ if (_led_id_filter >= LED_ID_TRIG && _noise_floor_sampled) { led_id_on(); }
++ } else {
++ if (_airtime_lock) { led_indicate_airtime_lock(); }
++ else { led_rx_off(); led_id_off(); }
++ }
++ }
++ }
++ void updateNoiseFloor() {
++ int current_rssi = currentRssi();
++ if (!_dcd) {
++ if (!_noise_floor_sampled || current_rssi < _noise_floor + CSMA_INFR_THRESHOLD_DB) {
++ _noise_floor_buffer[_noise_floor_sample] = current_rssi;
++ _noise_floor_sample = _noise_floor_sample+1;
++ if (_noise_floor_sample >= NOISE_FLOOR_SAMPLES) {
++ _noise_floor_sample %= NOISE_FLOOR_SAMPLES;
++ _noise_floor_sampled = true;
++ }
++
++ if (_noise_floor_sampled) {
++ _noise_floor = 0;
++ for (int ni = 0; ni < NOISE_FLOOR_SAMPLES; ni++) { _noise_floor += _noise_floor_buffer[ni]; }
++ _noise_floor /= NOISE_FLOOR_SAMPLES;
++ }
++ }
++ }
++ }
+ void checkModemStatus() {
+ if (millis()-_last_status_update >= STATUS_INTERVAL_MS) {
+ updateModemStatus();
- if (_util_samples[ui]) util_count++;
++ updateNoiseFloor();
++
++ _util_samples[_dcd_sample] = _dcd;
++ _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES;
++ if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
+ int util_count = 0;
+ for (int ui = 0; ui < DCD_SAMPLES; ui++) {
-
++ if (_util_samples[ui]) util_count++;
+ }
+ _local_channel_util = (float)util_count / (float)DCD_SAMPLES;
+ _total_channel_util = _local_channel_util + _airtime;
+ if (_total_channel_util > 1.0) _total_channel_util = 1.0;
-
++
+ int16_t cb = current_airtime_bin();
+ uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
+ if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util;
+ _longterm_bins[nb] = 0.0;
- }
++
+ updateAirtime();
- void updateModemStatus() {
- #if PLATFORM == PLATFORM_ESP32
- portENTER_CRITICAL(&update_lock);
- #elif PLATFORM == PLATFORM_NRF52
- portENTER_CRITICAL();
- #endif
-
- uint8_t status = modemStatus();
-
- _last_status_update = millis();
-
- #if PLATFORM == PLATFORM_ESP32
- portEXIT_CRITICAL(&update_lock);
- #elif PLATFORM == PLATFORM_NRF52
- portEXIT_CRITICAL();
- #endif
-
- if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; }
- if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; }
- if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; }
-
- // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) {
- if (_stat_signal_detected || _stat_signal_synced) {
- if (_stat_rx_ongoing) {
- if (_dcd_count < DCD_THRESHOLD) {
- _dcd_count++;
- } else {
- _last_dcd = _last_status_update;
- _dcd_led = true;
- _dcd = true;
- }
- }
- } else {
- if (_dcd_count == 0) {
- _dcd_led = false;
- } else if (_dcd_count > DCD_LED_STEP_D) {
- _dcd_count -= DCD_LED_STEP_D;
- } else {
- _dcd_count = 0;
- }
-
- if (_last_status_update > _last_dcd+_csma_slot_ms) {
- _dcd = false;
- _dcd_led = false;
- _dcd_count = 0;
- }
- }
++ }
+ }
+ };
-
- if (_dcd_led) {
- led_rx_on();
- } else {
- if (_airtime_lock) {
- led_indicate_airtime_lock();
- } else {
- led_rx_off();
- }
- }
- };
- void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; };
- uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; };
++ void updateCSMAParameters() {
++ int airtime_pct = (int)(_airtime*100);
++ int new_cw_band = _cw_band;
++
++ if (airtime_pct <= CSMA_BAND_1_MAX_AIRTIME) { new_cw_band = 1; }
++ else {
++ int at = airtime_pct + CSMA_BAND_1_MAX_AIRTIME;
++ new_cw_band = map(at, CSMA_BAND_1_MAX_AIRTIME, CSMA_BAND_N_MIN_AIRTIME, 2, CSMA_CW_BANDS);
++ }
+
- void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; };
- bool getDCDWaitUntil() { return _dcd_wait_until; };
++ if (new_cw_band > CSMA_CW_BANDS) { new_cw_band = CSMA_CW_BANDS; }
++ if (new_cw_band != _cw_band) {
++ _cw_band = (uint8_t)(new_cw_band);
++ _cw_min = (_cw_band-1) * CSMA_CW_PER_BAND_WINDOWS;
++ _cw_max = (_cw_band) * CSMA_CW_PER_BAND_WINDOWS - 1;
++ kiss_indicate_csma_stats(_index);
++ }
++ }
+ void setDCD(bool dcd) { _dcd = dcd; };
+ bool getDCD() { return _dcd; };
+ void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; };
+ bool getDCDWaiting() { return _dcd_waiting; };
- float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); };
- void updateCSMAp() {
- _csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime+_csma_b_speed)))*255.0);
- }
- uint8_t getCSMAp() { return _csma_p; };
+ float getAirtime() { return _airtime; };
+ float getLongtermAirtime() { return _longterm_airtime; };
+ float getTotalChannelUtil() { return _total_channel_util; };
+ float getLongtermChannelUtil() { return _longterm_channel_util; };
- long _dcd_wait_until;
+ void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; };
+ int getCSMASlotMS() { return _csma_slot_ms; };
+ float getSymbolTime() { return _lora_symbol_time_ms; };
+ float getSymbolRate() { return _lora_symbol_rate; };
+ long getPreambleLength() { return _preambleLength; };
++ void setAvdInterference(bool cfg) { _avoid_interference = cfg; };
++ bool getAvdInterference() { return _avoid_interference; };
++ bool getInterference() { return _interference_detected; };
++ int getNoiseFloor() { return _noise_floor; };
++ unsigned long getDifsMS() { return _difs_ms; };
++ uint8_t getCWBand() { return _cw_band; };
++ uint8_t getCWMin() { return _cw_min; };
++ uint8_t getCWMax() { return _cw_max; };
++ uint8_t getCW() { return _csma_cw; };
++ void setCW(uint8_t cw) { _csma_cw = cw; };
++ void setCWWaitTarget(unsigned long target) { _cw_wait_target = target; };
++ unsigned long getCWWaitTarget() { return _cw_wait_target; };
++ unsigned long getDifsWaitStart() { return _difs_wait_start; };
++ void setDifsWaitStart(unsigned long start) { _difs_wait_start = start; };
++ unsigned long getCWWaitStart() { return _cw_wait_start; };
++ void setCWWaitStart(unsigned long start) { _cw_wait_start = start; };
++ void addCWWaitPassed(unsigned long start) { _cw_wait_passed += start; };
++ void resetCWWaitPassed() { _cw_wait_passed = 0; };
++ bool getCWWaitStatus() { return _cw_wait_passed < _cw_wait_target; };
++ bool getLimitRate() { return _limit_rate; };
+protected:
+ virtual void explicitHeaderMode() = 0;
+ virtual void implicitHeaderMode() = 0;
+
+ uint8_t _index;
++ uint32_t _bitrate;
+ int8_t _txp;
++ uint8_t _sf;
+ bool _radio_locked;
+ bool _radio_online;
+ float _st_airtime_limit;
+ float _lt_airtime_limit;
+ bool _airtime_lock;
+ uint16_t _airtime_bins[AIRTIME_BINS] = {0};
+ uint16_t _longterm_bins[AIRTIME_BINS] = {0};
+ float _airtime;
+ float _longterm_airtime;
++ float _last_packet_cost;
+ float _local_channel_util;
+ float _total_channel_util;
+ float _longterm_channel_util;
+ uint32_t _last_status_update;
+ bool _stat_signal_detected;
+ bool _stat_signal_synced;
+ bool _stat_rx_ongoing;
+ uint32_t _last_dcd;
+ uint16_t _dcd_count;
+ bool _dcd;
+ bool _dcd_led;
+ bool _dcd_waiting;
- uint32_t _post_tx_yield_timeout;
- uint8_t _csma_p;
- int _csma_slot_ms;
- float _csma_p_min;
- float _csma_p_max;
- float _csma_b_speed;
+ bool _util_samples[DCD_SAMPLES] = {false};
+ int _dcd_sample;
- uint32_t _bitrate;
- uint8_t _packet[255];
+ long _preambleLength;
+ float _lora_symbol_time_ms;
+ float _lora_symbol_rate;
+ float _lora_us_per_byte;
- uint8_t modemStatus();
++ long _lora_preamble_time_ms;
++ long _lora_header_time_ms;
++ bool _ldro;
++ bool _limit_rate;
++ bool _interference_detected;
++ bool _avoid_interference;
++ int _csma_slot_ms;
++ unsigned long _difs_ms;
++ unsigned long _difs_wait_start;
++ unsigned long _cw_wait_start;
++ unsigned long _cw_wait_target;
++ unsigned long _cw_wait_passed;
++ int _csma_cw;
++ uint8_t _cw_band;
++ uint8_t _cw_min;
++ uint8_t _cw_max;
++ bool _noise_floor_sampled;
++ int _noise_floor_sample;
++ int _noise_floor_buffer[NOISE_FLOOR_SAMPLES];
++ int _noise_floor;
++ uint8_t _led_id_filter;
++ unsigned long _preamble_detected_at;
++
++ uint8_t _packet[255];
+ void (*_onReceive)(uint8_t, int);
+};
+
+class sx126x : public RadioInterface {
+public:
+ sx126x(uint8_t index, SPIClass* spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int
+ dio0, int busy, int rxen);
+
+ int begin();
+ void end();
+
+ int beginPacket(int implicitHeader = false);
+ int endPacket();
+
+ int packetRssi(uint8_t pkt_snr_raw = 0xFF);
+ int currentRssi();
+ uint8_t packetRssiRaw();
+ uint8_t currentRssiRaw();
+ uint8_t packetSnrRaw();
+ float packetSnr();
+ long packetFrequencyError();
+
+ // from Print
+ size_t write(uint8_t byte);
+ size_t write(const uint8_t *buffer, size_t size);
+
+ // from Stream
+ int available();
+ int read();
+ int peek();
+ void flush();
+
+ void onReceive(void(*callback)(uint8_t, int));
+
+ void receive(int size = 0);
+ void standby();
+ void sleep();
+
+ bool preInit();
+ int8_t getTxPower();
+ void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+ uint32_t getFrequency();
+ void setFrequency(uint32_t frequency);
+ void setSpreadingFactor(int sf);
+ uint8_t getSpreadingFactor();
+ uint32_t getSignalBandwidth();
+ void setSignalBandwidth(uint32_t sbw);
+ void setCodingRate4(int denominator);
+ uint8_t getCodingRate4();
+ void setPreambleLength(long length);
- void updateBitrate();
-
++ bool dcd();
+ void enableCrc();
+ void disableCrc();
+ void enableTCXO();
+ void disableTCXO();
+
+
+ byte random();
+
+ void setSPIFrequency(uint32_t frequency);
+
+ void dumpRegisters(Stream& out);
+
- uint8_t _sf;
+ void handleDio0Rise();
+private:
+ void writeBuffer(const uint8_t* buffer, size_t size);
+ void readBuffer(uint8_t* buffer, size_t size);
+ void loraMode();
+ void rxAntEnable();
+ void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+ void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
+ void setSyncWord(uint16_t sw);
+ void waitOnBusy();
+ void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
+ void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
+ void explicitHeaderMode();
+ void implicitHeaderMode();
+
+
+ uint8_t readRegister(uint16_t address);
+ void writeRegister(uint16_t address, uint8_t value);
+ uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
+
+ static void onDio0Rise();
+
+ void handleLowDataRate();
+ void optimizeModemSensitivity();
+
+ void reset(void);
+ void calibrate(void);
+ void calibrate_image(uint32_t frequency);
+ bool getPacketValidity();
+
+private:
+ SPISettings _spiSettings;
+ SPIClass* _spiModem;
+ int _ss;
+ int _sclk;
+ int _mosi;
+ int _miso;
+ int _reset;
+ int _dio0;
+ int _rxen;
+ int _busy;
+ uint32_t _frequency;
- uint8_t _ldro;
+ uint8_t _bw;
+ uint8_t _cr;
- uint8_t modemStatus();
+ int _packetIndex;
+ int _implicitHeaderMode;
+ int _payloadLength;
+ int _crcMode;
+ int _fifo_tx_addr_ptr;
+ int _fifo_rx_addr_ptr;
+ bool _preinit_done;
+ bool _tcxo;
+ bool _dio2_as_rf_switch;
+};
+
+class sx127x : public RadioInterface {
+public:
+ sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy);
+
+ int begin();
+ void end();
+
+ int beginPacket(int implicitHeader = false);
+ int endPacket();
+
+ int packetRssi(uint8_t pkt_snr_raw = 0xFF);
++ int packetRssi();
+ int currentRssi();
+ uint8_t packetRssiRaw();
+ uint8_t currentRssiRaw();
+ uint8_t packetSnrRaw();
+ float packetSnr();
+ long packetFrequencyError();
+
+ // from Print
+ size_t write(uint8_t byte);
+ size_t write(const uint8_t *buffer, size_t size);
+
+ // from Stream
+ int available();
+ int read();
+ int peek();
+ void flush();
+
+ void onReceive(void(*callback)(uint8_t, int));
+
+ void receive(int size = 0);
+ void standby();
+ void sleep();
+
+ bool preInit();
+ int8_t getTxPower();
+ void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+ uint32_t getFrequency();
+ void setFrequency(uint32_t frequency);
+ void setSpreadingFactor(int sf);
+ uint8_t getSpreadingFactor();
+ uint32_t getSignalBandwidth();
+ void setSignalBandwidth(uint32_t sbw);
+ void setCodingRate4(int denominator);
+ uint8_t getCodingRate4();
+ void setPreambleLength(long length);
- void updateBitrate();
-
++ bool dcd();
+ void enableCrc();
+ void disableCrc();
+ void enableTCXO();
+ void disableTCXO();
+
+ byte random();
+
+ void setSPIFrequency(uint32_t frequency);
+
- uint8_t _sf;
+ void handleDio0Rise();
+ bool getPacketValidity();
+private:
+ void setSyncWord(uint8_t sw);
+ void explicitHeaderMode();
+ void implicitHeaderMode();
+
+
+ uint8_t readRegister(uint8_t address);
+ void writeRegister(uint8_t address, uint8_t value);
+ uint8_t singleTransfer(uint8_t address, uint8_t value);
+
+ static void onDio0Rise();
+
+ void handleLowDataRate();
+ void optimizeModemSensitivity();
+
+private:
+ SPISettings _spiSettings;
+ SPIClass* _spiModem;
+ int _ss;
+ int _sclk;
+ int _mosi;
+ int _miso;
+ int _reset;
+ int _dio0;
+ int _busy;
+ uint32_t _frequency;
+ int _packetIndex;
+ int _implicitHeaderMode;
+ bool _preinit_done;
- uint8_t modemStatus();
+ uint8_t _cr;
+ uint32_t _bw;
+};
+
+class sx128x : public RadioInterface {
+public:
+ sx128x(uint8_t index, SPIClass* spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen);
+
+ int begin();
+ void end();
+
+ int beginPacket(int implicitHeader = false);
+ int endPacket();
+
+ int packetRssi(uint8_t pkt_snr_raw = 0xFF);
+ int currentRssi();
+ uint8_t packetRssiRaw();
+ uint8_t currentRssiRaw();
+ uint8_t packetSnrRaw();
+ float packetSnr();
+ long packetFrequencyError();
+
+ // from Print
+ size_t write(uint8_t byte);
+ size_t write(const uint8_t *buffer, size_t size);
+
+ // from Stream
+ int available();
+ int read();
+ int peek();
+ void flush();
+
+ void onReceive(void(*callback)(uint8_t, int));
+
+ void receive(int size = 0);
+ void standby();
+ void sleep();
+
+ bool preInit();
+ int8_t getTxPower();
+ void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+ uint32_t getFrequency();
+ void setFrequency(uint32_t frequency);
+ void setSpreadingFactor(int sf);
+ uint8_t getSpreadingFactor();
+ uint32_t getSignalBandwidth();
+ void setSignalBandwidth(uint32_t sbw);
+ void setCodingRate4(int denominator);
+ uint8_t getCodingRate4();
+ void setPreambleLength(long length);
- void updateBitrate();
-
++ bool dcd();
+ void enableCrc();
+ void disableCrc();
+ void enableTCXO();
+ void disableTCXO();
+
+ byte random();
+
+ void setSPIFrequency(uint32_t frequency);
+
+ void dumpRegisters(Stream& out);
+
- uint8_t _sf;
+ void handleDio0Rise();
+
+ bool getPacketValidity();
+
+private:
+ void writeBuffer(const uint8_t* buffer, size_t size);
+ void readBuffer(uint8_t* buffer, size_t size);
+ void txAntEnable();
+ void rxAntEnable();
+ void loraMode();
+ void waitOnBusy();
+ void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
+ void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
+ void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+ void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
+ void setSyncWord(int sw);
+ void explicitHeaderMode();
+ void implicitHeaderMode();
+
+
+ uint8_t readRegister(uint16_t address);
+ void writeRegister(uint16_t address, uint8_t value);
+ uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
+
+ static void onDio0Rise();
+
+ void handleLowDataRate();
+ void optimizeModemSensitivity();
+
+private:
+ SPISettings _spiSettings;
+ SPIClass* _spiModem;
+ int _ss;
+ int _sclk;
+ int _mosi;
+ int _miso;
+ int _reset;
+ int _dio0;
+ int _rxen;
+ int _txen;
+ int _busy;
+ int _modem;
+ uint32_t _frequency;
+ uint8_t _bw;
+ uint8_t _cr;
+ int _packetIndex;
+ int _implicitHeaderMode;
+ int _payloadLength;
+ int _crcMode;
+ int _fifo_tx_addr_ptr;
+ int _fifo_rx_addr_ptr;
+ bool _preinit_done;
+ int _rxPacketLength;
+ bool _tcxo;
++ uint8_t _preamble_e;
++ uint8_t _preamble_m;
++ uint32_t _last_preamble;
+};
+#endif
# Precompiled Firmware
--You can download and flash the firmware to supported boards using the [RNode Config Utility](https://github.com/markqvist/rnodeconfigutil). All firmware releases are now handled and installed directly through `rnodeconf`, which is inclueded in the `rns` package. It can be installed via `pip`:
++The firmware is now handled and installed to RNodes directly through `rnodeconf`, which is inclueded in the `rns` package. It can be installed via `pip`:
```
# Install rnodeconf via rns package
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
+ #elif BOARD_MODEL == BOARD_E22_ESP32
+ 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); }
+ void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
+ void led_id_on() { }
+ void led_id_off() { }
#elif BOARD_MODEL == BOARD_TBEAM
void led_rx_on() { digitalWrite(pin_led_rx, HIGH); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
+ void led_id_on() { }
+ void led_id_off() { }
++ #elif BOARD_MODEL == BOARD_H_W_PAPER
++ 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); }
++ void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
++ void led_id_on() { }
++ void led_id_off() { }
#elif BOARD_MODEL == BOARD_LORA32_V2_1
void led_rx_on() { digitalWrite(pin_led_rx, HIGH); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
+ void led_id_on() { }
+ void led_id_off() { }
#endif
#elif MCU_VARIANT == MCU_NRF52
- #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
- 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); }
- void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
- #elif BOARD_MODEL == BOARD_TECHO
+ #if HAS_NP == true
+ void led_rx_on() { npset(0, 0, 0xFF); }
+ void led_rx_off() { npset(0, 0, 0); }
+ void led_tx_on() { npset(0xFF, 0x50, 0x00); }
+ void led_tx_off() { npset(0, 0, 0); }
- void led_id_on() { npset(0x90, 0, 0x70); }
- void led_id_off() { npset(0, 0, 0); }
- #elif BOARD_MODEL == BOARD_RAK4631
++ void led_id_on() { npset(0x90, 0, 0x70); }
++ void led_id_off() { npset(0, 0, 0); }
++ #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
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); }
unsigned long led_standby_value = led_standby_min;
int8_t led_standby_direction = 0;
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
- void led_indicate_standby() {
- led_standby_ticks++;
- if (led_standby_ticks > led_standby_wait) {
- led_standby_ticks = 0;
- if (led_standby_value <= led_standby_min) {
- led_standby_direction = 1;
- } else if (led_standby_value >= led_standby_max) {
- led_standby_direction = -1;
- }
- led_standby_value += led_standby_direction;
- analogWrite(pin_led_rx, led_standby_value);
- led_tx_off();
- }
- }
-
-#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-- #if HAS_NP == true
-- void led_indicate_standby() {
-- led_standby_ticks++;
--
-- if (led_standby_ticks > led_standby_wait) {
-- led_standby_ticks = 0;
--
-- if (led_standby_value <= led_standby_min) {
-- led_standby_direction = 1;
-- } else if (led_standby_value >= led_standby_max) {
-- led_standby_direction = -1;
-- }
--
-- uint8_t led_standby_intensity;
-- led_standby_value += led_standby_direction;
-- int led_standby_ti = led_standby_value - led_standby_lng;
--
-- if (led_standby_ti < 0) {
-- led_standby_intensity = 0;
-- } else if (led_standby_ti > led_standby_cut) {
-- led_standby_intensity = led_standby_cut;
-- } else {
-- led_standby_intensity = led_standby_ti;
-- }
-- npset(led_standby_intensity/3, led_standby_intensity/3, led_standby_intensity/3);
-- }
-- }
++#if HAS_NP == true
++ void led_indicate_standby() {
++ led_standby_ticks++;
++
++ if (led_standby_ticks > led_standby_wait) {
++ led_standby_ticks = 0;
++
++ if (led_standby_value <= led_standby_min) {
++ led_standby_direction = 1;
++ } else if (led_standby_value >= led_standby_max) {
++ led_standby_direction = -1;
++ }
+
- void led_indicate_console() {
- npset(0x60, 0x00, 0x60);
- // led_standby_ticks++;
-
- // if (led_standby_ticks > led_console_wait) {
- // led_standby_ticks = 0;
-
- // if (led_standby_value <= led_standby_min) {
- // led_standby_direction = 1;
- // } else if (led_standby_value >= led_standby_max) {
- // led_standby_direction = -1;
- // }
-
- // uint8_t led_standby_intensity;
- // led_standby_value += led_standby_direction;
- // int led_standby_ti = led_standby_value - led_standby_lng;
-
- // if (led_standby_ti < 0) {
- // led_standby_intensity = 0;
- // } else if (led_standby_ti > led_standby_cut) {
- // led_standby_intensity = led_standby_cut;
- // } else {
- // led_standby_intensity = led_standby_ti;
- // }
- // npset(led_standby_intensity, 0x00, led_standby_intensity);
- // }
- }
++ uint8_t led_standby_intensity;
++ led_standby_value += led_standby_direction;
++ int led_standby_ti = led_standby_value - led_standby_lng;
+
- #else
- void led_indicate_standby() {
- led_standby_ticks++;
- if (led_standby_ticks > led_standby_wait) {
- led_standby_ticks = 0;
- if (led_standby_value <= led_standby_min) {
- led_standby_direction = 1;
- } else if (led_standby_value >= led_standby_max) {
- led_standby_direction = -1;
- }
- led_standby_value += led_standby_direction;
- if (led_standby_value > 253) {
- led_tx_on();
- } else {
- led_tx_off();
- }
- #if BOARD_MODEL == BOARD_LORA32_V2_1
- #if defined(EXTERNAL_LEDS)
- led_rx_off();
- #endif
- #elif BOARD_MODEL == BOARD_LORA32_V2_0
- #if defined(EXTERNAL_LEDS)
- led_rx_off();
- #endif
- #else
- led_rx_off();
- #endif
- }
- }
++ if (led_standby_ti < 0) {
++ led_standby_intensity = 0;
++ } else if (led_standby_ti > led_standby_cut) {
++ led_standby_intensity = led_standby_cut;
++ } else {
++ led_standby_intensity = led_standby_ti;
++ }
++ npset(led_standby_intensity/3, led_standby_intensity/3, led_standby_intensity/3);
++ }
++ }
-- void led_indicate_console() {
- led_indicate_standby();
- npset(0x60, 0x00, 0x60);
- // led_standby_ticks++;
-
- // if (led_standby_ticks > led_console_wait) {
- // led_standby_ticks = 0;
-
- // if (led_standby_value <= led_standby_min) {
- // led_standby_direction = 1;
- // } else if (led_standby_value >= led_standby_max) {
- // led_standby_direction = -1;
- // }
-
- // uint8_t led_standby_intensity;
- // led_standby_value += led_standby_direction;
- // int led_standby_ti = led_standby_value - led_standby_lng;
-
- // if (led_standby_ti < 0) {
- // led_standby_intensity = 0;
- // } else if (led_standby_ti > led_standby_cut) {
- // led_standby_intensity = led_standby_cut;
- // } else {
- // led_standby_intensity = led_standby_ti;
- // }
- // npset(led_standby_intensity, 0x00, led_standby_intensity);
- // }
-- }
- #endif
++ void led_indicate_console() {
++ npset(0x60, 0x00, 0x60);
++ // led_standby_ticks++;
++
++ // if (led_standby_ticks > led_console_wait) {
++ // led_standby_ticks = 0;
++
++ // if (led_standby_value <= led_standby_min) {
++ // led_standby_direction = 1;
++ // } else if (led_standby_value >= led_standby_max) {
++ // led_standby_direction = -1;
++ // }
++
++ // uint8_t led_standby_intensity;
++ // led_standby_value += led_standby_direction;
++ // int led_standby_ti = led_standby_value - led_standby_lng;
++
++ // if (led_standby_ti < 0) {
++ // led_standby_intensity = 0;
++ // } else if (led_standby_ti > led_standby_cut) {
++ // led_standby_intensity = led_standby_cut;
++ // } else {
++ // led_standby_intensity = led_standby_ti;
++ // }
++// npset(led_standby_intensity, 0x00, led_standby_intensity);
++ // }
++ }
+
- #else
- void led_indicate_standby() {
- led_standby_ticks++;
- if (led_standby_ticks > led_standby_wait) {
- led_standby_ticks = 0;
- if (led_standby_value <= led_standby_min) {
- led_standby_direction = 1;
- } else if (led_standby_value >= led_standby_max) {
- led_standby_direction = -1;
- }
- led_standby_value += led_standby_direction;
- if (led_standby_value > 253) {
- #if BOARD_MODEL == BOARD_TECHO
- led_rx_on();
- #else
- led_tx_on();
- #endif
- } else {
- #if BOARD_MODEL == BOARD_TECHO
- led_rx_off();
- #else
- led_tx_off();
- #endif
- }
- #if BOARD_MODEL == BOARD_LORA32_V2_1
- #if defined(EXTERNAL_LEDS)
- led_rx_off();
- #endif
- #elif BOARD_MODEL == BOARD_LORA32_V2_0
- #if defined(EXTERNAL_LEDS)
- led_rx_off();
- #endif
- #else
- led_rx_off();
- #endif
- }
- }
++#else
++ void led_indicate_standby() {
++ led_standby_ticks++;
++ if (led_standby_ticks > led_standby_wait) {
++ led_standby_ticks = 0;
++ if (led_standby_value <= led_standby_min) {
++ led_standby_direction = 1;
++ } else if (led_standby_value >= led_standby_max) {
++ led_standby_direction = -1;
++ }
++ led_standby_value += led_standby_direction;
++ if (led_standby_value > 253) {
++ #if BOARD_MODEL == BOARD_TECHO
++ led_rx_on();
++ #else
++ led_tx_on();
++ #endif
++ } else {
++ #if BOARD_MODEL == BOARD_TECHO
++ led_rx_off();
++ #else
++ led_tx_off();
++ #endif
++ }
++ #if BOARD_MODEL == BOARD_LORA32_V2_1
++ #if defined(EXTERNAL_LEDS)
++ led_rx_off();
++ #endif
++ #elif BOARD_MODEL == BOARD_LORA32_V2_0
++ #if defined(EXTERNAL_LEDS)
++ led_rx_off();
++ #endif
++ #else
++ led_rx_off();
++ #endif
++ }
++ }
+
- void led_indicate_console() {
- led_indicate_standby();
- }
- #endif
++ void led_indicate_console() {
++ led_indicate_standby();
++ }
#endif
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
- void led_indicate_not_ready() {
- led_standby_ticks++;
- if (led_standby_ticks > led_standby_wait) {
- led_standby_ticks = 0;
- if (led_standby_value <= led_standby_min) {
- led_standby_direction = 1;
- } else if (led_standby_value >= led_standby_max) {
- led_standby_direction = -1;
- }
- led_standby_value += led_standby_direction;
- analogWrite(pin_led_tx, led_standby_value);
- led_rx_off();
- }
- }
-#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-- #if HAS_NP == true
++#if HAS_NP == true
++void led_indicate_not_ready() {
++ led_standby_ticks++;
++
++ if (led_standby_ticks > led_notready_wait) {
++ led_standby_ticks = 0;
++
++ if (led_standby_value <= led_standby_min) {
++ led_standby_direction = 1;
++ } else if (led_standby_value >= led_standby_max) {
++ led_standby_direction = -1;
++ }
++
++ uint8_t led_standby_intensity;
++ led_standby_value += led_standby_direction;
++ int led_standby_ti = led_standby_value - led_standby_lng;
++
++ if (led_standby_ti < 0) {
++ led_standby_intensity = 0;
++ } else if (led_standby_ti > led_standby_cut) {
++ led_standby_intensity = led_standby_cut;
++ } else {
++ led_standby_intensity = led_standby_ti;
++ }
++
++ npset(led_standby_intensity, 0x00, 0x00);
++ }
++ }
++#else
void led_indicate_not_ready() {
-- led_standby_ticks++;
--
-- if (led_standby_ticks > led_notready_wait) {
-- led_standby_ticks = 0;
--
-- if (led_standby_value <= led_standby_min) {
-- led_standby_direction = 1;
-- } else if (led_standby_value >= led_standby_max) {
-- led_standby_direction = -1;
-- }
--
-- uint8_t led_standby_intensity;
-- led_standby_value += led_standby_direction;
-- int led_standby_ti = led_standby_value - led_standby_lng;
--
-- if (led_standby_ti < 0) {
-- led_standby_intensity = 0;
-- } else if (led_standby_ti > led_standby_cut) {
-- led_standby_intensity = led_standby_cut;
-- } else {
-- led_standby_intensity = led_standby_ti;
-- }
--
-- npset(led_standby_intensity, 0x00, 0x00);
-- }
-- }
-- #else
-- void led_indicate_not_ready() {
-- led_notready_ticks++;
-- if (led_notready_ticks > led_notready_wait) {
-- led_notready_ticks = 0;
-- if (led_notready_value <= led_notready_min) {
-- led_notready_direction = 1;
-- } else if (led_notready_value >= led_notready_max) {
-- led_notready_direction = -1;
-- }
-- led_notready_value += led_notready_direction;
-- if (led_notready_value > 128) {
-- led_tx_on();
-- } else {
-- led_tx_off();
-- }
-- #if BOARD_MODEL == BOARD_LORA32_V2_1
-- #if defined(EXTERNAL_LEDS)
-- led_rx_off();
-- #endif
-- #elif BOARD_MODEL == BOARD_LORA32_V2_0
-- #if defined(EXTERNAL_LEDS)
-- led_rx_off();
-- #endif
-- #else
-- led_rx_off();
-- #endif
-- }
-- }
-- #endif
++ led_notready_ticks++;
++ if (led_notready_ticks > led_notready_wait) {
++ led_notready_ticks = 0;
++ if (led_notready_value <= led_notready_min) {
++ led_notready_direction = 1;
++ } else if (led_notready_value >= led_notready_max) {
++ led_notready_direction = -1;
++ }
++ led_notready_value += led_notready_direction;
++ if (led_notready_value > 128) {
++ led_tx_on();
++ } else {
++ led_tx_off();
++ }
++ #if BOARD_MODEL == BOARD_LORA32_V2_1
++ #if defined(EXTERNAL_LEDS)
++ led_rx_off();
++ #endif
++ #elif BOARD_MODEL == BOARD_LORA32_V2_0
++ #if defined(EXTERNAL_LEDS)
++ led_rx_off();
++ #endif
++ #else
++ led_rx_off();
++ #endif
++ }
++ }
#endif
+
+bool interface_bitrate_cmp(RadioInterface* p, RadioInterface* q) {
+ long p_bitrate = p->getBitrate();
+ long q_bitrate = q->getBitrate();
+ return p_bitrate > q_bitrate;
+}
+
+// Sort interfaces in descending order according to bitrate.
+void sort_interfaces() {
+ std::sort(std::begin(interface_obj_sorted), std::end(interface_obj_sorted), interface_bitrate_cmp);
+}
+
void serial_write(uint8_t byte) {
#if HAS_BLUETOOTH || HAS_BLE == true
if (bt_state != BT_STATE_CONNECTED) {
serial_write(FEND);
}
- void kiss_indicate_channel_stats(RadioInterface* radio) {
-void kiss_indicate_channel_stats() {
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
- uint16_t ats = (uint16_t)(airtime*100*100);
- uint16_t atl = (uint16_t)(longterm_airtime*100*100);
- uint16_t cls = (uint16_t)(total_channel_util*100*100);
- uint16_t cll = (uint16_t)(longterm_channel_util*100*100);
- uint8_t crs = (uint8_t)(current_rssi+rssi_offset);
- uint8_t nfl = (uint8_t)(noise_floor+rssi_offset);
- uint8_t ntf = 0xFF; if (interference_detected) { ntf = (uint8_t)(current_rssi+rssi_offset); }
- serial_write(FEND);
- serial_write(CMD_STAT_CHTM);
- escaped_serial_write(ats>>8);
- escaped_serial_write(ats);
- escaped_serial_write(atl>>8);
- escaped_serial_write(atl);
- escaped_serial_write(cls>>8);
- escaped_serial_write(cls);
- escaped_serial_write(cll>>8);
- escaped_serial_write(cll);
- escaped_serial_write(crs);
- escaped_serial_write(nfl);
- escaped_serial_write(ntf);
- serial_write(FEND);
- #endif
-}
-
-void kiss_indicate_csma_stats() {
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
- serial_write(FEND);
- serial_write(CMD_STAT_CSMA);
- escaped_serial_write(cw_band);
- escaped_serial_write(cw_min);
- escaped_serial_write(cw_max);
- serial_write(FEND);
- #endif
-}
-
-void kiss_indicate_phy_stats() {
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
- uint16_t lst = (uint16_t)(lora_symbol_time_ms*1000);
- uint16_t lsr = (uint16_t)(lora_symbol_rate);
- uint16_t prs = (uint16_t)(lora_preamble_symbols);
- uint16_t prt = (uint16_t)(lora_preamble_time_ms);
- uint16_t cst = (uint16_t)(csma_slot_ms);
- uint16_t dft = (uint16_t)(difs_ms);
- serial_write(FEND);
- serial_write(CMD_STAT_PHYPRM);
- escaped_serial_write(lst>>8); escaped_serial_write(lst);
- escaped_serial_write(lsr>>8); escaped_serial_write(lsr);
- escaped_serial_write(prs>>8); escaped_serial_write(prs);
- escaped_serial_write(prt>>8); escaped_serial_write(prt);
- escaped_serial_write(cst>>8); escaped_serial_write(cst);
- escaped_serial_write(dft>>8); escaped_serial_write(dft);
- serial_write(FEND);
- #endif
++void kiss_indicate_channel_stats(uint8_t index) {
++ RadioInterface* radio = interface_obj[index];
++ int current_rssi = radio->currentRssi();
+ uint16_t ats = (uint16_t)(radio->getAirtime()*100*100);
+ uint16_t atl = (uint16_t)(radio->getLongtermAirtime()*100*100);
+ uint16_t cls = (uint16_t)(radio->getTotalChannelUtil()*100*100);
+ uint16_t cll = (uint16_t)(radio->getLongtermChannelUtil()*100*100);
++ uint8_t crs = (uint8_t)(current_rssi+rssi_offset);
++ uint8_t nfl = (uint8_t)(radio->getNoiseFloor()+rssi_offset);
++ uint8_t ntf = 0xFF; if (radio->getInterference()) { ntf = (uint8_t)(current_rssi+rssi_offset); }
+ serial_write(FEND);
+ serial_write(CMD_SEL_INT);
+ serial_write(radio->getIndex());
+ serial_write(FEND);
+ serial_write(FEND);
+ serial_write(CMD_STAT_CHTM);
+ escaped_serial_write(ats>>8);
+ escaped_serial_write(ats);
+ escaped_serial_write(atl>>8);
+ escaped_serial_write(atl);
+ escaped_serial_write(cls>>8);
+ escaped_serial_write(cls);
+ escaped_serial_write(cll>>8);
+ escaped_serial_write(cll);
++ escaped_serial_write(crs);
++ escaped_serial_write(nfl);
++ escaped_serial_write(ntf);
+ serial_write(FEND);
+}
+
+void kiss_indicate_phy_stats(RadioInterface* radio) {
+ uint16_t lst = (uint16_t)(radio->getSymbolTime()*1000);
+ uint16_t lsr = (uint16_t)(radio->getSymbolRate());
+ uint16_t prs = (uint16_t)(radio->getPreambleLength()+4);
+ uint16_t prt = (uint16_t)((radio->getPreambleLength()+4)*radio->getSymbolTime());
+ uint16_t cst = (uint16_t)(radio->getCSMASlotMS());
++ uint16_t dft = (uint16_t)(radio->getDifsMS());
+ serial_write(FEND);
+ serial_write(CMD_SEL_INT);
+ serial_write(radio->getIndex());
+ serial_write(FEND);
+ serial_write(FEND);
+ serial_write(CMD_STAT_PHYPRM);
- escaped_serial_write(lst>>8);
- escaped_serial_write(lst);
- escaped_serial_write(lsr>>8);
- escaped_serial_write(lsr);
- escaped_serial_write(prs>>8);
- escaped_serial_write(prs);
- escaped_serial_write(prt>>8);
- escaped_serial_write(prt);
- escaped_serial_write(cst>>8);
- escaped_serial_write(cst);
++ escaped_serial_write(lst>>8); escaped_serial_write(lst);
++ escaped_serial_write(lsr>>8); escaped_serial_write(lsr);
++ escaped_serial_write(prs>>8); escaped_serial_write(prs);
++ escaped_serial_write(prt>>8); escaped_serial_write(prt);
++ escaped_serial_write(cst>>8); escaped_serial_write(cst);
++ escaped_serial_write(dft>>8); escaped_serial_write(dft);
++ serial_write(FEND);
++}
++
++void kiss_indicate_csma_stats(uint8_t index) {
++ selected_radio = interface_obj[index];
++ serial_write(FEND);
++ serial_write(CMD_SEL_INT);
++ serial_write(index);
++ serial_write(FEND);
++ serial_write(FEND);
++ serial_write(CMD_STAT_CSMA);
++ escaped_serial_write(selected_radio->getCWBand());
++ escaped_serial_write(selected_radio->getCWMin());
++ escaped_serial_write(selected_radio->getCWMax());
+ serial_write(FEND);
}
void kiss_indicate_battery() {
- #if MCU_VARIANT == MCU_ESP32
- #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
serial_write(FEND);
serial_write(CMD_STAT_BAT);
escaped_serial_write(battery_state);
escaped_serial_write((uint8_t)int(battery_percent));
serial_write(FEND);
-- #endif
}
void kiss_indicate_btpin() {
serial_write(FEND);
}
--#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
void kiss_indicate_device_hash() {
serial_write(FEND);
serial_write(CMD_DEV_HASH);
}
serial_write(FEND);
}
--#endif
void kiss_indicate_fb() {
serial_write(FEND);
}
}
-int getTxPower() {
- uint8_t txp = LoRa->getTxPower();
- return (int)txp;
-}
-
-void setTXPower() {
- if (radio_online) {
- if (model == MODEL_11) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- if (model == MODEL_12) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
-
- if (model == MODEL_C6) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- if (model == MODEL_C7) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
-
- if (model == MODEL_A1) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A2) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A3) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- if (model == MODEL_A4) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- if (model == MODEL_A5) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A6) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A7) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_A9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_AA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_AC) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_BA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_BB) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_C5) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_CA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_D4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_D9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_DB) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_DC) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_E3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_E8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- }
-}
-
-
-void getBandwidth() {
- if (radio_online) {
- lora_bw = LoRa->getSignalBandwidth();
- }
- updateBitrate();
-}
+void setTXPower(RadioInterface* radio, int txp) {
+ // Todo, revamp this function. The current parameters for setTxPower are
+ // suboptimal, as some chips have power amplifiers which means that the max
+ // dBm is not always the same.
- if (model == MODEL_11) {
- if (interfaces[radio->getIndex()] == SX128X) {
++ if (model == MODEL_12) {
++ if (interfaces[radio->getIndex()] == SX1280) {
+ radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ } else {
+ radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ }
+ }
- if (model == MODEL_12) {
- if (interfaces[radio->getIndex()] == SX128X) {
++ if (model == MODEL_13) {
++ if (interfaces[radio->getIndex()] == SX1280) {
+ radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ } else {
+ radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ }
+ }
-void setBandwidth() {
- if (radio_online) {
- LoRa->setSignalBandwidth(lora_bw);
- getBandwidth();
- }
-}
+ if (model == MODEL_21) {
- if (interfaces[radio->getIndex()] == SX128X) {
++ if (interfaces[radio->getIndex()] == SX1280) {
+ radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ } else {
+ radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ }
+ }
-void getFrequency() {
- if (radio_online) {
- lora_freq = LoRa->getFrequency();
+ if (model == MODEL_A1) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_A2) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_A3) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ if (model == MODEL_A4) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ if (model == MODEL_A5) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ if (model == MODEL_A6) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_A7) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_A8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_A9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_AA) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_AC) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_B3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_B4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_B8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_B9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_BA) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_BB) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_C4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_C9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_C5) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
++ if (model == MODEL_C6) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
++ if (model == MODEL_C7) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+ if (model == MODEL_CA) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_D4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_D9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_DB) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_DC) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_E4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_E9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_E3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_E8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+
+ if (model == MODEL_FE) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_FF) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+}
+
+uint8_t getRandom(RadioInterface* radio) {
+ if (radio->getRadioOnline()) {
+ return radio->random();
+ } else {
+ return 0x00;
}
}
EEPROM.write(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.read(read_byte_ptr, 1);
- file.seek(mapped_addr);
- if (read_byte != byte) {
- file.write(byte);
- }
- written_bytes++;
-
- if (((mapped_addr - eeprom_addr(0)) == ADDR_INFO_LOCK) || (mapped_addr - eeprom_addr(0)) == ADDR_CONF_OK) {
- // have to do a flush because we're only writing 1 byte and it syncs after 4
- eeprom_flush();
- }
-
- if (written_bytes >= 4) {
- file.close();
- file.open(EEPROM_FILE, FILE_O_WRITE);
- written_bytes = 0;
+ #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
++ // each time is really slow
+ uint8_t read_byte;
+ void* read_byte_ptr = &read_byte;
+ file.seek(mapped_addr);
+ file.read(read_byte_ptr, 1);
+ file.seek(mapped_addr);
+ if (read_byte != byte) {
+ file.write(byte);
}
+ written_bytes++;
+ eeprom_flush();
#endif
}
uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
#endif
- #if PLATFORM == PLATFORM_AVR
- if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
- #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 || rval == PRODUCT_TDECK_V1 || rval == PRODUCT_TBEAM_S_V1) {
+ #if 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 || rval == PRODUCT_TDECK_V1 || rval == PRODUCT_TBEAM_S_V1) {
++ 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 || rval == PRODUCT_TDECK_V1 || rval == PRODUCT_TBEAM_S_V1 || rval == PRODUCT_H_W_PAPER) {
#elif PLATFORM == PLATFORM_NRF52
- if (rval == PRODUCT_TECHO || rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW || rval == PRODUCT_OPENCOM_XL) {
- if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HELTEC_T114 || rval == PRODUCT_TECHO || rval == PRODUCT_HMBRW) {
++ if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HELTEC_T114 || rval == PRODUCT_OPENCOM_XL || rval == PRODUCT_TECHO || 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_H_W_PAPER
++ if (model == MODEL_C8) {
+ #elif BOARD_MODEL == BOARD_HELTEC_T114
+ if (model == MODEL_C6 || model == MODEL_C7) {
#elif BOARD_MODEL == BOARD_RAK4631
- if (model == MODEL_11 || model == MODEL_12) {
+ if (model == MODEL_11 || model == MODEL_12 || model == MODEL_13 || model == MODEL_14) {
+ #elif BOARD_MODEL == BOARD_OPENCOM_XL
+ if (model == MODEL_21) {
#elif BOARD_MODEL == BOARD_HUZZAH32
if (model == MODEL_FF) {
+ #elif BOARD_MODEL == BOARD_HMBRW
+ if (model == MODEL_FF || model == MODEL_FE) {
#elif BOARD_MODEL == BOARD_GENERIC_ESP32
if (model == MODEL_FF || model == MODEL_FE) {
#else
board_manager:
additional_urls:
- - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
+ - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
+ - https://liberatedsystems.co.uk/rnode-firmware-ce/esp-custom-package.json
- https://raw.githubusercontent.com/RAKwireless/RAKwireless-Arduino-BSP-Index/main/package_rakwireless_index.json
- - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
- - http://unsigned.io/arduino/package_unsignedio_UnsignedBoards_index.json
+ - https://github.com/HelTecAutomation/Heltec_nRF52/releases/download/1.7.0/package_heltec_nrf_index.json
--- /dev/null
- // This class is for BLE serial functionality on ESP32 boards ONLY
-
+// Copyright (C) 2024, Mark Qvist
+
+// 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 <https://www.gnu.org/licenses/>.
+
+#include <Arduino.h>
+#include "../../Boards.h"
+
+#if PLATFORM != PLATFORM_NRF52
+#if HAS_BLE
+
+#include "BLESerial.h"
+
+uint32_t bt_passkey_callback();
+void bt_passkey_notify_callback(uint32_t passkey);
+bool bt_security_request_callback();
+void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result);
+bool bt_confirm_pin_callback(uint32_t pin);
+void bt_connect_callback(BLEServer *server);
+void bt_disconnect_callback(BLEServer *server);
+bool bt_client_authenticated();
+
+uint32_t BLESerial::onPassKeyRequest() { return bt_passkey_callback(); }
+void BLESerial::onPassKeyNotify(uint32_t passkey) { bt_passkey_notify_callback(passkey); }
+bool BLESerial::onSecurityRequest() { return bt_security_request_callback(); }
+void BLESerial::onAuthenticationComplete(esp_ble_auth_cmpl_t auth_result) { bt_authentication_complete_callback(auth_result); }
+void BLESerial::onConnect(BLEServer *server) { bt_connect_callback(server); }
+void BLESerial::onDisconnect(BLEServer *server) { bt_disconnect_callback(server); ble_server->startAdvertising(); }
+bool BLESerial::onConfirmPIN(uint32_t pin) { return bt_confirm_pin_callback(pin); };
+bool BLESerial::connected() { return ble_server->getConnectedCount() > 0; }
+
+int BLESerial::read() {
+ int result = this->rx_buffer.pop();
+ if (result == '\n') { this->numAvailableLines--; }
+ return result;
+}
+
+size_t BLESerial::readBytes(uint8_t *buffer, size_t bufferSize) {
+ int i = 0;
+ while (i < bufferSize && available()) { buffer[i] = (uint8_t)this->rx_buffer.pop(); i++; }
+ return i;
+}
+
+int BLESerial::peek() {
+ if (this->rx_buffer.getLength() == 0) return -1;
+ return this->rx_buffer.get(0);
+}
+
+int BLESerial::available() { return this->rx_buffer.getLength(); }
+
+size_t BLESerial::print(const char *str) {
+ if (ble_server->getConnectedCount() <= 0) return 0;
+ size_t written = 0; for (size_t i = 0; str[i] != '\0'; i++) { written += this->write(str[i]); }
+ flush();
+
+ return written;
+}
+
+size_t BLESerial::write(const uint8_t *buffer, size_t bufferSize) {
+ if (ble_server->getConnectedCount() <= 0) { return 0; } else {
+ size_t written = 0; for (int i = 0; i < bufferSize; i++) { written += this->write(buffer[i]); }
+ flush();
+
+ return written;
+ }
+}
+
+size_t BLESerial::write(uint8_t byte) {
+ if (bt_client_authenticated()) {
+ if (ble_server->getConnectedCount() <= 0) { return 0; } else {
+ this->transmitBuffer[this->transmitBufferLength] = byte;
+ this->transmitBufferLength++;
+ if (this->transmitBufferLength == maxTransferSize) { flush(); }
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+void BLESerial::flush() {
+ if (this->transmitBufferLength > 0) {
+ TxCharacteristic->setValue(this->transmitBuffer, this->transmitBufferLength);
+ this->transmitBufferLength = 0;
+ this->lastFlushTime = millis();
+ TxCharacteristic->notify(true);
+ }
+}
+
+void BLESerial::begin(const char *name) {
+ ConnectedDeviceCount = 0;
+ BLEDevice::init(name);
+
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN ,ESP_PWR_LVL_P9);
+
+ ble_server = BLEDevice::createServer();
+ ble_server->setCallbacks(this);
+ BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_MITM);
+ BLEDevice::setSecurityCallbacks(this);
+
+ SetupSerialService();
+
+ ble_adv = BLEDevice::getAdvertising();
+ ble_adv->addServiceUUID(BLE_SERIAL_SERVICE_UUID);
+ ble_adv->setMinPreferred(0x20);
+ ble_adv->setMaxPreferred(0x40);
+ ble_adv->setScanResponse(true);
+ ble_adv->start();
+}
+
+void BLESerial::end() { BLEDevice::deinit(); }
+
+void BLESerial::onWrite(BLECharacteristic *characteristic) {
+ if (characteristic->getUUID().toString() == BLE_RX_UUID) {
+ auto value = characteristic->getValue();
+ for (int i = 0; i < value.length(); i++) { rx_buffer.push(value[i]); }
+ }
+}
+
+void BLESerial::SetupSerialService() {
+ SerialService = ble_server->createService(BLE_SERIAL_SERVICE_UUID);
+
+ RxCharacteristic = SerialService->createCharacteristic(BLE_RX_UUID, BLECharacteristic::PROPERTY_WRITE);
+ RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM);
+ RxCharacteristic->addDescriptor(new BLE2902());
+ RxCharacteristic->setWriteProperty(true);
+ RxCharacteristic->setCallbacks(this);
+
+ TxCharacteristic = SerialService->createCharacteristic(BLE_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
+ TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM);
+ TxCharacteristic->addDescriptor(new BLE2902());
+ TxCharacteristic->setNotifyProperty(true);
+ TxCharacteristic->setReadProperty(true);
+
+ SerialService->start();
+}
+
+BLESerial::BLESerial() { }
+
+#endif
+#endif
--- /dev/null
- // This class is for BLE serial functionality on ESP32 boards ONLY
-
+// Copyright (C) 2024, Mark Qvist
+
+// 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 <https://www.gnu.org/licenses/>.
+
+#include "../../Boards.h"
+
+#if PLATFORM != PLATFORM_NRF52
+#if HAS_BLE
+
+#include <Arduino.h>
+
+#include <BLEDevice.h>
+#include <BLEUtils.h>
+#include <BLEServer.h>
+#include <BLE2902.h>
+
+template <size_t n>
+class BLEFIFO {
+private:
+ uint8_t buffer[n];
+ int head = 0;
+ int tail = 0;
+
+public:
+ void push(uint8_t value) {
+ buffer[head] = value;
+ head = (head + 1) % n;
+ if (head == tail) { tail = (tail + 1) % n; }
+ }
+
+ int pop() {
+ if (head == tail) {
+ return -1;
+ } else {
+ uint8_t value = buffer[tail];
+ tail = (tail + 1) % n;
+ return value;
+ }
+ }
+
+ void clear() { head = 0; tail = 0; }
+
+ int get(size_t index) {
+ if (index >= this->getLength()) {
+ return -1;
+ } else {
+ return buffer[(tail + index) % n];
+ }
+ }
+
+ size_t getLength() {
+ if (head >= tail) {
+ return head - tail;
+ } else {
+ return n - tail + head;
+ }
+ }
+};
+
+#define RX_BUFFER_SIZE 6144
+#define BLE_BUFFER_SIZE 512 // Must fit in max GATT attribute length
+#define MIN_MTU 50
+
+class BLESerial : public BLECharacteristicCallbacks, public BLEServerCallbacks, public BLESecurityCallbacks, public Stream {
+public:
+ BLESerial();
+
+ void begin(const char *name);
+ void end();
+ void onWrite(BLECharacteristic *characteristic);
+ int available();
+ int peek();
+ int read();
+ size_t readBytes(uint8_t *buffer, size_t bufferSize);
+ size_t write(uint8_t byte);
+ size_t write(const uint8_t *buffer, size_t bufferSize);
+ size_t print(const char *value);
+ void flush();
+ void onConnect(BLEServer *server);
+ void onDisconnect(BLEServer *server);
+
+ uint32_t onPassKeyRequest();
+ void onPassKeyNotify(uint32_t passkey);
+ bool onSecurityRequest();
+ void onAuthenticationComplete(esp_ble_auth_cmpl_t);
+ bool onConfirmPIN(uint32_t pin);
+
+ bool connected();
+
+ BLEServer *ble_server;
+ BLEAdvertising *ble_adv;
+ BLEService *SerialService;
+ BLECharacteristic *TxCharacteristic;
+ BLECharacteristic *RxCharacteristic;
+ size_t transmitBufferLength;
+ unsigned long long lastFlushTime;
+
+private:
+ BLESerial(BLESerial const &other) = delete;
+ void operator=(BLESerial const &other) = delete;
+
+ BLEFIFO<RX_BUFFER_SIZE> rx_buffer;
+ size_t numAvailableLines;
+ uint8_t transmitBuffer[BLE_BUFFER_SIZE];
+
+ int ConnectedDeviceCount;
+ void SetupSerialService();
+
+ uint16_t peerMTU;
+ uint16_t maxTransferSize = BLE_BUFFER_SIZE;
+
+ bool checkMTU();
+
+ const char *BLE_SERIAL_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
+ const char *BLE_RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
+ const char *BLE_TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
+
+ bool started = false;
+};
+
+#endif
+#endif
--- /dev/null
--- /dev/null
++/**
++ * The MIT License (MIT)
++ *
++ * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
++ * Copyright (c) 2018 by Fabrice Weinberg
++ * Copyright (c) 2024 by Heltec AutoMation
++ * 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.
++ *
++ * ThingPulse invests considerable time and money to develop these open source libraries.
++ * Please support us by buying our products (and not the clones) from
++ * https://thingpulse.com
++ *
++ */
++
++#ifndef ST7789Spi_h
++#define ST7789Spi_h
++
++#include "OLEDDisplay.h"
++#include <SPI.h>
++
++
++#define ST_CMD_DELAY 0x80 // special signifier for command lists
++
++#define ST77XX_NOP 0x00
++#define ST77XX_SWRESET 0x01
++#define ST77XX_RDDID 0x04
++#define ST77XX_RDDST 0x09
++
++#define ST77XX_SLPIN 0x10
++#define ST77XX_SLPOUT 0x11
++#define ST77XX_PTLON 0x12
++#define ST77XX_NORON 0x13
++
++#define ST77XX_INVOFF 0x20
++#define ST77XX_INVON 0x21
++#define ST77XX_DISPOFF 0x28
++#define ST77XX_DISPON 0x29
++#define ST77XX_CASET 0x2A
++#define ST77XX_RASET 0x2B
++#define ST77XX_RAMWR 0x2C
++#define ST77XX_RAMRD 0x2E
++
++#define ST77XX_PTLAR 0x30
++#define ST77XX_TEOFF 0x34
++#define ST77XX_TEON 0x35
++#define ST77XX_MADCTL 0x36
++#define ST77XX_COLMOD 0x3A
++
++#define ST77XX_MADCTL_MY 0x80
++#define ST77XX_MADCTL_MX 0x40
++#define ST77XX_MADCTL_MV 0x20
++#define ST77XX_MADCTL_ML 0x10
++#define ST77XX_MADCTL_RGB 0x00
++
++#define ST77XX_RDID1 0xDA
++#define ST77XX_RDID2 0xDB
++#define ST77XX_RDID3 0xDC
++#define ST77XX_RDID4 0xDD
++
++// Some ready-made 16-bit ('565') color settings:
++#define ST77XX_BLACK 0x0000
++#define ST77XX_WHITE 0xFFFF
++#define ST77XX_RED 0xF800
++#define ST77XX_GREEN 0x07E0
++#define ST77XX_BLUE 0x001F
++#define ST77XX_CYAN 0x07FF
++#define ST77XX_MAGENTA 0xF81F
++#define ST77XX_YELLOW 0xFFE0
++#define ST77XX_ORANGE 0xFC00
++
++#define LED_A_ON LOW
++
++#ifdef ESP_PLATFORM
++#undef LED_A_ON
++#define LED_A_ON HIGH
++#define rtos_free free
++#define rtos_malloc malloc
++//SPIClass SPI1(HSPI);
++#endif
++class ST7789Spi : public OLEDDisplay {
++ private:
++ uint8_t _rst;
++ uint8_t _dc;
++ uint8_t _cs;
++ uint8_t _ledA;
++ int _miso;
++ int _mosi;
++ int _clk;
++ SPIClass * _spi;
++ SPISettings _spiSettings;
++ uint16_t _RGB=0xFFFF;
++ uint8_t _buffheight;
++ public:
++ /* pass _cs as -1 to indicate "do not use CS pin", for cases where it is hard wired low */
++ ST7789Spi(SPIClass *spiClass,uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_RAWMODE,uint16_t width=240,uint16_t height=320,int mosi=-1,int miso=-1,int clk=-1) {
++ this->_spi = spiClass;
++ this->_rst = _rst;
++ this->_dc = _dc;
++ this->_cs = _cs;
++ this->_mosi=mosi;
++ this->_miso=miso;
++ this->_clk=clk;
++ //this->_ledA = _ledA;
++ _spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0);
++ setGeometry(g,width,height);
++ }
++
++ bool connect(){
++ this->_buffheight=displayHeight / 8;
++ this->_buffheight+=displayHeight % 8 ? 1:0;
++ pinMode(_cs, OUTPUT);
++ pinMode(_dc, OUTPUT);
++ //pinMode(_ledA, OUTPUT);
++ if (_cs != (uint8_t) -1) {
++ pinMode(_cs, OUTPUT);
++ }
++ pinMode(_rst, OUTPUT);
++
++#ifdef ESP_PLATFORM
++ _spi->begin(_clk,_miso,_mosi,-1);
++#else
++ _spi->begin();
++#endif
++ _spi->setClockDivider (SPI_CLOCK_DIV2);
++
++ // Pulse Reset low for 10ms
++ digitalWrite(_rst, HIGH);
++ delay(1);
++ digitalWrite(_rst, LOW);
++ delay(10);
++ digitalWrite(_rst, HIGH);
++ _spi->begin ();
++ //digitalWrite(_ledA, LED_A_ON);
++ return true;
++ }
++
++ void display(void) {
++ #ifdef OLEDDISPLAY_DOUBLE_BUFFER
++
++ uint16_t minBoundY = UINT16_MAX;
++ uint16_t maxBoundY = 0;
++
++ uint16_t minBoundX = UINT16_MAX;
++ uint16_t maxBoundX = 0;
++
++ uint16_t x, y;
++
++ // Calculate the Y bounding box of changes
++ // and copy buffer[pos] to buffer_back[pos];
++ for (y = 0; y < _buffheight; y++) {
++ for (x = 0; x < displayWidth; x++) {
++ //Serial.printf("x %d y %d\r\n",x,y);
++ uint16_t pos = x + y * displayWidth;
++ if (buffer[pos] != buffer_back[pos]) {
++ minBoundY = min(minBoundY, y);
++ maxBoundY = max(maxBoundY, y);
++ minBoundX = min(minBoundX, x);
++ maxBoundX = max(maxBoundX, x);
++ }
++ buffer_back[pos] = buffer[pos];
++ }
++ yield();
++ }
++
++ // If the minBoundY wasn't updated
++ // we can savely assume that buffer_back[pos] == buffer[pos]
++ // holdes true for all values of pos
++ if (minBoundY == UINT16_MAX) return;
++
++ set_CS(LOW);
++ _spi->beginTransaction(_spiSettings);
++
++ for (y = minBoundY; y <= maxBoundY; y++)
++ {
++ for(int temp = 0; temp<8;temp++)
++ {
++ //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
++ setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
++ //setAddrWindow(y*8+temp,minBoundX,1,maxBoundX-minBoundX+1);
++ uint32_t const pixbufcount = maxBoundX-minBoundX+1;
++ uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount);
++ for (x = minBoundX; x <= maxBoundX; x++)
++ {
++ pixbuf[x-minBoundX] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0;
++ }
++#ifdef ESP_PLATFORM
++ _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount);
++#else
++ _spi->transfer(pixbuf, NULL, 2 * pixbufcount);
++#endif
++ rtos_free(pixbuf);
++ }
++ }
++ _spi->endTransaction();
++ set_CS(HIGH);
++
++ #else
++ set_CS(LOW);
++ _spi->beginTransaction(_spiSettings);
++ uint8_t x, y;
++ for (y = 0; y < _buffheight; y++)
++ {
++ for(int temp = 0; temp<8;temp++)
++ {
++ //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
++ //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
++ setAddrWindow(y*8+temp,0,1,displayWidth);
++ uint32_t const pixbufcount = displayWidth;
++ uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount);
++ for (x = 0; x < displayWidth; x++)
++ {
++ pixbuf[x] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0;
++ }
++#ifdef ESP_PLATFORM
++ _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount);
++#else
++ _spi->transfer(pixbuf, NULL, 2 * pixbufcount);
++#endif
++ rtos_free(pixbuf);
++ }
++ }
++ _spi->endTransaction();
++ set_CS(HIGH);
++
++ #endif
++ }
++
++ virtual void resetOrientation() {
++ uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
++ sendCommand(ST77XX_MADCTL);
++ WriteData(madctl);
++ delay(10);
++ }
++
++ virtual void flipScreenVertically() {
++ uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY;
++ sendCommand(ST77XX_MADCTL);
++ WriteData(madctl);
++ delay(10);
++ }
++
++ virtual void mirrorScreen() {
++ uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX|ST77XX_MADCTL_MY;
++ sendCommand(ST77XX_MADCTL);
++ WriteData(madctl);
++ delay(10);
++ }
++
++ virtual void setRotation(uint8_t r) {
++ uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
++ if (r == 1) { madctl = 0xC0; }
++ if (r == 2) { madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; }
++ if (r == 3) { madctl = 0x00; }
++ sendCommand(ST77XX_MADCTL);
++ WriteData(madctl);
++ delay(10);
++ }
++
++ void setRGB(uint16_t c)
++ {
++
++ this->_RGB=0x00|c>>8|c<<8&0xFF00;
++ }
++
++ void displayOn(void) {
++ //sendCommand(DISPLAYON);
++ }
++
++ void displayOff(void) {
++ //sendCommand(DISPLAYOFF);
++ }
++
++//#define ST77XX_MADCTL_MY 0x80
++//#define ST77XX_MADCTL_MX 0x40
++//#define ST77XX_MADCTL_MV 0x20
++//#define ST77XX_MADCTL_ML 0x10
++ protected:
++ // Send all the init commands
++ virtual void sendInitCommands()
++ {
++ sendCommand(ST77XX_SWRESET); // 1: Software reset, no args, w/delay
++ delay(150);
++
++ sendCommand(ST77XX_SLPOUT); // 2: Out of sleep mode, no args, w/delay
++ delay(10);
++
++ sendCommand(ST77XX_COLMOD); // 3: Set color mode, 16-bit color
++ WriteData(0x55);
++ delay(10);
++
++ sendCommand(ST77XX_MADCTL); // 4: Mem access ctrl (directions), Row/col addr, bottom-top refresh
++ WriteData(0x08);
++
++ sendCommand(ST77XX_CASET); // 5: Column addr set,
++ WriteData(0x00);
++ WriteData(0x00); // XSTART = 0
++ WriteData(0x00);
++ WriteData(240); // XEND = 240
++
++ sendCommand(ST77XX_RASET); // 6: Row addr set,
++ WriteData(0x00);
++ WriteData(0x00); // YSTART = 0
++ WriteData(320>>8);
++ WriteData(320&0xFF); // YSTART = 320
++
++ sendCommand(ST77XX_SLPOUT); // 7: hack
++ delay(10);
++
++ sendCommand(ST77XX_NORON); // 8: Normal display on, no args, w/delay
++ delay(10);
++
++ sendCommand(ST77XX_DISPON); // 9: Main screen turn on, no args, delay
++ delay(10);
++
++ sendCommand(ST77XX_INVON); // 10: invert
++ delay(10);
++
++ //uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MX;
++ uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
++ sendCommand(ST77XX_MADCTL);
++ WriteData(madctl);
++ delay(10);
++ setRGB(ST77XX_GREEN);
++ }
++
++
++ private:
++
++ void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
++ x += (320-displayWidth)/2;
++ y += (240-displayHeight)/2;
++ uint32_t xa = ((uint32_t)x << 16) | (x + w - 1);
++ uint32_t ya = ((uint32_t)y << 16) | (y + h - 1);
++
++ writeCommand(ST77XX_CASET); // Column addr set
++ SPI_WRITE32(xa);
++
++ writeCommand(ST77XX_RASET); // Row addr set
++ SPI_WRITE32(ya);
++
++ writeCommand(ST77XX_RAMWR); // write to RAM
++ }
++ int getBufferOffset(void) {
++ return 0;
++ }
++ inline void set_CS(bool level) {
++ if (_cs != (uint8_t) -1) {
++ digitalWrite(_cs, level);
++ }
++ };
++ inline void sendCommand(uint8_t com) __attribute__((always_inline)){
++ set_CS(HIGH);
++ digitalWrite(_dc, LOW);
++ set_CS(LOW);
++ _spi->beginTransaction(_spiSettings);
++ _spi->transfer(com);
++ _spi->endTransaction();
++ set_CS(HIGH);
++ digitalWrite(_dc, HIGH);
++ }
++
++ inline void WriteData(uint8_t data) __attribute__((always_inline)){
++ digitalWrite(_cs, LOW);
++ _spi->beginTransaction(_spiSettings);
++ _spi->transfer(data);
++ _spi->endTransaction();
++ digitalWrite(_cs, HIGH);
++ }
++ void SPI_WRITE32(uint32_t l)
++ {
++ _spi->transfer(l >> 24);
++ _spi->transfer(l >> 16);
++ _spi->transfer(l >> 8);
++ _spi->transfer(l);
++ }
++ void writeCommand(uint8_t cmd) {
++ digitalWrite(_dc, LOW);
++ _spi->transfer(cmd);
++ digitalWrite(_dc, HIGH);
++ }
++
++// Private functions
++ void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) {
++ this->geometry = g;
++
++ switch (g) {
++ case GEOMETRY_128_128:
++ this->displayWidth = 128;
++ this->displayHeight = 128;
++ break;
++ case GEOMETRY_128_64:
++ this->displayWidth = 128;
++ this->displayHeight = 64;
++ break;
++ case GEOMETRY_128_32:
++ this->displayWidth = 128;
++ this->displayHeight = 32;
++ break;
++ case GEOMETRY_64_48:
++ this->displayWidth = 64;
++ this->displayHeight = 48;
++ break;
++ case GEOMETRY_64_32:
++ this->displayWidth = 64;
++ this->displayHeight = 32;
++ break;
++ case GEOMETRY_RAWMODE:
++ this->displayWidth = width > 0 ? width : 128;
++ this->displayHeight = height > 0 ? height : 64;
++ break;
++ }
++ uint8_t tmp=displayHeight % 8;
++ uint8_t _buffheight=displayHeight / 8;
++
++ if(tmp!=0)
++ _buffheight++;
++ this->displayBufferSize = displayWidth * _buffheight ;
++ }
++
++
++
++};
++
++#endif