]> git.tnoah.ca Git - lora-radio.git/commitdiff
Update to RNode Firmware v1.78 (upstream)
authorjacob.eva <x00010@disroot.org>
Sat, 12 Oct 2024 17:04:19 +0000 (18:04 +0100)
committerjacob.eva <x00010@disroot.org>
Sat, 12 Oct 2024 17:04:19 +0000 (18:04 +0100)
28 files changed:
Bluetooth.h
Boards.h
Config.h
Console.h
Console/Makefile
Console/build.py
Device.h
Display.h
Framing.h
Graphics.h
MD5.cpp [deleted file]
MD5.h [deleted file]
Makefile
Power.h
README.md
RNode_Firmware_CE.ino
ROM.h
Radio.cpp
Radio.hpp
Release/console_image.bin
Utilities.h
esp32_btbufs.py [new file with mode: 0755]
partition_hashes
release_hashes.py
src/ble/BLESerial.cpp [new file with mode: 0644]
src/ble/BLESerial.h [new file with mode: 0644]
src/misc/MD5.cpp [new file with mode: 0644]
src/misc/MD5.h [new file with mode: 0644]

index 4761e24f212907cda8cf523b68eab97162ca1fa9..ee3964e081c9470d9eca3b5c142fb1b6c7e44040 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
@@ -22,8 +22,8 @@
   #elif HAS_BLE == true
     #include "esp_bt_main.h"
     #include "esp_bt_device.h"
-    // TODO: Remove
-    #define SerialBT Serial
+    #include "src/ble/BLESerial.h"
+    BLESerial SerialBT;
   #endif
 
 #elif MCU_VARIANT == MCU_NRF52
@@ -35,6 +35,7 @@
 #endif
 
 #define BT_PAIRING_TIMEOUT 35000
+#define BLE_FLUSH_TIMEOUT 20
 uint32_t bt_pairing_started = 0;
 
 #define BT_DEV_ADDR_LEN 6
@@ -58,6 +59,7 @@ char bt_devname[11];
     }
 
     void bt_stop() {
+      //display_unblank();
       if (bt_state != BT_STATE_OFF) {
         SerialBT.end();
         bt_allow_pairing = false;
@@ -66,6 +68,7 @@ char bt_devname[11];
     }
 
     void bt_start() {
+      //display_unblank();
       if (bt_state == BT_STATE_OFF) {
         SerialBT.begin(bt_devname);
         bt_state = BT_STATE_ON;
@@ -73,6 +76,7 @@ char bt_devname[11];
     }
 
     void bt_enable_pairing() {
+      //display_unblank();
       if (bt_state == BT_STATE_OFF) bt_start();
       bt_allow_pairing = true;
       bt_pairing_started = millis();
@@ -80,12 +84,14 @@ char bt_devname[11];
     }
 
     void bt_disable_pairing() {
+      //display_unblank();
       bt_allow_pairing = false;
       bt_ssp_pin = 0;
       bt_state = BT_STATE_ON;
     }
 
     void bt_pairing_complete(boolean success) {
+      //display_unblank();
       if (success) {
         bt_disable_pairing();
       } else {
@@ -93,7 +99,8 @@ char bt_devname[11];
       }
     }
 
-    void bt_connection_callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param){
+    void bt_connection_callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
+      //display_unblank();
       if(event == ESP_SPP_SRV_OPEN_EVT) {
         bt_state = BT_STATE_CONNECTED;
         cable_state = CABLE_STATE_DISCONNECTED;
@@ -156,25 +163,122 @@ char bt_devname[11];
     }
 
   #elif HAS_BLE == true
-    void bt_stop() {
-      if (bt_state != BT_STATE_OFF) {
-        bt_allow_pairing = false;
-        bt_state = BT_STATE_OFF;
-      }
-    }
+    BLESecurity *ble_security = new BLESecurity();
+    bool ble_authenticated = false;
+    uint32_t pairing_pin = 0;
+
+    void bt_flush() { if (bt_state == BT_STATE_CONNECTED) { SerialBT.flush(); } }
 
     void bt_disable_pairing() {
+      //display_unblank();
       bt_allow_pairing = false;
       bt_ssp_pin = 0;
       bt_state = BT_STATE_ON;
     }
 
-    void bt_connect_callback(uint16_t conn_handle) {
+    void bt_passkey_notify_callback(uint32_t passkey) {
+      // Serial.printf("Got passkey notification: %d\n", passkey);
+      bt_ssp_pin = passkey;
+      bt_state = BT_STATE_PAIRING;
+      bt_allow_pairing = true;
+      bt_pairing_started = millis();
+      kiss_indicate_btpin();
+    }
+
+    bool bt_confirm_pin_callback(uint32_t pin) {
+      // Serial.printf("Confirm PIN callback: %d\n", pin);
+      return true;
+    }
+
+    void bt_debond_all() {
+      // Serial.println("Debonding all");
+      int dev_num = esp_ble_get_bond_device_num();
+      esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
+      esp_ble_get_bond_device_list(&dev_num, dev_list);
+      for (int i = 0; i < dev_num; i++) { esp_ble_remove_bond_device(dev_list[i].bd_addr); }
+      free(dev_list);
+    }
+
+    void bt_update_passkey() {
+      // Serial.println("Updating passkey");
+      pairing_pin = random(899999)+100000;
+      bt_ssp_pin = pairing_pin;
+    }
+
+    uint32_t bt_passkey_callback() {
+      // Serial.println("API passkey request");
+      if (pairing_pin == 0) { bt_update_passkey(); }
+      return pairing_pin;
+    }
+
+    bool bt_client_authenticated() {
+      return ble_authenticated;
+    }
+
+    void bt_security_setup() {
+      uint32_t passkey = bt_passkey_callback();
+
+      // Serial.printf("Executing BT security setup, passkey is %d\n", passkey);
+
+      uint8_t key_size = 16;
+      uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
+      uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
+
+      esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
+      uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE;
+      uint8_t oob_support = ESP_BLE_OOB_DISABLE;
+
+      esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT;
+
+      esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
+      esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
+    }
+
+    bool bt_security_request_callback() {
+      if (bt_allow_pairing) {
+          // Serial.println("Accepting security request");
+          return true;
+        } else {
+          // Serial.println("Rejecting security request");
+          return false;
+        }
+    }
+
+    void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result) {
+      if (auth_result.success == true) {
+        // Serial.println("Authentication success");
+        ble_authenticated = true;
+        bt_state = BT_STATE_CONNECTED;
+      } else {
+        // Serial.println("Authentication fail");
+        ble_authenticated = false;
+        bt_state = BT_STATE_ON;
+        bt_security_setup();
+      }
+      bt_allow_pairing = false;
+      bt_ssp_pin = 0;
+    }
+
+    void bt_connect_callback(BLEServer *server) {
+      // uint16_t conn_id = server->getConnId();
+      // Serial.printf("Connected: %d\n", conn_id);
+      //display_unblank();
+      ble_authenticated = false;
       bt_state = BT_STATE_CONNECTED;
       cable_state = CABLE_STATE_DISCONNECTED;
     }
 
-    void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) {
+    void bt_disconnect_callback(BLEServer *server) {
+      // uint16_t conn_id = server->getConnId();
+      // Serial.printf("Disconnected: %d\n", conn_id);
+      //display_unblank();
+      ble_authenticated = false;
       bt_state = BT_STATE_ON;
     }
 
@@ -199,8 +303,8 @@ char bt_devname[11];
               sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
               free(data);
 
-              // TODO: Implement GAP & GATT for RNode comms over BLE
-              
+              bt_security_setup();
+
               bt_ready = true;
               return true;
 
@@ -211,9 +315,20 @@ char bt_devname[11];
     }
 
     void bt_start() {
+      //display_unblank();
       if (bt_state == BT_STATE_OFF) {
         bt_state = BT_STATE_ON;
-        // TODO: Implement
+        SerialBT.begin(bt_devname);
+        SerialBT.setTimeout(10);
+      }
+    }
+
+    void bt_stop() {
+      //display_unblank();
+      if (bt_state != BT_STATE_OFF) {
+        bt_allow_pairing = false;
+        bt_state = BT_STATE_OFF;
+        SerialBT.end();
       }
     }
 
@@ -228,7 +343,13 @@ char bt_devname[11];
     }
 
     void bt_enable_pairing() {
+      //display_unblank();
       if (bt_state == BT_STATE_OFF) bt_start();
+
+      bt_security_setup();
+      //bt_debond_all();
+      //bt_update_passkey();
+
       bt_allow_pairing = true;
       bt_pairing_started = millis();
       bt_state = BT_STATE_PAIRING;
@@ -238,26 +359,31 @@ char bt_devname[11];
       if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
         bt_disable_pairing();
       }
+      if (bt_state == BT_STATE_CONNECTED && millis()-SerialBT.lastFlushTime >= BLE_FLUSH_TIMEOUT) {
+        if (SerialBT.transmitBufferLength > 0) {
+          bt_flush();
+        }
+      }
     }
   #endif
 
 #elif MCU_VARIANT == MCU_NRF52
-uint8_t eeprom_read(uint32_t mapped_addr);
+  uint8_t eeprom_read(uint32_t mapped_addr);
 
-void bt_stop() {
-  if (bt_state != BT_STATE_OFF) {
-    bt_allow_pairing = false;
-    bt_state = BT_STATE_OFF;
+  void bt_stop() {
+    if (bt_state != BT_STATE_OFF) {
+      bt_allow_pairing = false;
+      bt_state = BT_STATE_OFF;
+    }
   }
-}
 
-void bt_disable_pairing() {
-  bt_allow_pairing = false;
-  bt_ssp_pin = 0;
-  bt_state = BT_STATE_ON;
-}
+  void bt_disable_pairing() {
+    bt_allow_pairing = false;
+    bt_ssp_pin = 0;
+    bt_state = BT_STATE_ON;
+  }
 
-void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
+  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);
 
@@ -290,24 +416,23 @@ void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
     } else {
       bt_ssp_pin = 0;
     }
-}
+  }
 
-bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
+  bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
     for (int i = 0; i < 6; i++) {
-        // multiply by tens however many times needed to make numbers appear in order
-        bt_ssp_pin += ((int)passkey[i] - 48) * pow(10, 5-i);
+      // multiply by tens however many times needed to make numbers appear in order
+      bt_ssp_pin += ((int)passkey[i] - 48) * pow(10, 5-i);
     }
     kiss_indicate_btpin();
     if (bt_allow_pairing) {
-        return true;
+      return true;
     }
     return false;
-}
+  }
 
-void bt_connect_callback(uint16_t conn_handle) {
+  void bt_connect_callback(uint16_t conn_handle) {
     bt_state = BT_STATE_CONNECTED;
     cable_state = CABLE_STATE_DISCONNECTED;
-
     BLEConnection* conn = Bluefruit.Connection(conn_handle);
     conn->requestPHY(BLE_GAP_PHY_2MBPS);
     conn->requestMtuExchange(512+3);
@@ -320,89 +445,87 @@ void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) {
     }
 }
 
-bool bt_setup_hw() {
-  if (!bt_ready) {
-    #if HAS_EEPROM 
-        if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
-    #else
-        if (eeprom_read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
-    #endif
-      bt_enabled = true;
-    } else {
-      bt_enabled = false;
-    }
-    Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
-    Bluefruit.autoConnLed(false);
-    if (Bluefruit.begin()) {
-      Bluefruit.setTxPower(8);    // Check bluefruit.h for supported values
-      Bluefruit.Security.setIOCaps(true, false, false); // display, yes; yes / no, no; keyboard, no
-      // This device is indeed capable of yes / no through the pairing mode
-      // being set, but I have chosen to set it thus to force the input of the
-      // pin on the device initiating the pairing. This prevents it from being
-      // paired with automatically by a hypothetical malicious device nearby
-      // without physical access to the RNode.
-
-      Bluefruit.Security.setMITM(true);
-      Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback);
-      Bluefruit.Security.setSecuredCallback(bt_connect_callback);
-      Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback);
-      Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete);
-      Bluefruit.Periph.setConnInterval(6, 12); // 7.5 - 15 ms
-
-      const ble_gap_addr_t gap_addr = Bluefruit.getAddr();
-      char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
-      for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
-          data[i] = gap_addr.addr[i];
-      }
+  bool bt_setup_hw() {
+    if (!bt_ready) {
       #if HAS_EEPROM 
-          data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
+          if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
       #else
-          data[BT_DEV_ADDR_LEN] = eeprom_read(eeprom_addr(ADDR_SIGNATURE));
+          if (eeprom_read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
       #endif
-      unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
-      memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
-      sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
-      free(data);
-
-      bt_ready = true;
-      return true;
+        bt_enabled = true;
+      } else {
+        bt_enabled = false;
+      }
+      Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+      Bluefruit.autoConnLed(false);
+      if (Bluefruit.begin()) {
+        Bluefruit.setTxPower(8);    // Check bluefruit.h for supported values
+        Bluefruit.Security.setIOCaps(true, false, false); // display, yes; yes / no, no; keyboard, no
+        // This device is indeed capable of yes / no through the pairing mode
+        // being set, but I have chosen to set it thus to force the input of the
+        // pin on the device initiating the pairing.
+
+        Bluefruit.Security.setMITM(true);
+        Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback);
+        Bluefruit.Security.setSecuredCallback(bt_connect_callback);
+        Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback);
+        Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete);
+        Bluefruit.Periph.setConnInterval(6, 12); // 7.5 - 15 ms
+
+        const ble_gap_addr_t gap_addr = Bluefruit.getAddr();
+        char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
+        for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
+            data[i] = gap_addr.addr[i];
+        }
+        #if HAS_EEPROM 
+            data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
+        #else
+            data[BT_DEV_ADDR_LEN] = eeprom_read(eeprom_addr(ADDR_SIGNATURE));
+        #endif
+        unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
+        memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
+        sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
+        free(data);
+
+        bt_ready = true;
+        return true;
 
+      } else { return false; }
     } else { return false; }
-  } else { return false; }
-}
+  }
 
-void bt_start() {
-  if (bt_state == BT_STATE_OFF) {
-    Bluefruit.setName(bt_devname);
-    bledis.setManufacturer(BLE_MANUFACTURER);
-    bledis.setModel(BLE_MODEL);
-    // start device information service
-    bledis.begin();
+  void bt_start() {
+    if (bt_state == BT_STATE_OFF) {
+      Bluefruit.setName(bt_devname);
+      bledis.setManufacturer(BLE_MANUFACTURER);
+      bledis.setModel(BLE_MODEL);
+      // start device information service
+      bledis.begin();
 
-    SerialBT.bufferTXD(true); // enable buffering
+      SerialBT.bufferTXD(true); // enable buffering
 
-    SerialBT.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // enable encryption for BLE serial
-    SerialBT.begin();
+      SerialBT.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // enable encryption for BLE serial
+      SerialBT.begin();
 
-    blebas.begin();
+      blebas.begin();
 
-    Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
-    Bluefruit.Advertising.addTxPower();
+      Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+      Bluefruit.Advertising.addTxPower();
 
-    // Include bleuart 128-bit uuid
-    Bluefruit.Advertising.addService(SerialBT);
+      // Include bleuart 128-bit uuid
+      Bluefruit.Advertising.addService(SerialBT);
 
-    // There is no room for Name in Advertising packet
-    // Use Scan response for Name
-    Bluefruit.ScanResponse.addName();
+      // There is no room for Name in Advertising packet
+      // Use Scan response for Name
+      Bluefruit.ScanResponse.addName();
 
-    Bluefruit.Advertising.start(0);
+      Bluefruit.Advertising.start(0);
 
-    bt_state = BT_STATE_ON;
-   }
-}
+      bt_state = BT_STATE_ON;
+     }
+  }
 
-bool bt_init() {
+  bool bt_init() {
     bt_state = BT_STATE_OFF;
     if (bt_setup_hw()) {
       if (bt_enabled && !console_active) bt_start();
@@ -410,18 +533,18 @@ bool bt_init() {
     } else {
       return false;
     }
-}
+  }
 
-void bt_enable_pairing() {
-  if (bt_state == BT_STATE_OFF) bt_start();
-  bt_allow_pairing = true;
-  bt_pairing_started = millis();
-  bt_state = BT_STATE_PAIRING;
-}
+  void bt_enable_pairing() {
+    if (bt_state == BT_STATE_OFF) bt_start();
+    bt_allow_pairing = true;
+    bt_pairing_started = millis();
+    bt_state = BT_STATE_PAIRING;
+  }
 
-void update_bt() {
-  if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
-    bt_disable_pairing();
+  void update_bt() {
+    if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
+      bt_disable_pairing();
+    }
   }
-}
 #endif
index 12e878386394073eead3c93c15c35a50354b9f46..481866d169b83aa6e9fab698b95566854361fcc8 100644 (file)
--- a/Boards.h
+++ b/Boards.h
   #define MCU_ESP32           0x81
   #define MCU_NRF52           0x71
 
-  // Boards
-  #define BOARD_RNODE         0x31
-  #define BOARD_HMBRW         0x32
+  // Products, boards and models. Grouped by manufacturer.
+  // Below are the original RNodes, sold by Mark Qvist.
+  #define PRODUCT_RNODE       0x03 // RNode devices
+  #define BOARD_RNODE         0x31 // Original v1.0 RNode
+  #define MODEL_A4            0xA4 // RNode v1.0, 433 MHz
+  #define MODEL_A9            0xA9 // RNode v1.0, 868 MHz
+
+  #define BOARD_RNODE_NG_20   0x40 // RNode hardware revision v2.0
+  #define MODEL_A3            0xA3 // RNode v2.0, 433 MHz
+  #define MODEL_A8            0xA8 // RNode v2.0, 868 MHz
+
+  #define BOARD_RNODE_NG_21   0x41 // RNode hardware revision v2.1
+  #define MODEL_A2            0xA2 // RNode v2.1, 433 MHz
+  #define MODEL_A7            0xA7 // RNode v2.1, 868 MHz
+
+  #define BOARD_RNODE_NG_22   0x42 // RNode hardware revision v2.2 (T3S3)
+  #define MODEL_A1            0xA1 // RNode v2.2, 433 MHz with SX1268
+  #define MODEL_A5            0xA5 // RNode v2.2, 433 MHz with SX1278
+  #define MODEL_A6            0xA6 // RNode v2.2, 868 MHz with SX1262
+  #define MODEL_AA            0xAA // RNode v2.2, 868 MHz with SX1276
+
+  #define PRODUCT_TBEAM       0xE0 // T-Beam - sold by LilyGO
   #define BOARD_TBEAM         0x33
-  #define BOARD_HUZZAH32      0x34
-  #define BOARD_GENERIC_ESP32 0x35
+  #define MODEL_E4            0xE4 // T-Beam SX1278, 433 Mhz
+  #define MODEL_E9            0xE9 // T-Beam SX1276, 868 Mhz
+  #define MODEL_E3            0xE3 // T-Beam SX1268, 433 Mhz
+  #define MODEL_E8            0xE8 // T-Beam SX1262, 868 Mhz
+
+  #define PRODUCT_TDECK_V1    0xD0 // T-Deck - sold by LilyGO
+  #define BOARD_TDECK         0x3B
+  #define MODEL_D4            0xD4 // LilyGO T-Deck, 433 MHz
+  #define MODEL_D9            0xD9 // LilyGO T-Deck, 868 MHz
+
+  #define PRODUCT_TBEAM_S_V1  0xEA // T-Beam Supreme - sold by LilyGO
+  #define BOARD_TBEAM_S_V1    0x3D
+  #define MODEL_DB            0xDB // LilyGO T-Beam Supreme, 433 MHz
+  #define MODEL_DC            0xDC // LilyGO T-Beam Supreme, 868 MHz
+
+  #define PRODUCT_T32_10      0xB2 // T3 v1.0 - sold by LilyGO
+  #define BOARD_LORA32_V1_0   0x39
+  #define MODEL_BA            0xBA // LilyGO T3 v1.0, 433 MHz
+  #define MODEL_BB            0xBB // LilyGO T3 v1.0, 868 MHz
+
+  #define PRODUCT_T32_20      0xB0 // T3 v2.0 - sold by LilyGO
   #define BOARD_LORA32_V2_0   0x36
+  #define MODEL_B3            0xB3 // LilyGO T3 v2.0, 433 MHz
+  #define MODEL_B8            0xB8 // LilyGO T3 v2.0, 868 MHz
+
+  #define PRODUCT_T32_21      0xB1 // T3 v2.1 - sold by LilyGO
   #define BOARD_LORA32_V2_1   0x37
-  #define BOARD_LORA32_V1_0   0x39
+  #define MODEL_B4            0xB4 // LilyGO T3 v2.1, 433 MHz
+  #define MODEL_B9            0xB9 // LilyGO T3 v2.1, 868 MHz
+
+  #define BOARD_T3S3          0x42 // T3S3 - sold by LilyGO
+  #define MODEL_A1 0xA1            // T3S3 SX1262 868/915 MHz
+  #define MODEL_AB 0xAB            // T3S3 SX1276 868/915 MHz
+  #define MODEL_A5 0xA5            // T3S3 SX1280 PA (2.4GHz)
+
+  #define PRODUCT_TECHO 0x15       // LilyGO T-Echo devices
+  #define BOARD_TECHO         0x43
+  #define MODEL_16 0x16            // T-Echo 433 MHz
+  #define MODEL_17 0x17            // T-Echo 868/915 MHz
+
+
+  #define PRODUCT_H32_V2      0xC0 // LoRa32 v2 - sold by Heltec
   #define BOARD_HELTEC32_V2   0x38
+  #define MODEL_C4            0xC4 // Heltec Lora32 v2, 433 MHz
+  #define MODEL_C9            0xC9 // Heltec Lora32 v2, 868 MHz
+
+  #define PRODUCT_H32_V3      0xC1 // LoRa32 v3 - sold by Heltec
   #define BOARD_HELTEC32_V3   0x3A
-  #define BOARD_RNODE_NG_20   0x40
-  #define BOARD_RNODE_NG_21   0x41
-  #define BOARD_T3S3          0x42
-  #define BOARD_TECHO         0x43
-  #define BOARD_E22_ESP32     0x44
-  #define BOARD_GENERIC_NRF52 0x50
+  #define MODEL_C5            0xC5 // Heltec Lora32 v3, 433 MHz
+  #define MODEL_CA            0xCA // Heltec Lora32 v3, 868 MHz
+
+  #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 MODEL_21            0x21 // openCom XL, 868/915 MHz
+
+  #define BOARD_E22_ESP32     0x44 // 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_HMBRW       0xF0
+  #define BOARD_HMBRW         0x32
+  #define BOARD_HUZZAH32      0x34
+  #define BOARD_GENERIC_ESP32 0x35
+  #define BOARD_GENERIC_NRF52 0x50
+  #define MODEL_FE            0xFE // Homebrew board, max 17dBm output power
+  #define MODEL_FF            0xFF // Homebrew board, max 14dBm output power
 
   // Displays
   #define OLED 0x01
       #define DISPLAY OLED
       #define HAS_PMU true
       #define HAS_BLUETOOTH true
-      #define HAS_BLE true
       #define HAS_CONSOLE true
       #define HAS_SD false
       #define HAS_EEPROM true
       #define I2C_SCL 22
       #define PMU_IRQ 35
       #define INTERFACE_COUNT 1
+      #define HAS_INPUT true
+      const int pin_btn_usr1 = 38;
       const int pin_led_rx = 2;
       const int pin_led_tx = 4;
 
-      const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+      #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};
       const bool interface_cfg[INTERFACE_COUNT][3] = { 
                     // SX1262
           {
               -1  // pin_tcxo_enable
           }
       };
+      #endif
 
     #elif BOARD_MODEL == BOARD_HUZZAH32
       #define HAS_BLUETOOTH true
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_BLUETOOTH true
-      #define HAS_BLE true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
       #define INTERFACE_COUNT 1
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_BLUETOOTH true
-      #define HAS_BLE true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
       #define INTERFACE_COUNT 1
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_BLUETOOTH true
-      #define HAS_BLE true
       #define HAS_PMU true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
       #define INTERFACE_COUNT 1
+      #define HAS_INPUT true
+      #define HAS_SLEEP true
+      #define PIN_WAKEUP GPIO_NUM_0
+      #define WAKEUP_LEVEL 0
+
+      const int pin_btn_usr1 = 0;
+
       #if defined(EXTERNAL_LEDS)
         const int pin_led_rx = 36;
         const int pin_led_tx = 37;
       #define HAS_DISPLAY true
       #define HAS_BLUETOOTH false
       #define HAS_BLE true
-      #define HAS_CONSOLE false
+      #define HAS_PMU true
+      #define HAS_CONSOLE true
       #define HAS_EEPROM true
       #define HAS_INPUT true
       #define HAS_SLEEP true
 
     #elif BOARD_MODEL == BOARD_T3S3
       #define IS_ESP32S3 true
-
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_CONSOLE false
       const int SD_MOSI = 11;
       const int SD_CLK = 14;
       const int SD_CS = 13;
+
       #if HAS_NP == false
         #if defined(EXTERNAL_LEDS)
           const int pin_led_rx = 37;
               -1  // pin_tcxo_enable
           }
       };
-    #else
-      #error An unsupported ESP32 board was selected. Cannot compile RNode firmware.
-    #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
+      #define HAS_CONSOLE false
+      #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 true
+      #define PIN_WAKEUP GPIO_NUM_0
+      #define WAKEUP_LEVEL 0
+
+      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 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
+  #endif
+
   
   #elif MCU_VARIANT == MCU_NRF52
      #if BOARD_MODEL == BOARD_TECHO
 
       const int pin_led_rx = LED_BLUE;
       const int pin_led_tx = LED_RED;
-    #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_FREENODE
+    #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
       #define HAS_EEPROM false
       #define HAS_DISPLAY true
       #define DISPLAY EINK_BW
       #define HAS_PMU true
       #define HAS_NP false
       #define HAS_SD false
-      #define CONFIG_UART_BUFFER_SIZE 40000
+      #define CONFIG_UART_BUFFER_SIZE 6144
       #define CONFIG_QUEUE_0_SIZE 6144
+      #define HAS_INPUT true
       #define CONFIG_QUEUE_MAX_LENGTH 200
       #define EEPROM_SIZE 296
       #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
       #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 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;
 
index 9a468782f13b1109c5209883372bbb43e2c85376..47980623fd9e574b5b21cc2d67a397853f85dce7 100644 (file)
--- a/Config.h
+++ b/Config.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
@@ -20,7 +20,7 @@
        #define CONFIG_H
 
        #define MAJ_VERS  0x01
-       #define MIN_VERS  0x49
+       #define MIN_VERS  0x4e
 
        #define MODE_HOST 0x11
        #define MODE_TNC  0x12
@@ -65,7 +65,6 @@
        // 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;
@@ -78,6 +77,7 @@
        bool pmu_ready     = false;
        bool promisc       = false;
        bool implicit      = false;
+       bool memory_low    = false;
        uint8_t implicit_l = 0;
 
     volatile bool packet_ready  = false;
@@ -90,7 +90,7 @@
        int             last_rssi               = -292;
        uint8_t last_rssi_raw   = 0x00;
        uint8_t last_snr_raw    = 0x80;
-       uint8_t seq                             = 0xFF;
+       uint8_t seq[INTERFACE_COUNT];
        uint16_t read_len               = 0;
 
     bool serial_in_frame = false;
     unsigned long last_rx   = 0;
 
     // Power management
+    #define BATTERY_STATE_UNKNOWN     0x00
     #define BATTERY_STATE_DISCHARGING 0x01
-    #define BATTERY_STATE_CHARGING 0x02
-    #define BATTERY_STATE_CHARGED 0x03
+    #define BATTERY_STATE_CHARGING    0x02
+    #define BATTERY_STATE_CHARGED     0x03
     bool battery_installed = false;
     bool battery_indeterminate = false;
     bool external_power = false;
     uint8_t battery_state = 0x00;
     uint8_t display_intensity = 0xFF;
     uint8_t display_addr = 0xFF;
+    bool display_blanking_enabled = false;
     bool display_diagnostics = true;    
     bool device_init_done = false;
     bool eeprom_ok = false;
 
        // Boot flags
        #define START_FROM_BOOTLOADER 0x01
-       #define START_FROM_POWERON 0x02
-       #define START_FROM_BROWNOUT 0x03
-       #define START_FROM_JTAG 0x04
+       #define START_FROM_POWERON    0x02
+       #define START_FROM_BROWNOUT   0x03
+       #define START_FROM_JTAG       0x04
 
     // Subinterfaces
     // select interface 0 by default
index 4321d9f7884873ec55b0ffdb1bbe798c781155e4..c59d3484148b5702f327a462efc9ebea91f25ce5 100644 (file)
--- a/Console.h
+++ b/Console.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
index 6fdfc6533d939e2210473a64d64dc1cf7a2fec3f..15d06d25b49eadb9d36c14f1330e1674f4b97aee 100644 (file)
@@ -21,7 +21,7 @@ pages-debug:
 
 sourcepack:
        @echo Packing firmware sources...
-       zip --junk-paths -r build/pkg/rnode_firmware.zip ../arduino-cli.yaml ../Bluetooth.h ../Boards.h ../Config.h ../Console.h ../Device.h ../Display.h ../Framing.h ../Graphics.h ../Input.h ../LICENSE ../Makefile ../MD5.cpp ../MD5.h ../Modem.h ../partition_hashes ../Power.h ../README.md ../release_hashes.py ../RNode_Firmware_CE.ino ../ROM.h ../sx126x.cpp ../sx126x.h ../sx127x.cpp ../sx127x.h ../sx128x.cpp ../sx128x.h ../Utilities.h
+       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
        
 data:
        @echo Including assets...
index 5d8a119c81d339c35eabaca620d49b1e00cf5f1f..f61fc04381fd63ba544ba737f7701b399cc302e2 100644 (file)
@@ -4,9 +4,9 @@ import sys
 import shutil
 
 packages = {
-    "rns": "rns-0.7.5-py3-none-any.whl",
-    "nomadnet": "nomadnet-0.4.9-py3-none-any.whl",
-    "lxmf": "lxmf-0.4.3-py3-none-any.whl",
+    "rns": "rns-0.8.2-py3-none-any.whl",
+    "nomadnet": "nomadnet-0.5.4-py3-none-any.whl",
+    "lxmf": "lxmf-0.5.5-py3-none-any.whl",
     "rnsh": "rnsh-0.1.4-py3-none-any.whl",
 }
 
@@ -174,26 +174,34 @@ mf.write(help_redirect)
 mf.close()
 
 def optimise_manual(path):
-    pm = 110
+    pm = 60
     scale_imgs = [
         ("_images/board_rnodev2.png", pm),
         ("_images/board_rnode.png", pm),
-        ("_images/board_heltec32.png", pm),
+        ("_images/board_heltec32v20.png", pm),
+        ("_images/board_heltec32v30.png", pm),
         ("_images/board_t3v21.png", pm),
         ("_images/board_t3v20.png", pm),
-        ("_images/sideband_devices.webp", pm),
+        ("_images/board_t3v10.png", pm),
+        ("_images/board_t3s3.png", pm),
         ("_images/board_tbeam.png", pm),
+        ("_images/board_tdeck.png", pm),
+        ("_images/board_rak4631.png", pm),
+        ("_images/board_tbeam_supreme.png", pm),
+        ("_images/sideband_devices.webp", pm),
         ("_images/nomadnet_3.png", pm),
+        ("_images/meshchat_1.webp", pm),
         ("_images/radio_is5ac.png", pm),
         ("_images/radio_rblhg5.png", pm),
         ("_static/rns_logo_512.png", 256),
+        ("../images/bg_h_1.webp", pm),
     ]
 
     import subprocess
     import shlex
     for i,s in scale_imgs:
         fp = path+"/"+i
-        resize = "convert "+fp+" -resize "+str(s)+" "+fp
+        resize = "convert "+fp+" -quality 25 -resize "+str(s)+" "+fp
         print(resize)
         subprocess.call(shlex.split(resize), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 
@@ -205,6 +213,7 @@ def optimise_manual(path):
         "_static/scripts/furo.js.map",
         "_static/jquery-3.6.0.js",
         "_static/jquery.js",
+        "static/underscore-1.13.1.js",
         "_static/_sphinx_javascript_frameworks_compat.js",
         "_static/scripts/furo.js.LICENSE.txt",
         "_static/styles/furo-extensions.css.map",
index a1492214208c796aef31015c4a5961db86a96b2d..ab8419631020112f6a2ebb865d5b473f4238f726 100644 (file)
--- a/Device.h
+++ b/Device.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
index 0aa6a7f674d9a7111640787dcb6d12c321975c5b..4bfaf56ad59e5457a2c8642a1187903f099fccf2 100644 (file)
--- a/Display.h
+++ b/Display.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
 // 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 <Adafruit_GFX.h>
 
 #if DISPLAY == OLED
 #include <Wire.h>
 #include <Adafruit_SSD1306.h>
-#define DISP_W 128
-#define DISP_H 64
 #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)()) {
@@ -81,6 +96,11 @@ void busyCallback(const void* p) {
   #endif
 #elif BOARD_MODEL == BOARD_RAK4631
   #if DISPLAY == OLED
+  // RAK1921/SSD1306
+  #define DISP_RST -1
+  #define DISP_ADDR 0x3C
+  #define SCL_OLED 14
+  #define SDA_OLED 13
   // todo: add support for OLED board
   #elif DISPLAY == EINK_BW
   // todo: change this to be defined in Boards.h in the future
@@ -99,6 +119,12 @@ void busyCallback(const void* p) {
   #define DISP_W 128
   #define DISP_H 64
   #define DISP_ADDR -1
+#elif BOARD_MODEL == BOARD_TBEAM_S_V1
+  #define DISP_RST -1
+  #define DISP_ADDR 0x3C
+  #define SCL_OLED 18
+  #define SDA_OLED 17
+  #define DISP_CUSTOM_ADDR false
 #else
   #define DISP_RST -1
   #define DISP_ADDR 0x3C
@@ -128,7 +154,13 @@ uint32_t last_epd_refresh = 0;
 #define REFRESH_PERIOD 300000 // 5 minutes in ms
 #else
   #if DISPLAY == OLED
-  Adafruit_SSD1306 display(DISP_W, DISP_H, &Wire, DISP_RST);
+  #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);
+  #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;
@@ -141,10 +173,12 @@ uint32_t last_epd_refresh = 0;
 #define DISP_MODE_LANDSCAPE 0x01
 #define DISP_MODE_PORTRAIT  0x02
 #define DISP_PIN_SIZE   6
+#define DISPLAY_BLANKING_TIMEOUT 15*1000
 uint8_t disp_mode = DISP_MODE_UNKNOWN;
 uint8_t disp_ext_fb = false;
 unsigned char fb[512];
 uint32_t last_disp_update = 0;
+bool display_tx = false;
 int disp_update_interval = 1000/disp_target_fps;
 
 uint32_t last_page_flip = 0;
@@ -171,7 +205,6 @@ uint8_t online_interfaces = 0;
 #define WATERFALL_SIZE 92
 #else
 #define WATERFALL_SIZE int(DISP_H * 0.75) // default to 75% of the display height
-// add more eink compatible boards here
 #endif
 
 int waterfall[INTERFACE_COUNT][WATERFALL_SIZE] = {0};
@@ -206,10 +239,40 @@ void update_area_positions() {
 
 uint8_t display_contrast = 0x00;
 #if DISPLAY == OLED
-void set_contrast(Adafruit_SSD1306 *display, uint8_t contrast) {
+#if BOARD_MODEL == BOARD_TBEAM_S_V1
+void set_contrast(Adafruit_SH1106G *display, uint8_t 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;
+}
+#else
+  void set_contrast(Adafruit_SSD1306 *display, uint8_t contrast) {
     display->ssd1306_command(SSD1306_SETCONTRAST);
     display->ssd1306_command(contrast);
-}
+  }
+#endif
 #endif
 
 bool display_init() {
@@ -267,6 +330,8 @@ bool display_init() {
       // check for any commands from the host.
       display.epd2.setBusyCallback(busyCallback);
       #endif
+    #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+      Wire.begin(SDA_OLED, SCL_OLED);
     #endif
 
     #if DISP_CUSTOM_ADDR == true
@@ -280,12 +345,17 @@ bool display_init() {
       uint8_t display_address = DISP_ADDR;
     #endif
 
-
-    #if DISPLAY == OLED
-    if(!display.begin(SSD1306_SWITCHCAPVCC, display_address)) {
-    #elif DISPLAY == EINK_BW || DISPLAY == EINK_3C
+    #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
     // don't check if display is actually connected
     if(false) {
+    #elif BOARD_MODEL == BOARD_TDECK
+    display.init(240, 320);
+    display.setSPISpeed(80e6);
+    if (false) {
+    #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+    if (!display.begin(display_address, true)) {
+    #else
+    if (!display.begin(SSD1306_SWITCHCAPVCC, display_address)) {
     #endif
       return false;
     } else {
@@ -310,6 +380,9 @@ bool display_init() {
       #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);
@@ -323,10 +396,13 @@ bool display_init() {
         display.setRotation(3);
       #elif BOARD_MODEL == BOARD_HELTEC32_V3
         disp_mode = DISP_MODE_PORTRAIT;
-        // Antenna conx up
         display.setRotation(1);
-        // USB-C up
-        // display.setRotation(3);
+      #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);
       #else
         disp_mode = DISP_MODE_PORTRAIT;
         display.setRotation(3);
@@ -340,10 +416,14 @@ bool display_init() {
       disp_area.cp437(true);
       display.cp437(true);
 
-      #if HAS_EEPROM
-      display_intensity = EEPROM.read(eeprom_addr(ADDR_CONF_DINT));
-      #elif MCU_VARIANT == MCU_NRF52
-      display_intensity = eeprom_read(eeprom_addr(ADDR_CONF_DINT));
+      #if MCU_VARIANT != MCU_NRF52
+        display_intensity = EEPROM.read(eeprom_addr(ADDR_CONF_DINT));
+      #else
+        display_intensity = eeprom_read(eeprom_addr(ADDR_CONF_DINT));
+      #endif
+
+      #if BOARD_MODEL == BOARD_TDECK
+        display.fillScreen(DISPLAY_BLACK);
       #endif
 
       return true;
@@ -469,15 +549,23 @@ void draw_battery_bars(int px, int py) {
   if (pmu_ready) {
     if (battery_ready) {
       if (battery_installed) {
-      float battery_value = battery_percent;
+        float battery_value = battery_percent;
+
+        // Disable charging state display for now, since
+        // boards without dedicated PMU are completely
+        // unreliable for determining actual charging state.
+        bool disable_charge_status = false;
+        if (battery_indeterminate && battery_state == BATTERY_STATE_CHARGING) {
+          disable_charge_status = true;
+        }
         
-        if (battery_state == BATTERY_STATE_CHARGING) {
+        if (battery_state == BATTERY_STATE_CHARGING && !disable_charge_status) {
           battery_value = charge_tick;
           charge_tick += 3;
           if (charge_tick > 100) charge_tick = 0;
         }
 
-        if (battery_indeterminate && battery_state == BATTERY_STATE_CHARGING) {
+        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);
@@ -551,113 +639,115 @@ void draw_battery_bars(int px, int py) {
 #define Q_SNR_MAX 6.0
 
 void draw_quality_bars(int px, int py) {
-  signed char t_snr = (signed int)last_snr_raw;
-  int snr_int = (int)t_snr;
-  float snr_min = Q_SNR_MIN_BASE-(int)interface_obj[interface_page]->getSpreadingFactor()*Q_SNR_STEP;
-  float snr_span = (Q_SNR_MAX-snr_min);
-  float snr = ((int)snr_int) * 0.25;
-  float quality = ((snr-snr_min)/(snr_span))*100;
-  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 (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);
+    signed char t_snr = (signed int)last_snr_raw;
+    int snr_int = (int)t_snr;
+    float snr_min = Q_SNR_MIN_BASE-(int)interface_obj[interface_page]->getSpreadingFactor()*Q_SNR_STEP;
+    float snr_span = (Q_SNR_MAX-snr_min);
+    float snr = ((int)snr_int) * 0.25;
+    float quality = ((snr-snr_min)/(snr_span))*100;
+    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 (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
 #define S_RSSI_SPAN (S_RSSI_MAX-S_RSSI_MIN)
 void draw_signal_bars(int px, int py) {
-  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;
-  int signal = ((rssi_val - S_RSSI_MIN)*(1.0/S_RSSI_SPAN))*100.0;
-
-  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 (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);
+    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;
+    int signal = ((rssi_val - S_RSSI_MIN)*(1.0/S_RSSI_SPAN))*100.0;
+
+    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 (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
 #define WF_RSSI_MAX -60
 #define WF_RSSI_MIN -135
 #define WF_RSSI_SPAN (WF_RSSI_MAX - WF_RSSI_MIN)
@@ -673,9 +763,16 @@ void draw_waterfall(int px, int py) {
   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;
-
-  waterfall[interface_page][waterfall_head[interface_page]++] = rssi_normalised;
-  if (waterfall_head[interface_page] >= WATERFALL_SIZE) waterfall_head[interface_page] = 0;
+  if (display_tx) {
+    for (uint8_t i; i < WF_TX_SIZE; i++) {
+      waterfall[interface_page][waterfall_head[interface_page]++] = -1;
+      if (waterfall_head[interface_page] >= WATERFALL_SIZE) waterfall_head[interface_page] = 0;
+    }
+    display_tx = false;
+  } else {
+    waterfall[interface_page][waterfall_head[interface_page]++] = rssi_normalised;
+    if (waterfall_head[interface_page] >= WATERFALL_SIZE) waterfall_head[interface_page] = 0;
+  }
 
   stat_area.fillRect(px,py,WF_PIXEL_WIDTH, WATERFALL_SIZE, DISPLAY_BLACK);
   for (int i = 0; i < WATERFALL_SIZE; i++){
@@ -683,6 +780,11 @@ void draw_waterfall(int px, int py) {
     int ws = waterfall[interface_page][wi];
     if (ws > 0) {
       stat_area.drawLine(px, py+i, px+ws-1, py+i, DISPLAY_WHITE);
+    } else if (ws == -1) {
+      uint8_t o = i%2;
+      for (uint8_t ti = 0; ti < WF_PIXEL_WIDTH/2; ti++) {
+        stat_area.drawPixel(px+ti*2+o, py+i, DISPLAY_WHITE);
+      }
     }
   }
 }
@@ -1117,9 +1219,7 @@ void update_display(bool blank = false) {
   } else {
     if (millis()-last_disp_update >= disp_update_interval) {
       uint32_t current = millis();
-      #if screensaver_enabled
-        do_screensaver(current);
-      #endif
+      do_screensaver(current);
       #if DISPLAY == EINK_BW || DISPLAY == EINK_3C
       display.setFullWindow();
       display.fillScreen(DISPLAY_WHITE);
index 7b1c930e2d718d1d32b56eb99fe813db98b5ac2d..980edbd1c236d193f3a8d9f851e9b20c36af2886 100644 (file)
--- a/Framing.h
+++ b/Framing.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
@@ -53,6 +53,8 @@
   #define CMD_FB_READL    0x44
   #define CMD_DISP_INT    0x45
   #define CMD_DISP_ADDR   0x63
+  #define CMD_DISP_BLNK   0x64
+  #define CMD_NP_INT      0x65
   #define CMD_BT_CTRL     0x46
   #define CMD_BT_PIN      0x62
 
   #define ERROR_TXFAILED      0x02
   #define ERROR_EEPROM_LOCKED 0x03
   #define ERROR_QUEUE_FULL    0x04
+  #define ERROR_MEMORY_LOW    0x05
+  #define ERROR_MODEM_TIMEOUT 0x06
 
   // Serial framing variables
   size_t frame_len;
index 3d74c0fbcc819ac766fadacde0f4ffdfd1060452..faf5f2fdf66e2441f6ef5461ddec37b81949f250 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
diff --git a/MD5.cpp b/MD5.cpp
deleted file mode 100644 (file)
index 56daa77..0000000
--- a/MD5.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-#include "MD5.h"
-
-MD5::MD5()
-{
-       //nothing
-       return;
-}
-
-char* MD5::make_digest(const unsigned char *digest, int len) /* {{{ */
-{
-       char * md5str = (char*) malloc(sizeof(char)*(len*2+1));
-       static const char hexits[17] = "0123456789abcdef";
-       int i;
-
-       for (i = 0; i < len; i++) {
-               md5str[i * 2]       = hexits[digest[i] >> 4];
-               md5str[(i * 2) + 1] = hexits[digest[i] &  0x0F];
-       }
-       md5str[len * 2] = '\0';
-       return md5str;
-}
-
-/*
- * The basic MD5 functions.
- *
- * E and G are optimized compared to their RFC 1321 definitions for
- * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
- * implementation.
- * E() has been used instead of F() because F() is already defined in the Arduino core
- */
-#define E(x, y, z)                     ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z)                     ((y) ^ ((z) & ((x) ^ (y))))
-#define H(x, y, z)                     ((x) ^ (y) ^ (z))
-#define I(x, y, z)                     ((y) ^ ((x) | ~(z)))
-
-/*
- * The MD5 transformation for all four rounds.
- */
-#define STEP(f, a, b, c, d, x, t, s) \
-       (a) += f((b), (c), (d)) + (x) + (t); \
-       (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
-       (a) += (b);
-
-/*
- * SET reads 4 input bytes in little-endian byte order and stores them
- * in a properly aligned word in host byte order.
- *
- * The check for little-endian architectures that tolerate unaligned
- * memory accesses is just an optimization.  Nothing will break if it
- * doesn't work.
- */
-#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
-# define SET(n) \
-       (*(MD5_u32plus *)&ptr[(n) * 4])
-# define GET(n) \
-       SET(n)
-#else
-# define SET(n) \
-       (ctx->block[(n)] = \
-       (MD5_u32plus)ptr[(n) * 4] | \
-       ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
-       ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
-       ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
-# define GET(n) \
-       (ctx->block[(n)])
-#endif
-
-/*
- * This processes one or more 64-byte data blocks, but does NOT update
- * the bit counters.  There are no alignment requirements.
- */
-const void *MD5::body(void *ctxBuf, const void *data, size_t size)
-{
-       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
-       const unsigned char *ptr;
-       MD5_u32plus a, b, c, d;
-       MD5_u32plus saved_a, saved_b, saved_c, saved_d;
-
-       ptr = (unsigned char*)data;
-
-       a = ctx->a;
-       b = ctx->b;
-       c = ctx->c;
-       d = ctx->d;
-
-       do {
-               saved_a = a;
-               saved_b = b;
-               saved_c = c;
-               saved_d = d;
-
-/* Round 1
- * E() has been used instead of F() because F() is already defined in the Arduino core
- */
-               STEP(E, a, b, c, d, SET(0), 0xd76aa478, 7)
-               STEP(E, d, a, b, c, SET(1), 0xe8c7b756, 12)
-               STEP(E, c, d, a, b, SET(2), 0x242070db, 17)
-               STEP(E, b, c, d, a, SET(3), 0xc1bdceee, 22)
-               STEP(E, a, b, c, d, SET(4), 0xf57c0faf, 7)
-               STEP(E, d, a, b, c, SET(5), 0x4787c62a, 12)
-               STEP(E, c, d, a, b, SET(6), 0xa8304613, 17)
-               STEP(E, b, c, d, a, SET(7), 0xfd469501, 22)
-               STEP(E, a, b, c, d, SET(8), 0x698098d8, 7)
-               STEP(E, d, a, b, c, SET(9), 0x8b44f7af, 12)
-               STEP(E, c, d, a, b, SET(10), 0xffff5bb1, 17)
-               STEP(E, b, c, d, a, SET(11), 0x895cd7be, 22)
-               STEP(E, a, b, c, d, SET(12), 0x6b901122, 7)
-               STEP(E, d, a, b, c, SET(13), 0xfd987193, 12)
-               STEP(E, c, d, a, b, SET(14), 0xa679438e, 17)
-               STEP(E, b, c, d, a, SET(15), 0x49b40821, 22)
-
-/* Round 2 */
-               STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
-               STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
-               STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
-               STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
-               STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
-               STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
-               STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
-               STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
-               STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
-               STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
-               STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
-               STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
-               STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
-               STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
-               STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
-               STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
-
-/* Round 3 */
-               STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
-               STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
-               STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
-               STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
-               STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
-               STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
-               STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
-               STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
-               STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
-               STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
-               STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
-               STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
-               STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
-               STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
-               STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
-               STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
-
-/* Round 4 */
-               STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
-               STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
-               STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
-               STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
-               STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
-               STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
-               STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
-               STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
-               STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
-               STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
-               STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
-               STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
-               STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
-               STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
-               STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
-               STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
-
-               a += saved_a;
-               b += saved_b;
-               c += saved_c;
-               d += saved_d;
-
-               ptr += 64;
-       } while (size -= 64);
-
-       ctx->a = a;
-       ctx->b = b;
-       ctx->c = c;
-       ctx->d = d;
-
-       return ptr;
-}
-
-void MD5::MD5Init(void *ctxBuf)
-{
-       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
-       ctx->a = 0x67452301;
-       ctx->b = 0xefcdab89;
-       ctx->c = 0x98badcfe;
-       ctx->d = 0x10325476;
-
-       ctx->lo = 0;
-       ctx->hi = 0;
-
-    memset(ctx->block, 0, sizeof(ctx->block));
-    memset(ctx->buffer, 0, sizeof(ctx->buffer));
-}
-
-void MD5::MD5Update(void *ctxBuf, const void *data, size_t size)
-{
-       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
-       MD5_u32plus saved_lo;
-       MD5_u32plus used, free;
-
-       saved_lo = ctx->lo;
-       if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
-               ctx->hi++;
-       }
-       ctx->hi += size >> 29;
-
-       used = saved_lo & 0x3f;
-
-       if (used) {
-               free = 64 - used;
-
-               if (size < free) {
-                       memcpy(&ctx->buffer[used], data, size);
-                       return;
-               }
-
-               memcpy(&ctx->buffer[used], data, free);
-               data = (unsigned char *)data + free;
-               size -= free;
-               body(ctx, ctx->buffer, 64);
-       }
-
-       if (size >= 64) {
-               data = body(ctx, data, size & ~(size_t)0x3f);
-               size &= 0x3f;
-       }
-
-       memcpy(ctx->buffer, data, size);
-}
-
-void MD5::MD5Final(unsigned char *result, void *ctxBuf)
-{
-       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
-       MD5_u32plus used, free;
-
-       used = ctx->lo & 0x3f;
-
-       ctx->buffer[used++] = 0x80;
-
-       free = 64 - used;
-
-       if (free < 8) {
-               memset(&ctx->buffer[used], 0, free);
-               body(ctx, ctx->buffer, 64);
-               used = 0;
-               free = 64;
-       }
-
-       memset(&ctx->buffer[used], 0, free - 8);
-
-       ctx->lo <<= 3;
-       ctx->buffer[56] = ctx->lo;
-       ctx->buffer[57] = ctx->lo >> 8;
-       ctx->buffer[58] = ctx->lo >> 16;
-       ctx->buffer[59] = ctx->lo >> 24;
-       ctx->buffer[60] = ctx->hi;
-       ctx->buffer[61] = ctx->hi >> 8;
-       ctx->buffer[62] = ctx->hi >> 16;
-       ctx->buffer[63] = ctx->hi >> 24;
-
-       body(ctx, ctx->buffer, 64);
-
-       result[0] = ctx->a;
-       result[1] = ctx->a >> 8;
-       result[2] = ctx->a >> 16;
-       result[3] = ctx->a >> 24;
-       result[4] = ctx->b;
-       result[5] = ctx->b >> 8;
-       result[6] = ctx->b >> 16;
-       result[7] = ctx->b >> 24;
-       result[8] = ctx->c;
-       result[9] = ctx->c >> 8;
-       result[10] = ctx->c >> 16;
-       result[11] = ctx->c >> 24;
-       result[12] = ctx->d;
-       result[13] = ctx->d >> 8;
-       result[14] = ctx->d >> 16;
-       result[15] = ctx->d >> 24;
-
-       memset(ctx, 0, sizeof(*ctx));
-}
-unsigned char* MD5::make_hash(char *arg)
-{
-       MD5_CTX context;
-       unsigned char * hash = (unsigned char *) malloc(16);
-       MD5Init(&context);
-       MD5Update(&context, arg, strlen(arg));
-       MD5Final(hash, &context);
-       return hash;
-}
-unsigned char* MD5::make_hash(char *arg,size_t size)
-{
-       MD5_CTX context;
-       unsigned char * hash = (unsigned char *) malloc(16);
-       MD5Init(&context);
-       MD5Update(&context, arg, size);
-       MD5Final(hash, &context);
-       return hash;
-}
\ No newline at end of file
diff --git a/MD5.h b/MD5.h
deleted file mode 100644 (file)
index 3ec8d81..0000000
--- a/MD5.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef MD5_h
-#define MD5_h
-
-#include "Arduino.h"
-
-/*
- * This is an OpenSSL-compatible implementation of the RSA Data Security,
- * Inc. MD5 Message-Digest Algorithm (RFC 1321).
- *
- * Written by Solar Designer <solar at openwall.com> in 2001, and placed
- * in the public domain.  There's absolutely no warranty.
- *
- * This differs from Colin Plumb's older public domain implementation in
- * that no 32-bit integer data type is required, there's no compile-time
- * endianness configuration, and the function prototypes match OpenSSL's.
- * The primary goals are portability and ease of use.
- *
- * This implementation is meant to be fast, but not as fast as possible.
- * Some known optimizations are not included to reduce source code size
- * and avoid compile-time configuration.
- */
-
-/*
- * Updated by Scott MacVicar for arduino
- * <scott@macvicar.net>
- */
-
-#include <string.h>
-
-typedef unsigned long MD5_u32plus;
-
-typedef struct {
-       MD5_u32plus lo, hi;
-       MD5_u32plus a, b, c, d;
-       unsigned char buffer[64];
-       MD5_u32plus block[16];
-} MD5_CTX;
-
-class MD5
-{
-public:
-       MD5();
-       static unsigned char* make_hash(char *arg);
-       static unsigned char* make_hash(char *arg,size_t size);
-       static char* make_digest(const unsigned char *digest, int len);
-       static const void *body(void *ctxBuf, const void *data, size_t size);
-       static void MD5Init(void *ctxBuf);
-       static void MD5Final(unsigned char *result, void *ctxBuf);
-       static void MD5Update(void *ctxBuf, const void *data, size_t size);
-};
-
-#endif
\ No newline at end of file
index cd4df9c5f96e4d6333966e7746cca88b1a42aa4b..ec202e33b4ab67ebb599421dd3f8e6aab3412943 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 2023, Mark Qvist
+# 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
@@ -13,7 +13,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-ESP_IDF_VER = 2.0.17
+# Version 2.0.17 of the Arduino ESP core is based on ESP-IDF v4.4.7
+ARDUINO_ESP_CORE_VER = 2.0.17
 
 V ?= 0
 VFLAG =
@@ -36,8 +37,11 @@ prep-index:
        arduino-cli core update-index --config-file arduino-cli.yaml
 
 prep-esp32:
-       arduino-cli core install esp32:esp32@$(ESP_IDF_VER) --config-file arduino-cli.yaml
+       arduino-cli core install esp32:esp32@$(ARDUINO_ESP_CORE_VER) --config-file arduino-cli.yaml
        arduino-cli lib install "Adafruit SSD1306"
+       arduino-cli lib install "Adafruit SH110X"
+       arduino-cli lib install "Adafruit ST7735 and ST7789 Library"
+       arduino-cli lib install "Adafruit NeoPixel"
        arduino-cli lib install "XPowersLib"
        arduino-cli lib install "Crypto"
        arduino-cli lib install "Adafruit NeoPixel"
@@ -66,11 +70,14 @@ upload-spiffs:
 
 firmware: $(shell grep ^firmware- Makefile | cut -d: -f1)
 
-firmware-tbeam:
-       arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\""
+check_bt_buffers:
+       @./esp32_btbufs.py ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/libraries/BluetoothSerial/src/BluetoothSerial.cpp
+
+firmware-tbeam: 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=0xE4\""
 
-firmware-tbeam_sx126x:
-       arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\""
+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
 
@@ -80,7 +87,10 @@ firmware-techo4:
 firmware-techo9:
        arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x43\"  \"-DBOARD_VARIANT=0x17\""
 
-firmware-t3s3_sx1262:
+firmware-t3s3:
+       arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_VARIANT=0xAB\""
+
+firmware-t3s3_sx126x:
        arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_VARIANT=0xA1\""
 
 firmware-t3s3_sx1280_pa:
@@ -89,43 +99,43 @@ firmware-t3s3_sx1280_pa:
 firmware-e22_esp32:
        arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\" \"-DEXTERNAL_LEDS=true\""
 
-firmware-lora32_v10:
+firmware-lora32_v10: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
 
-firmware-lora32_v10_extled:
+firmware-lora32_v10_extled: check_bt_buffers
        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:
+firmware-lora32_v20: check_bt_buffers
        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:
+firmware-lora32_v21: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\""
 
-firmware-lora32_v21_extled:
+firmware-lora32_v21_extled: check_bt_buffers
        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:
+firmware-lora32_v21_tcxo: check_bt_buffers
        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:
+firmware-heltec32_v2: check_bt_buffers
        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:
+firmware-heltec32_v2_extled: check_bt_buffers
        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 --fqbn esp32:esp32:heltec_wifi_lora_32_V3 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\""
 
-firmware-rnode_ng_20:
+firmware-rnode_ng_20: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
 
-firmware-rnode_ng_21:
+firmware-rnode_ng_21: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\""
 
-firmware-featheresp32:
+firmware-featheresp32: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:featheresp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
 
-firmware-genericesp32:
+firmware-genericesp32: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\""
 
 firmware-rak4631:
@@ -134,9 +144,10 @@ firmware-rak4631:
 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\""
 
-firmware-freenode:
+firmware-opencom-xl:
        arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x21\""
 
+
 upload-tbeam:
        arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:t-beam
        @sleep 1
@@ -184,6 +195,20 @@ upload-heltec32_v3:
        @sleep 3
        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 $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
+       @sleep 1
+       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 $(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
+
+upload-tbeam_supreme:
+       arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
+       @sleep 1
+       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 $(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
+
 upload-rnode_ng_20:
        arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:ttgo-lora32
        @sleep 1
@@ -199,17 +224,11 @@ upload-rnode_ng_21:
        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 $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
-       @sleep 2
-       python3 ./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 $(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 esp32s3 $(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
 
 upload-featheresp32:
        arduino-cli upload -p $(or $(port), /dev/ttyUSB0) --fqbn esp32:esp32:featheresp32
@@ -230,15 +249,13 @@ upload-e22_esp32:
        @sleep 3
        python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyUSB0) $(COMMON_ESP_UPLOAD_FlAGS)  ./Release/console_image.bin
 
-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-rnode_ng_20 release-rnode_ng_21 release-t3s3 release-e22_esp32 release-hashes
+release:  console-site spiffs-image $(shell grep ^release- Makefile | cut -d: -f1)
 
 release-hashes:
        python3 ./release_hashes.py > ./Release/release.json
 
-release-tbeam:
-       arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\""
+release-tbeam: 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=0xE4\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam.boot_app0
        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
@@ -246,8 +263,8 @@ release-tbeam:
        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
 
-release-tbeam_sx1262:
-       arduino-cli compile --fqbn esp32:esp32:t-beam $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\""
+release-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_MODEL=E8\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam_sx1262.boot_app0
        cp build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bin build/rnode_firmware_tbeam_sx1262.bin
        cp build/esp32.esp32.t-beam/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_tbeam_sx1262.bootloader
@@ -255,7 +272,7 @@ release-tbeam_sx1262:
        zip --junk-paths ./Release/rnode_firmware_tbeam_sx1262.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tbeam_sx1262.boot_app0 build/rnode_firmware_tbeam_sx1262.bin build/rnode_firmware_tbeam_sx1262.bootloader build/rnode_firmware_tbeam_sx1262.partitions
        rm -r build
 
-release-lora32_v10:
+release-lora32_v10: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v10.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v10.bin
@@ -264,7 +281,7 @@ release-lora32_v10:
        zip --junk-paths ./Release/rnode_firmware_lora32v10.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v10.boot_app0 build/rnode_firmware_lora32v10.bin build/rnode_firmware_lora32v10.bootloader build/rnode_firmware_lora32v10.partitions
        rm -r build
 
-release-lora32_v20:
+release-lora32_v20: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v20.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v20.bin
@@ -273,7 +290,7 @@ release-lora32_v20:
        zip --junk-paths ./Release/rnode_firmware_lora32v20.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v20.boot_app0 build/rnode_firmware_lora32v20.bin build/rnode_firmware_lora32v20.bootloader build/rnode_firmware_lora32v20.partitions
        rm -r build
 
-release-lora32_v21:
+release-lora32_v21: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v21.bin
@@ -282,7 +299,7 @@ release-lora32_v21:
        zip --junk-paths ./Release/rnode_firmware_lora32v21.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v21.boot_app0 build/rnode_firmware_lora32v21.bin build/rnode_firmware_lora32v21.bootloader build/rnode_firmware_lora32v21.partitions
        rm -r build
 
-release-lora32_v10_extled:
+release-lora32_v10_extled: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v10.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v10.bin
@@ -291,7 +308,7 @@ release-lora32_v10_extled:
        zip --junk-paths ./Release/rnode_firmware_lora32v10.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v10.boot_app0 build/rnode_firmware_lora32v10.bin build/rnode_firmware_lora32v10.bootloader build/rnode_firmware_lora32v10.partitions
        rm -r build
 
-release-lora32_v20_extled:
+release-lora32_v20_extled: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v20.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v20.bin
@@ -300,7 +317,7 @@ release-lora32_v20_extled:
        zip --junk-paths ./Release/rnode_firmware_lora32v20_extled.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v20.boot_app0 build/rnode_firmware_lora32v20.bin build/rnode_firmware_lora32v20.bootloader build/rnode_firmware_lora32v20.partitions
        rm -r build
 
-release-lora32_v21_extled:
+release-lora32_v21_extled: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v21.bin
@@ -309,7 +326,7 @@ release-lora32_v21_extled:
        zip --junk-paths ./Release/rnode_firmware_lora32v21_extled.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v21.boot_app0 build/rnode_firmware_lora32v21.bin build/rnode_firmware_lora32v21.bootloader build/rnode_firmware_lora32v21.partitions
        rm -r build
 
-release-lora32_v21_tcxo:
+release-lora32_v21_tcxo: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21_tcxo.boot_app0
        cp build/esp32.esp32.ttgo-lora32/RNode_Firmware_CE.ino.bin build/rnode_firmware_lora32v21_tcxo.bin
@@ -318,53 +335,51 @@ release-lora32_v21_tcxo:
        zip --junk-paths ./Release/rnode_firmware_lora32v21_tcxo.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_lora32v21_tcxo.boot_app0 build/rnode_firmware_lora32v21_tcxo.bin build/rnode_firmware_lora32v21_tcxo.bootloader build/rnode_firmware_lora32v21_tcxo.partitions
        rm -r build
 
-release-heltec32_v2:
+release-heltec32_v2: check_bt_buffers
        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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0
        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-heltec32_v3:
        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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v3.boot_app0
-       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
+       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
        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-heltec32_v2_extled:
+release-heltec32_v2_extled: check_bt_buffers
        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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0
-       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
+       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
 
-release-rnode_ng_20:
+release-rnode_ng_20: check_bt_buffers
        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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_ng20.boot_app0
-       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
+       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
        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:
+release-rnode_ng_21: check_bt_buffers
        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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_ng21.boot_app0
-       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
+       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
        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-t3s3:
-       arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\""
+       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/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3.boot_app0
        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
@@ -373,7 +388,7 @@ release-t3s3:
        rm -r build
 
 release-e22_esp32:
-       arduino-cli compile --fqbn esp32:esp32:esp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\""
+       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/$(ESP_IDF_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
@@ -381,7 +396,35 @@ release-e22_esp32:
        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-featheresp32:
+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_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
+       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_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
+
+release-tbeam_supreme:
+       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
+       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 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_featheresp32.boot_app0
        cp build/esp32.esp32.featheresp32/RNode_Firmware_CE.ino.bin build/rnode_firmware_featheresp32.bin
@@ -390,7 +433,7 @@ release-featheresp32:
        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
 
-release-genericesp32:
+release-genericesp32: check_bt_buffers
        arduino-cli compile --fqbn esp32:esp32:esp32 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_esp32_generic.boot_app0
        cp build/esp32.esp32.esp32/RNode_Firmware_CE.ino.bin build/rnode_firmware_esp32_generic.bin
@@ -409,7 +452,7 @@ release-rak4631_sx1280:
        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
 
-release-freenode:
+release-opencom-xl:
        arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DBOARD_VARIANT=0x21\""
-       cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware_CE.ino.hex build/rnode_firmware_freenode.hex
-       adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_freenode.hex Release/rnode_firmware_freenode.zip
+       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
diff --git a/Power.h b/Power.h
index 56bcf167443df1dc97f5194fadc8045b4e350771..0aec81898979da69351a814ec26c46b0107c9722 100644 (file)
--- a/Power.h
+++ b/Power.h
@@ -1,9 +1,28 @@
-#if BOARD_MODEL == BOARD_TBEAM
+// 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/>.
+
+#if BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_TBEAM_S_V1
   #include <XPowersLib.h>
   XPowersLibInterface* PMU = NULL;
 
   #ifndef PMU_WIRE_PORT
-    #define PMU_WIRE_PORT   Wire
+    #if BOARD_MODEL == BOARD_TBEAM_S_V1
+      #define PMU_WIRE_PORT   Wire1
+    #else
+      #define PMU_WIRE_PORT   Wire
+    #endif
   #endif
 
   #define BAT_V_MIN       3.15
@@ -28,8 +47,6 @@
       pmuInterrupt = true;
   }
 #elif BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1
-  #define BAT_C_SAMPLES   7
-  #define BAT_D_SAMPLES   2
   #define BAT_V_MIN       3.15
   #define BAT_V_MAX       4.3
   #define BAT_V_CHG       4.48
   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_RAK4631
-#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;
+  #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_TDECK
+  #define BAT_V_MIN       3.15
+  #define BAT_V_MAX       4.3
+  #define BAT_V_CHG       4.48
+  #define BAT_V_FLOAT     4.33
+  #define BAT_SAMPLES     5
+  const uint8_t pin_vbat = 4;
+  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_HELTEC32_V3
+  #define BAT_V_MIN       3.15
+  #define BAT_V_MAX       4.3
+  #define BAT_V_CHG       4.48
+  #define BAT_V_FLOAT     4.33
+  #define BAT_SAMPLES     7
+  const uint8_t pin_vbat = 1;
+  const uint8_t pin_ctrl = 37;
+  float bat_p_samples[BAT_SAMPLES];
+  float bat_v_samples[BAT_SAMPLES];
+  uint8_t bat_samples_count = 0;
+  int bat_discharging_samples = 0;
+  int bat_charging_samples = 0;
+  int bat_charged_samples = 0;
+  bool bat_voltage_dropping = false;
+  float bat_delay_v = 0;
+  float bat_state_change_v = 0;
 #endif
 
 uint32_t last_pmu_update = 0;
@@ -74,10 +125,17 @@ uint8_t pmu_rc = 0;
 void kiss_indicate_battery();
 
 void measure_battery() {
-  #if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1
+  #if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_TDECK
     battery_installed = true;
     battery_indeterminate = true;
-    bat_v_samples[bat_samples_count%BAT_SAMPLES] = (float)(analogRead(pin_vbat)) / 4095*2*3.3*1.1;
+
+    #if BOARD_MODEL == BOARD_HELTEC32_V3
+      float battery_measurement = (float)(analogRead(pin_vbat)) * 0.0041;
+    #else
+      float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*2.0*3.3*1.1;
+    #endif
+
+    bat_v_samples[bat_samples_count%BAT_SAMPLES] = battery_measurement;
     bat_p_samples[bat_samples_count%BAT_SAMPLES] = ((battery_voltage-BAT_V_MIN) / (BAT_V_MAX-BAT_V_MIN))*100.0;
     
     bat_samples_count++;
@@ -100,41 +158,60 @@ void measure_battery() {
       battery_voltage = battery_voltage/BAT_SAMPLES;
       
       if (bat_delay_v == 0) bat_delay_v = battery_voltage;
+      if (bat_state_change_v == 0) bat_state_change_v = battery_voltage;
       if (battery_percent > 100.0) battery_percent = 100.0;
       if (battery_percent < 0.0) battery_percent = 0.0;
 
       if (bat_samples_count%BAT_SAMPLES == 0) {
+        float bat_delay_diff = bat_state_change_v-battery_voltage;
+        if (bat_delay_diff < 0) { bat_delay_diff *= -1; }
+
         if (battery_voltage < bat_delay_v && battery_voltage < BAT_V_FLOAT) {
-          bat_voltage_dropping = true;
+          if (bat_voltage_dropping == false) {
+            if (bat_delay_diff > 0.008) {
+              bat_voltage_dropping = true;
+              bat_state_change_v = battery_voltage;
+              // SerialBT.printf("STATE CHANGE to DISCHARGE at delta=%.3fv. State change v is now %.3fv.\n", bat_delay_diff, bat_state_change_v);
+            }
+          }
         } else {
-          bat_voltage_dropping = false;
+          if (bat_voltage_dropping == true) {
+            if (bat_delay_diff > 0.01) {
+              bat_voltage_dropping = false;
+              bat_state_change_v = battery_voltage;
+              // SerialBT.printf("STATE CHANGE to CHARGE at delta=%.3fv. State change v is now %.3fv.\n", bat_delay_diff, bat_state_change_v);
+            }
+          }
         }
         bat_samples_count = 0;
+        bat_delay_v = battery_voltage;
       }
 
       if (bat_voltage_dropping && battery_voltage < BAT_V_FLOAT) {
         battery_state = BATTERY_STATE_DISCHARGING;
       } else {
-        #if BOARD_MODEL == BOARD_RNODE_NG_21
+        if (battery_percent < 100.0) {
           battery_state = BATTERY_STATE_CHARGING;
-        #else
-          battery_state = BATTERY_STATE_DISCHARGING;
-        #endif
+        } else {
+          battery_state = BATTERY_STATE_CHARGED;
+        }
       }
 
-
-
       // if (bt_state == BT_STATE_CONNECTED) {
       //   SerialBT.printf("Bus voltage %.3fv. Unfiltered %.3fv.", battery_voltage, bat_v_samples[BAT_SAMPLES-1]);
       //   if (bat_voltage_dropping) {
-      //     SerialBT.printf(" Voltage is dropping. Percentage %.1f%%.\n", battery_percent);
+      //     SerialBT.printf(" Voltage is dropping. Percentage %.1f%%.", battery_percent);
       //   } else {
-      //     SerialBT.print(" Voltage is not dropping.\n");
+      //     SerialBT.printf(" Voltage is not dropping. Percentage %.1f%%.", battery_percent);
       //   }
+      //   if (battery_state == BATTERY_STATE_DISCHARGING) { SerialBT.printf(" Battery discharging. delay_v %.3fv", bat_delay_v); }
+      //   if (battery_state == BATTERY_STATE_CHARGING) { SerialBT.printf(" Battery charging. delay_v %.3fv", bat_delay_v); }
+      //   if (battery_state == BATTERY_STATE_CHARGED) { SerialBT.print(" Battery is charged."); }
+      //   SerialBT.print("\n");
       // }
     }
 
-  #elif BOARD_MODEL == BOARD_TBEAM
+  #elif BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_TBEAM_S_V1
     if (PMU) {
       float discharge_current = 0;
       float charge_current    = 0;
@@ -172,7 +249,7 @@ void measure_battery() {
           }
         }
       } else {
-        battery_state = BATTERY_STATE_DISCHARGING;
+        battery_state = BATTERY_STATE_UNKNOWN;
         battery_percent = 0.0;
         battery_voltage = 0.0;
       }
@@ -301,31 +378,29 @@ void update_pmu() {
 }
 
 bool init_pmu() {
-  #if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1
+  #if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TDECK
     pinMode(pin_vbat, INPUT);
     return true;
+  #elif BOARD_MODEL == BOARD_HELTEC32_V3
+    pinMode(pin_ctrl,OUTPUT);
+    digitalWrite(pin_ctrl, LOW);
+    return true;
   #elif BOARD_MODEL == BOARD_TBEAM
     Wire.begin(I2C_SDA, I2C_SCL);
 
     if (!PMU) {
         PMU = new XPowersAXP2101(PMU_WIRE_PORT);
         if (!PMU->init()) {
-            Serial.println("Warning: Failed to find AXP2101 power management");
             delete PMU;
             PMU = NULL;
-        } else {
-            Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU");
         }
     }
 
     if (!PMU) {
         PMU = new XPowersAXP192(PMU_WIRE_PORT);
         if (!PMU->init()) {
-            Serial.println("Warning: Failed to find AXP192 power management");
             delete PMU;
             PMU = NULL;
-        } else {
-            Serial.println("AXP192 PMU init succeeded, using AXP192 PMU");
         }
     }
 
@@ -431,7 +506,7 @@ bool init_pmu() {
     PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
 
     return true; 
-  #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_FREENODE
+  #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL
     // board doesn't have PMU but we can measure batt voltage
 
     // prep ADC for reading battery level
@@ -446,6 +521,86 @@ bool init_pmu() {
     // Get a single ADC sample and throw it away
     float raw = analogRead(PIN_VBAT);
     return true;
+  #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+    Wire1.begin(I2C_SDA, I2C_SCL);
+
+    if (!PMU) {
+        PMU = new XPowersAXP2101(PMU_WIRE_PORT);
+        if (!PMU->init()) {
+            delete PMU;
+            PMU = NULL;
+        }
+    }
+
+    if (!PMU) {
+      return false;
+    }
+
+    /**
+     * gnss module power channel
+     * The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during
+     * initialization
+     */
+    PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
+    PMU->enablePowerOutput(XPOWERS_ALDO4);
+
+    // lora radio power channel
+    PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
+    PMU->enablePowerOutput(XPOWERS_ALDO3);
+
+    // m.2 interface
+    PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
+    PMU->enablePowerOutput(XPOWERS_DCDC3);
+
+    /**
+     * ALDO2 cannot be turned off.
+     * It is a necessary condition for sensor communication.
+     * It must be turned on to properly access the sensor and screen
+     * It is also responsible for the power supply of PCF8563
+     */
+    PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
+    PMU->enablePowerOutput(XPOWERS_ALDO2);
+
+    // 6-axis , magnetometer ,bme280 , oled screen power channel
+    PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
+    PMU->enablePowerOutput(XPOWERS_ALDO1);
+
+    // sdcard power channle
+    PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
+    PMU->enablePowerOutput(XPOWERS_BLDO1);
+
+    // PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300);
+    // PMU->enablePowerOutput(XPOWERS_DCDC4);
+
+    // not use channel
+    PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited
+    PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited
+    PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
+    PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
+    PMU->disablePowerOutput(XPOWERS_VBACKUP);
+
+    // Configure charging
+    PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
+    PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
+    // TODO: Reset
+    PMU->setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
+
+    // Set the time of pressing the button to turn off
+    PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
+    PMU->setPowerKeyPressOnTime(XPOWERS_POWERON_128MS);
+
+    // disable all axp chip interrupt
+    PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
+    PMU->clearIrqStatus();
+
+    // It is necessary to disable the detection function of the TS pin on the board
+    // without the battery temperature detection function, otherwise it will cause abnormal charging
+    PMU->disableTSPinMeasure();
+    PMU->enableVbusVoltageMeasure();
+    PMU->enableBattVoltageMeasure();
+
+
+    return true; 
   #else
     return false;
   #endif
index e5016bab41aea6a39dd35fa384625e6021604c30..6d2697798045ede96a0bcf52e996a25863ed57b8 100644 (file)
--- a/README.md
+++ b/README.md
@@ -24,7 +24,6 @@ The latest release, installable through `rnodeconf`, is version `1.73`. This rel
 - 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`.
 
 ## Supported products and boards
index 5a8384564f8c162277c72b99b0f3c59100ecc287..48c38e4f900ea3b3dff8b30435e4201dc77121d0 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
@@ -74,6 +74,16 @@ volatile bool serial_buffering = false;
   #include "Console.h"
 #endif
 
+#define MODEM_QUEUE_SIZE 4*INTERFACE_COUNT
+typedef struct {
+      size_t len;
+      int rssi;
+      int snr_raw;
+      uint8_t data[];
+      uint8_t interface;
+} modem_packet_t;
+static xQueueHandle modem_packet_queue = NULL;
+
 char sbuf[128];
 
 uint8_t *packet_queue[INTERFACE_COUNT];
@@ -83,6 +93,18 @@ void setup() {
     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
   #endif
 
   #if MCU_VARIANT == MCU_NRF52
@@ -109,7 +131,11 @@ void setup() {
 
   Serial.begin(serial_baudrate);
 
-  #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TECHO
+  #if HAS_NP
+    led_init();
+  #endif
+
+  #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
   // 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
@@ -141,6 +167,10 @@ void setup() {
   memset(packet_starts_buf, 0, sizeof(packet_starts_buf));
   memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
 
+  memset(seq, 0xFF, sizeof(seq));
+
+  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);
@@ -151,6 +181,8 @@ void setup() {
 
   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]) {
@@ -320,30 +352,75 @@ inline void kiss_write_packet(int index) {
   uint8_t cmd_byte = getInterfaceCommandByte(index);
 
   serial_write(FEND);
-
   // Add index of interface the packet came from
   serial_write(cmd_byte);
 
   for (uint16_t i = 0; i < read_len; i++) {
-    uint8_t byte = pbuf[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 = 0;
   packet_ready = false;
+
+  #if MCU_VARIANT == MCU_ESP32 && HAS_BLE
+      bt_flush();
+  #endif
 }
 
 inline void getPacketData(RadioInterface* radio, uint16_t len) {
-  while (len-- && read_len < MTU) {
-    pbuf[read_len++] = radio->read();
-  }
+  #if MCU_VARIANT != MCU_NRF52
+    while (len-- && read_len < MTU) {
+      pbuf[read_len++] = radio->read();
+    }  
+  #else
+    BaseType_t int_mask = taskENTER_CRITICAL_FROM_ISR();
+    while (len-- && read_len < MTU) {
+      pbuf[read_len++] = radio->read();
+    }
+    taskEXIT_CRITICAL_FROM_ISR(int_mask);
+  #endif
+}
+
+inline bool queuePacket(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);
+    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;
+    memcpy(modem_packet->data, pbuf, read_len);
+    if (!modem_packet_queue || xQueueSendFromISR(modem_packet_queue, &modem_packet, NULL) != pdPASS) {
+        free(modem_packet);
+    }
 }
 
-void receive_callback(uint8_t index, int packet_size) {
-        selected_radio = interface_obj[index];
+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,
@@ -354,33 +431,41 @@ void receive_callback(uint8_t index, int packet_size) {
     uint8_t header   = selected_radio->read(); packet_size--;
     uint8_t sequence = packetSequence(header);
 
-    if (isSplitPacket(header) && seq == SEQ_UNSET) {
+    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
-      read_len = 0;
-      seq = sequence;
+      #if MCU_VARIANT == MCU_NRF52
+        int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+      #else
+        read_len = 0;
+      #endif
+      
+      seq[index] = sequence;
 
       getPacketData(selected_radio, packet_size);
 
-    } else if (isSplitPacket(header) && seq == sequence) {
+    } 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 = SEQ_UNSET;
-      packet_interface = index;
-      packet_ready = true;
+      seq[index] = SEQ_UNSET;
+      ready = true;
 
-    } else if (isSplitPacket(header) && seq != sequence) {
+    } 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.
-      read_len = 0;
-      seq = sequence;
+      #if MCU_VARIANT == MCU_NRF52
+        int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+      #else
+        read_len = 0;
+      #endif
+      seq[index] = sequence;
 
       getPacketData(selected_radio, packet_size);
 
@@ -389,17 +474,20 @@ void receive_callback(uint8_t index, int packet_size) {
       // just read it and set the ready
       // flag to true.
 
-      if (seq != SEQ_UNSET) {
+      if (seq[index] != SEQ_UNSET) {
         // If we already had part of a split
         // packet in the buffer, we clear it.
-        read_len = 0;
-        seq = SEQ_UNSET;
+        #if MCU_VARIANT == MCU_NRF52
+          int_mask = taskENTER_CRITICAL_FROM_ISR(); read_len = 0; taskEXIT_CRITICAL_FROM_ISR(int_mask);
+        #else
+          read_len = 0;
+        #endif
+        seq[index] = SEQ_UNSET;
       }
 
       getPacketData(selected_radio, packet_size);
 
-      packet_interface = index;
-      packet_ready = true;
+      ready = true;
     }
   } else {
     // In promiscuous mode, raw packets are
@@ -408,8 +496,11 @@ void receive_callback(uint8_t index, int packet_size) {
 
     getPacketData(selected_radio, packet_size);
 
-    packet_interface = index;
-    packet_ready = true;
+    ready = true;
+  }
+
+  if (ready) {
+      queuePacket(selected_radio, index);
   }
 
   last_rx = millis();
@@ -517,6 +608,9 @@ void flushQueue(RadioInterface* radio) {
   queued_bytes[index] = 0;
   selected_radio->updateAirtime();
   queue_flushing = false;
+  #if HAS_DISPLAY
+    display_tx = true;
+  #endif
 }
 
 void transmit(RadioInterface* radio, uint16_t size) {
@@ -548,7 +642,14 @@ void transmit(RadioInterface* radio, uint16_t size) {
         }
       }
 
-      radio->endPacket(); radio->addAirtime(written);
+      if (!radio->endPacket()) {
+        kiss_indicate_error(ERROR_MODEM_TIMEOUT);
+        kiss_indicate_error(ERROR_TXFAILED);
+        led_indicate_error(5);
+        hard_reset();
+      }
+      radio->addAirtime(written);
+
     } else {
       // In promiscuous mode, we only send out
       // plain raw LoRa packets with a maximum
@@ -783,6 +884,7 @@ void serialCallback(uint8_t 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;
@@ -1024,7 +1126,13 @@ void serialCallback(uint8_t sbyte) {
           bt_start();
           bt_conf_save(true);
         } else if (sbyte == 0x02) {
-          bt_enable_pairing();
+          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) {
@@ -1039,6 +1147,7 @@ void serialCallback(uint8_t sbyte) {
             }
             display_intensity = sbyte;
             di_conf_save(display_intensity);
+            //display_unblank();
         }
 
       #endif
@@ -1056,6 +1165,37 @@ void serialCallback(uint8_t 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);
+            //display_unblank();
+        }
+
+      #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
     }
   }
@@ -1122,7 +1262,7 @@ void validate_status() {
             }
           } else {
             hw_ready = false;
-            Serial.write("No valid radio module found\r\n");
+            Serial.write("No radio module found\r\n");
             #if HAS_DISPLAY
               if (disp_ready) {
                 device_init_done = true;
@@ -1132,6 +1272,7 @@ void validate_status() {
           }
         } else {
           hw_ready = false;
+          Serial.write("Invalid EEPROM checksum\r\n");
           #if HAS_DISPLAY
             if (disp_ready) {
               device_init_done = true;
@@ -1141,6 +1282,7 @@ void validate_status() {
         }
       } else {
         hw_ready = false;
+        Serial.write("Invalid EEPROM configuration\r\n");
         #if HAS_DISPLAY
           if (disp_ready) {
             device_init_done = true;
@@ -1150,6 +1292,7 @@ void validate_status() {
       }
     } 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;
@@ -1171,23 +1314,38 @@ void validate_status() {
 }
 
 void loop() {
-  if (packet_ready) {
-        #if MCU_VARIANT == MCU_ESP32
-        portENTER_CRITICAL(&update_lock);
-        #elif MCU_VARIANT == MCU_NRF52
-        portENTER_CRITICAL();
-        #endif
-        last_rssi = selected_radio->packetRssi();
-        last_snr_raw = selected_radio->packetSnrRaw();
-        #if MCU_VARIANT == MCU_ESP32
-        portEXIT_CRITICAL(&update_lock);
-        #elif MCU_VARIANT == MCU_NRF52
-        portEXIT_CRITICAL();
-        #endif
+    #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) {
+        read_len = modem_packet->len;
+        last_rssi = modem_packet->rssi;
+        last_snr_raw = modem_packet->snr_raw;
+        packet_interface = modem_packet->interface;
+        memcpy(&pbuf, modem_packet->data, modem_packet->len);
+        free(modem_packet);
+        modem_packet = NULL;
+
         kiss_indicate_stat_rssi();
         kiss_indicate_stat_snr();
         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) {
+        memcpy(&pbuf, modem_packet->data, modem_packet->len);
+        read_len = modem_packet->len;
+        last_rssi = modem_packet->rssi;
+        last_snr_raw = modem_packet->snr_raw;
+        packet_interface = modem_packet->interface;
+        free(modem_packet);
+        modem_packet = NULL;
+
+        kiss_indicate_stat_rssi();
+        kiss_indicate_stat_snr();
+        kiss_write_packet(packet_interface);
+      }
+    #endif
 
     bool ready = false;
     for (int i = 0; i < INTERFACE_COUNT; i++) {
@@ -1209,19 +1367,6 @@ void loop() {
             continue;
         }
 
-        // If a higher data rate interface has received a packet after its
-        // loop, it still needs to be the first to transmit, so check if this
-        // is the case.
-        for (int j = 0; j < INTERFACE_COUNT; j++) {
-            if (!interface_obj_sorted[j]->calculateALock() && interface_obj_sorted[j]->getRadioOnline()) {
-                if (interface_obj_sorted[j]->getBitrate() > selected_radio->getBitrate()) {
-                    if (queue_height[interface_obj_sorted[j]->getIndex()] > 0) {
-                        selected_radio = interface_obj_sorted[j];
-                    }
-                }
-            }
-        }
-
         if (queue_height[selected_radio->getIndex()] > 0) {
             uint32_t check_time = millis();
             if (check_time > selected_radio->getPostTxYieldTimeout()) {
@@ -1300,6 +1445,18 @@ void loop() {
   #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() {
@@ -1317,15 +1474,51 @@ void sleep_now() {
       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();
   #endif
 }
 
 void button_event(uint8_t event, unsigned long duration) {
-  if (duration > 2000) {
-    sleep_now();
-  }
+    //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
+      }
+    //}
 }
 
 void poll_buffers() {
diff --git a/ROM.h b/ROM.h
index 4ecd02af2579d4ddda719c40a1df3acef2e5cfa9..9ab3a705e53738c4169d017e6e2e80dcfa0b8a9b 100644 (file)
--- a/ROM.h
+++ b/ROM.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #ifndef ROM_H
-       #define ROM_H
-
-       #define CHECKSUMMED_SIZE 0x0B
-
-       #define PRODUCT_RNODE  0x03
-       #define PRODUCT_HMBRW  0xF0
-       #define PRODUCT_TBEAM  0xE0
-       #define PRODUCT_T32_10 0xB2
-       #define PRODUCT_T32_20 0xB0
-       #define PRODUCT_T32_21 0xB1
-       #define PRODUCT_H32_V2 0xC0
-       #define PRODUCT_H32_V3 0xC1
-    #define PRODUCT_RAK4631 0x10
-    #define PRODUCT_FREENODE 0x20
-    #define MODEL_11 0x11
-    #define MODEL_12 0x12
-    #define MODEL_13 0x13 // RAK4631 LF with WisBlock SX1280 module (LIBSYS002)
-    #define MODEL_14 0x14 // RAK4631 HF with WisBlock SX1280 module (LIBSYS002)
-       #define PRODUCT_TECHO 0x15
-       #define MODEL_16 0x16 // T-Echo 433
-       #define MODEL_17 0x17 // T-Echo 915
-    #define MODEL_21 0x21 // European band, 868MHz
-       #define MODEL_A1 0xA1
-    #define MODEL_A5 0xA5 // T3S3 SX1280 PA
-       #define MODEL_A6 0xA6
-       #define MODEL_A4 0xA4
-       #define MODEL_A9 0xA9
-       #define MODEL_A3 0xA3
-       #define MODEL_A8 0xA8
-       #define MODEL_A2 0xA2
-       #define MODEL_A7 0xA7
-       #define MODEL_B3 0xB3
-       #define MODEL_B8 0xB8
-       #define MODEL_B4 0xB4
-       #define MODEL_B9 0xB9
-       #define MODEL_BA 0xBA
-       #define MODEL_BB 0xBB
-       #define MODEL_C4 0xC4
-       #define MODEL_C9 0xC9
-       #define MODEL_C5 0xC5
-       #define MODEL_CA 0xCA
-       #define MODEL_E4 0xE4
-       #define MODEL_E9 0xE9
-       #define MODEL_E3 0xE3
-       #define MODEL_E8 0xE8
-       #define MODEL_FE 0xFE
-       #define MODEL_FF 0xFF
-
-       #define ADDR_PRODUCT   0x00
-       #define ADDR_MODEL     0x01
-       #define ADDR_HW_REV    0x02
-       #define ADDR_SERIAL    0x03
-       #define ADDR_MADE      0x07
-       #define ADDR_CHKSUM        0x0B
-       #define ADDR_SIGNATURE 0x1B
-       #define ADDR_INFO_LOCK 0x9B
-
-       #define ADDR_CONF_SF   0x9C
-       #define ADDR_CONF_CR   0x9D
-       #define ADDR_CONF_TXP  0x9E
-       #define ADDR_CONF_BW   0x9F
-       #define ADDR_CONF_FREQ 0xA3
-       #define ADDR_CONF_OK   0xA7
-       
-       #define ADDR_CONF_BT   0xB0
-       #define ADDR_CONF_DSET 0xB1
-       #define ADDR_CONF_DINT 0xB2
-       #define ADDR_CONF_DADR 0xB3
-
-       #define INFO_LOCK_BYTE 0x73
-       #define CONF_OK_BYTE   0x73
-       #define BT_ENABLE_BYTE 0x73
-
-       #define EEPROM_RESERVED 200
+  #define ROM_H
+  #define CHECKSUMMED_SIZE  0x0B
+
+  // ROM address map ///////////////
+  #define ADDR_PRODUCT   0x00
+  #define ADDR_MODEL     0x01
+  #define ADDR_HW_REV    0x02
+  #define ADDR_SERIAL    0x03
+  #define ADDR_MADE      0x07
+  #define ADDR_CHKSUM     0x0B
+  #define ADDR_SIGNATURE 0x1B
+  #define ADDR_INFO_LOCK 0x9B
+
+  #define ADDR_CONF_SF   0x9C
+  #define ADDR_CONF_CR   0x9D
+  #define ADDR_CONF_TXP  0x9E
+  #define ADDR_CONF_BW   0x9F
+  #define ADDR_CONF_FREQ 0xA3
+  #define ADDR_CONF_OK   0xA7
+
+  #define ADDR_CONF_BT   0xB0
+  #define ADDR_CONF_DSET 0xB1
+  #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 INFO_LOCK_BYTE 0x73
+  #define CONF_OK_BYTE   0x73
+  #define BT_ENABLE_BYTE 0x73
+
+  #define EEPROM_RESERVED 200
+  //////////////////////////////////
 
 #endif
index fe45ceb888c1c2dc85a4bd0cc262fe8b1f64a302..f512b758232f15629fe259c1b1966818a64dc674 100644 (file)
--- a/Radio.cpp
+++ b/Radio.cpp
@@ -488,34 +488,34 @@ int sx126x::beginPacket(int implicitHeader)
 
 int sx126x::endPacket()
 {
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
 
-      // put in single TX mode
-      uint8_t timeout[3] = {0};
-      executeOpcode(OP_TX_6X, timeout, 3);
+    // put in single TX mode
+    uint8_t timeout[3] = {0};
+    executeOpcode(OP_TX_6X, timeout, 3);
 
-      uint8_t buf[2];
+    uint8_t buf[2];
 
-      buf[0] = 0x00;
-      buf[1] = 0x00;
+    buf[0] = 0x00;
+    buf[1] = 0x00;
 
-      executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+    executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
 
-      // wait for TX done
-      while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) {
+    // wait for TX done
+    while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) {
         buf[0] = 0x00;
         buf[1] = 0x00;
         executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
         yield();
-      }
+    }
 
-      // clear IRQ's
+    // 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);
-  return 1;
+    uint8_t mask[2];
+    mask[0] = 0x00;
+    mask[1] = IRQ_TX_DONE_MASK_6X;
+    executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
+    return 1;
 }
 
 uint8_t sx126x::modemStatus() {
@@ -561,7 +561,7 @@ uint8_t sx126x::packetRssiRaw() {
     return buf[2];
 }
 
-int ISR_VECT sx126x::packetRssi() {
+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);
@@ -748,6 +748,10 @@ void sx126x::enableTCXO() {
       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};
     #else
       uint8_t buf[4] = {0};
     #endif
@@ -995,7 +999,9 @@ void sx126x::updateBitrate() {
         _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*10;
+        _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;
@@ -1246,23 +1252,27 @@ uint8_t sx127x::packetRssiRaw() {
     return pkt_rssi_value;
 }
 
-int ISR_VECT sx127x::packetRssi() {
-    int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
-    int pkt_snr = packetSnr();
-
-    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(uint8_t pkt_snr_raw) {
+  int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
+  int pkt_snr;
+  if (pkt_snr_raw == 0xFF) {
+      pkt_snr = packetSnr();
+  } else {
+      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;
 }
 
+
 uint8_t ISR_VECT sx127x::packetSnrRaw() {
     return readRegister(REG_PKT_SNR_VALUE_7X);
 }
@@ -1531,7 +1541,9 @@ void sx127x::updateBitrate() {
         _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*10;
+        _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;
@@ -2028,7 +2040,7 @@ uint8_t sx128x::packetRssiRaw() {
     return buf[0];
 }
 
-int ISR_VECT sx128x::packetRssi() {
+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);
@@ -2626,7 +2638,9 @@ void sx128x::updateBitrate() {
         _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 = 10;
+        _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) {
index 681db4ff4f8a8b162b4063c53ea6a3d0c90206aa..43abef306fdd6ff6a05d8afbb62e54c79ee93668 100644 (file)
--- a/Radio.hpp
+++ b/Radio.hpp
@@ -1,7 +1,7 @@
 // Copyright (c) Sandeep Mistry. All rights reserved.
 // Licensed under the MIT license.
 
-// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva
+// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
 // Obviously still under the MIT license.
 
 #ifndef RADIO_H
 #define LORA_PREAMBLE_TARGET_MS   15
 #define LORA_PREAMBLE_FAST_TARGET_MS 1
 #define LORA_FAST_BITRATE_THRESHOLD 40000
+#define CSMA_SLOT_MAX_MS 100
+#define CSMA_SLOT_MIN_MS 24
 
 #define RSSI_OFFSET 157
 
 #define PHY_HEADER_LORA_SYMBOLS 8
 
 #define _e 2.71828183
-#define _S 10.0
+#define _S 12.5
 
 // Status flags
 const uint8_t SIG_DETECT = 0x01;
@@ -71,8 +73,8 @@ public:
      _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), 
      _dcd_count(0), _dcd(false), _dcd_led(false),
     _dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0),
-    _post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1),
-    _csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.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) {};
     virtual int begin() = 0;
@@ -81,7 +83,7 @@ public:
     virtual int beginPacket(int implicitHeader = false) = 0;
     virtual int endPacket() = 0;
 
-    virtual int packetRssi() = 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;
@@ -281,8 +283,8 @@ public:
     float getLongtermChannelUtil() { return _longterm_channel_util; };
     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)))*255.0);
-    };
+      _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; };
     void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; };
     int getCSMASlotMS() { return _csma_slot_ms; };
@@ -323,6 +325,7 @@ protected:
     int _csma_slot_ms;
     float _csma_p_min;
     float _csma_p_max;
+    float _csma_b_speed;
     long _preambleLength;
     float _lora_symbol_time_ms;
     float _lora_symbol_rate;
@@ -343,7 +346,7 @@ public:
   int beginPacket(int implicitHeader = false);
   int endPacket();
 
-  int packetRssi();
+  int packetRssi(uint8_t pkt_snr_raw = 0xFF);
   int currentRssi();
   uint8_t packetRssiRaw();
   uint8_t currentRssiRaw();
@@ -463,7 +466,7 @@ public:
   int beginPacket(int implicitHeader = false);
   int endPacket();
 
-  int packetRssi();
+  int packetRssi(uint8_t pkt_snr_raw = 0xFF);
   int currentRssi();
   uint8_t packetRssiRaw();
   uint8_t currentRssiRaw();
@@ -557,7 +560,7 @@ public:
   int beginPacket(int implicitHeader = false);
   int endPacket();
 
-  int packetRssi();
+  int packetRssi(uint8_t pkt_snr_raw = 0xFF);
   int currentRssi();
   uint8_t packetRssiRaw();
   uint8_t currentRssiRaw();
index ee7e5b1c8b5e5aebb7ca3e85354afcd6490eb047..8fb621b57760363a5fa878a17ce9a0143576fde5 100644 (file)
Binary files a/Release/console_image.bin and b/Release/console_image.bin differ
index 36138f60ff885b15bb166927b443390ecf8568b8..c26099924f6685c0e25540035d3466d4ac59921c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2023, Mark Qvist
+// 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
@@ -35,7 +35,7 @@
 
 #include "ROM.h"
 #include "Framing.h"
-#include "MD5.h"
+#include "src/misc/MD5.h"
 
 #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
 uint8_t eeprom_read(uint32_t mapped_addr);
@@ -43,6 +43,9 @@ uint8_t eeprom_read(uint32_t mapped_addr);
 
 #if HAS_DISPLAY == true
   #include "Display.h"
+#else
+       void display_unblank() {}
+       bool display_blanked = false;
 #endif
 
 #if HAS_BLUETOOTH == true || HAS_BLE == true
@@ -65,11 +68,11 @@ uint8_t eeprom_read(uint32_t mapped_addr);
   #if BOARD_MODEL == BOARD_HELTEC32_V3
     //https://github.com/espressif/esp-idf/issues/8855
     #include "hal/wdt_hal.h"
-       #elif BOARD_MODEL == BOARD_T3S3
-               #include "hal/wdt_hal.h"
-  #else BOARD_MODEL != BOARD_T3S3
-         #include "soc/rtc_wdt.h"
-       #endif
+  #elif BOARD_MODEL == BOARD_T3S3
+       #include "hal/wdt_hal.h"
+  #else
+       #include "hal/wdt_hal.h"
+  #endif
   #define ISR_VECT IRAM_ATTR
 #else
   #define ISR_VECT
@@ -92,7 +95,20 @@ uint8_t boot_vector = 0x00;
        uint8_t npr = 0;
   uint8_t npg = 0;
   uint8_t npb = 0;
+  float npi = NP_M;
   bool pixels_started = false;
+
+  void led_set_intensity(uint8_t intensity) {
+       npi = (float)intensity/255.0;
+  }
+
+  void led_init() {
+       if (EEPROM.read(eeprom_addr(ADDR_CONF_PSET)) == CONF_OK_BYTE) {
+               uint8_t int_val = EEPROM.read(eeprom_addr(ADDR_CONF_PINT));
+               led_set_intensity(int_val);
+       }
+  }
+
   void npset(uint8_t r, uint8_t g, uint8_t b) {
        if (pixels_started != true) {
                pixels.begin();
@@ -101,7 +117,7 @@ uint8_t boot_vector = 0x00;
 
        if (r != npr || g != npg || b != npb) {
                npr = r; npg = g; npb = b;
-               pixels.setPixelColor(0, pixels.Color(npr*NP_M, npg*NP_M, npb*NP_M));
+               pixels.setPixelColor(0, pixels.Color(npr*npi, npg*npi, npb*npi));
                pixels.show();
        }
   }
@@ -152,6 +168,16 @@ uint8_t boot_vector = 0x00;
                void led_rx_off() {     digitalWrite(pin_led_rx, LOW); }
                void led_tx_on()  { digitalWrite(pin_led_tx, LOW); }
                void led_tx_off() { digitalWrite(pin_led_tx, HIGH); }
+       #elif BOARD_MODEL == BOARD_TDECK
+               void led_rx_on()  { }
+               void led_rx_off() {     }
+               void led_tx_on()  { }
+               void led_tx_off() { }
+       #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+               void led_rx_on()  { }
+               void led_rx_off() {     }
+               void led_tx_on()  { }
+               void led_tx_off() { }
        #elif BOARD_MODEL == BOARD_LORA32_V1_0
                #if defined(EXTERNAL_LEDS)
                        void led_rx_on()  { digitalWrite(pin_led_rx, HIGH); }
@@ -376,8 +402,8 @@ unsigned long led_standby_ticks = 0;
 #if MCU_VARIANT == MCU_ESP32
 
        #if HAS_NP == true
-               int led_standby_lng = 100;
-               int led_standby_cut = 200;
+               int led_standby_lng = 200;
+               int led_standby_cut = 100;
                int led_standby_min = 0;
                int led_standby_max = 375+led_standby_lng;
                int led_notready_min = 0;
@@ -441,7 +467,7 @@ int8_t  led_standby_direction = 0;
                                } else {
                                        led_standby_intensity = led_standby_ti;
                                }
-                       npset(0x00, 0x00, led_standby_intensity);
+                       npset(led_standby_intensity/3, led_standby_intensity/3, led_standby_intensity/3);
                        }
                }
 
@@ -588,18 +614,17 @@ void serial_write(uint8_t byte) {
                        Serial.write(byte);
                } else {
                        SerialBT.write(byte);
-
-            #if MCU_VARIANT == MCU_NRF52 && HAS_BLE
-            // This ensures that the TX buffer is flushed after a frame is queued in serial.
-            // serial_in_frame is used to ensure that the flush only happens at the end of the frame
-            if (serial_in_frame && byte == FEND) {
-                SerialBT.flushTXD();
-                serial_in_frame = false;
-            }
-            else if (!serial_in_frame && byte == FEND) {
-                serial_in_frame = true;
-            }
-            #endif
+      #if MCU_VARIANT == MCU_NRF52 && HAS_BLE
+             // This ensures that the TX buffer is flushed after a frame is queued in serial.
+             // serial_in_frame is used to ensure that the flush only happens at the end of the frame
+             if (serial_in_frame && byte == FEND) {
+                 SerialBT.flushTXD();
+                 serial_in_frame = false;
+             }
+             else if (!serial_in_frame && byte == FEND) {
+                 serial_in_frame = true;
+             }
+      #endif
                }
        #else
                Serial.write(byte);
@@ -1022,6 +1047,8 @@ void setTXPower(RadioInterface* radio, int txp) {
     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_AB) 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);
@@ -1031,6 +1058,12 @@ void setTXPower(RadioInterface* radio, int txp) {
     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_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);
@@ -1216,11 +1249,11 @@ void promisc_disable() {
 #endif
 
 bool eeprom_info_locked() {
-    #if HAS_EEPROM
-           uint8_t lock_byte = EEPROM.read(eeprom_addr(ADDR_INFO_LOCK));
-    #elif MCU_VARIANT == MCU_NRF52
-        uint8_t lock_byte = eeprom_read(eeprom_addr(ADDR_INFO_LOCK));
-    #endif
+  #if HAS_EEPROM
+    uint8_t lock_byte = EEPROM.read(eeprom_addr(ADDR_INFO_LOCK));
+  #elif MCU_VARIANT == MCU_NRF52
+    uint8_t lock_byte = eeprom_read(eeprom_addr(ADDR_INFO_LOCK));
+  #endif
        if (lock_byte == INFO_LOCK_BYTE) {
                return true;
        } else {
@@ -1297,15 +1330,15 @@ void eeprom_update(int mapped_addr, uint8_t 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();
+            // 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;
-        }
+    if (written_bytes >= 4) {
+      file.close();
+      file.open(EEPROM_FILE, FILE_O_WRITE);
+      written_bytes = 0;
+    }
        #endif
 }
 
@@ -1337,16 +1370,16 @@ bool eeprom_lock_set() {
 }
 
 bool eeprom_product_valid() {
-    #if HAS_EEPROM
-           uint8_t rval = EEPROM.read(eeprom_addr(ADDR_PRODUCT));
-    #elif MCU_VARIANT == MCU_NRF52
-           uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
-    #endif
+  #if HAS_EEPROM
+    uint8_t rval = EEPROM.read(eeprom_addr(ADDR_PRODUCT));
+  #elif MCU_VARIANT == MCU_NRF52
+    uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
+  #endif
 
        #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) {
+       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) {
        #elif PLATFORM == PLATFORM_NRF52
-       if (rval == PRODUCT_TECHO || rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW || rval == PRODUCT_FREENODE) {
+       if (rval == PRODUCT_TECHO || rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW || rval == PRODUCT_OPENCOM_XL) {
        #else
        if (false) {
        #endif
@@ -1368,14 +1401,18 @@ bool eeprom_model_valid() {
        if (model == MODEL_A3 || model == MODEL_A8) {
        #elif BOARD_MODEL == BOARD_RNODE_NG_21
        if (model == MODEL_A2 || model == MODEL_A7) {
+       #elif BOARD_MODEL == BOARD_RNODE_NG_22
+       if (model == MODEL_A1 || model == MODEL_A6 || model == MODEL_A5 || model == MODEL_AA) {
        #elif BOARD_MODEL == BOARD_T3S3
        if (model == MODEL_A1 || model == MODEL_A5 || model == MODEL_A6) {
-       #elif BOARD_MODEL == BOARD_HMBRW
-       if (model == MODEL_FF || model == MODEL_FE) {
        #elif BOARD_MODEL == BOARD_TBEAM
        if (model == MODEL_E4 || model == MODEL_E9 || model == MODEL_E3 || model == MODEL_E8) {
        #elif BOARD_MODEL == BOARD_TECHO
        if (model == MODEL_16 || model == MODEL_17) {
+       #elif BOARD_MODEL == BOARD_TDECK
+       if (model == MODEL_D4 || model == MODEL_D9) {
+       #elif BOARD_MODEL == BOARD_TBEAM_S_V1
+       if (model == MODEL_DB || model == MODEL_DC) {
        #elif BOARD_MODEL == BOARD_LORA32_V1_0
        if (model == MODEL_BA || model == MODEL_BB) {
        #elif BOARD_MODEL == BOARD_LORA32_V2_0
@@ -1390,6 +1427,8 @@ bool eeprom_model_valid() {
     if (model == MODEL_11 || model == MODEL_12 || model == MODEL_13 || model == MODEL_14 || 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
@@ -1447,16 +1486,16 @@ bool eeprom_checksum_valid() {
 void bt_conf_save(bool is_enabled) {
        if (is_enabled) {
                eeprom_update(eeprom_addr(ADDR_CONF_BT), BT_ENABLE_BYTE);
-        #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
-            // have to do a flush because we're only writing 1 byte and it syncs after 8
-            eeprom_flush();
-        #endif
+      #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+        // have to do a flush because we're only writing 1 byte and it syncs after 8
+        eeprom_flush();
+      #endif
        } else {
                eeprom_update(eeprom_addr(ADDR_CONF_BT), 0x00);
-        #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
-            // have to do a flush because we're only writing 1 byte and it syncs after 8
-            eeprom_flush();
-        #endif
+    #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+      // have to do a flush because we're only writing 1 byte and it syncs after 8
+      eeprom_flush();
+    #endif
        }
 }
 
@@ -1468,6 +1507,25 @@ void da_conf_save(uint8_t dadr) {
        eeprom_update(eeprom_addr(ADDR_CONF_DADR), dadr);
 }
 
+void db_conf_save(uint8_t val) {
+       #if HAS_DISPLAY
+               if (val == 0x00) {
+                       display_blanking_enabled = false;
+               } else {
+                       display_blanking_enabled = true;
+                       //display_blanking_timeout = val*1000;
+               }
+               eeprom_update(eeprom_addr(ADDR_CONF_BSET), CONF_OK_BYTE);
+               eeprom_update(eeprom_addr(ADDR_CONF_DBLK), val);
+       #endif
+}
+
+void np_int_conf_save(uint8_t p_int) {
+       eeprom_update(eeprom_addr(ADDR_CONF_PSET), CONF_OK_BYTE);
+       eeprom_update(eeprom_addr(ADDR_CONF_PINT), p_int);
+}
+
+
 bool eeprom_have_conf() {
     #if HAS_EEPROM
            if (EEPROM.read(eeprom_addr(ADDR_CONF_OK)) == CONF_OK_BYTE) {
@@ -1542,4 +1600,4 @@ void unlock_rom() {
        eeprom_erase();
 }
 
-#include "src/misc/FIFOBuffer.h"
\ No newline at end of file
+#include "src/misc/FIFOBuffer.h"
diff --git a/esp32_btbufs.py b/esp32_btbufs.py
new file mode 100755 (executable)
index 0000000..8a5b15e
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+import sys
+
+try:
+    target_path = sys.argv[1]
+    rxbuf_size = 0; rxbuf_minsize = 6144
+    txbuf_size = 0; txbuf_minsize = 384
+    line_index = 0
+    rx_line_index = 0
+    tx_line_index = 0
+    with open(target_path) as sf:
+        for line in sf:
+            line_index += 1
+            if line.startswith("#define RX_QUEUE_SIZE"):
+                ents = line.split(" ")
+                try:
+                    rxbuf_size = int(ents[2])
+                    rx_line_index = line_index
+                except Exception as e:
+                    print(f"Could not parse Bluetooth RX_QUEUE_SIZE: {e}")
+
+            if line.startswith("#define TX_QUEUE_SIZE"):
+                ents = line.split(" ")
+                try:
+                    txbuf_size = int(ents[2])
+                    tx_line_index = line_index
+                except Exception as e:
+                    print(f"Could not parse Bluetooth RX_QUEUE_SIZE: {e}")
+
+            if rxbuf_size != 0 and txbuf_size != 0:
+                break
+
+    if rxbuf_size < rxbuf_minsize:
+        print(f"Error: The configured ESP32 Bluetooth RX buffer size is too small, please set it to at least {rxbuf_minsize} and try compiling again.")
+        print(f"The buffer configuration can be modified in line {rx_line_index} of: {target_path}")
+        exit(1)
+
+    if txbuf_size < txbuf_minsize:
+        print(f"Error: The configured ESP32 Bluetooth TX buffer size is too small, please set it to at least {txbuf_minsize} and try compiling again.")
+        print(f"The buffer configuration can be modified in line {tx_line_index} of: {target_path}")
+        exit(1)
+
+    exit(0)
+
+except Exception as e:
+    print(f"Could not determine ESP32 Bluetooth buffer configuration: {e}")
+    print("Please fix this error and try again")
\ No newline at end of file
index d6b81fe30694535062a256e38f19c41933ab261c..fa608683cd1199faf0298fe5accaa8f44510b21d 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (C) 2023, Mark Qvist
+# 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
index c54a9d1a35fdcbc0269a5548e1f7559021b85755..e1608d82e1ed26ae93be12f6ab455c21e4237bf6 100644 (file)
@@ -1,6 +1,6 @@
 #!/bin/python3
 
-# Copyright (C) 2023, Mark Qvist
+# 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
diff --git a/src/ble/BLESerial.cpp b/src/ble/BLESerial.cpp
new file mode 100644 (file)
index 0000000..94cbb2f
--- /dev/null
@@ -0,0 +1,155 @@
+// 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/>.
+
+// This class is for BLE serial functionality on ESP32 boards ONLY
+
+#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
diff --git a/src/ble/BLESerial.h b/src/ble/BLESerial.h
new file mode 100644 (file)
index 0000000..1ad2e25
--- /dev/null
@@ -0,0 +1,135 @@
+// 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/>.
+
+// This class is for BLE serial functionality on ESP32 boards ONLY
+
+#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
diff --git a/src/misc/MD5.cpp b/src/misc/MD5.cpp
new file mode 100644 (file)
index 0000000..56daa77
--- /dev/null
@@ -0,0 +1,301 @@
+#include "MD5.h"
+
+MD5::MD5()
+{
+       //nothing
+       return;
+}
+
+char* MD5::make_digest(const unsigned char *digest, int len) /* {{{ */
+{
+       char * md5str = (char*) malloc(sizeof(char)*(len*2+1));
+       static const char hexits[17] = "0123456789abcdef";
+       int i;
+
+       for (i = 0; i < len; i++) {
+               md5str[i * 2]       = hexits[digest[i] >> 4];
+               md5str[(i * 2) + 1] = hexits[digest[i] &  0x0F];
+       }
+       md5str[len * 2] = '\0';
+       return md5str;
+}
+
+/*
+ * The basic MD5 functions.
+ *
+ * E and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ * E() has been used instead of F() because F() is already defined in the Arduino core
+ */
+#define E(x, y, z)                     ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)                     ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)                     ((x) ^ (y) ^ (z))
+#define I(x, y, z)                     ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+       (a) += f((b), (c), (d)) + (x) + (t); \
+       (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+       (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization.  Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+# define SET(n) \
+       (*(MD5_u32plus *)&ptr[(n) * 4])
+# define GET(n) \
+       SET(n)
+#else
+# define SET(n) \
+       (ctx->block[(n)] = \
+       (MD5_u32plus)ptr[(n) * 4] | \
+       ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+       ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+       ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+# define GET(n) \
+       (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters.  There are no alignment requirements.
+ */
+const void *MD5::body(void *ctxBuf, const void *data, size_t size)
+{
+       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
+       const unsigned char *ptr;
+       MD5_u32plus a, b, c, d;
+       MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+       ptr = (unsigned char*)data;
+
+       a = ctx->a;
+       b = ctx->b;
+       c = ctx->c;
+       d = ctx->d;
+
+       do {
+               saved_a = a;
+               saved_b = b;
+               saved_c = c;
+               saved_d = d;
+
+/* Round 1
+ * E() has been used instead of F() because F() is already defined in the Arduino core
+ */
+               STEP(E, a, b, c, d, SET(0), 0xd76aa478, 7)
+               STEP(E, d, a, b, c, SET(1), 0xe8c7b756, 12)
+               STEP(E, c, d, a, b, SET(2), 0x242070db, 17)
+               STEP(E, b, c, d, a, SET(3), 0xc1bdceee, 22)
+               STEP(E, a, b, c, d, SET(4), 0xf57c0faf, 7)
+               STEP(E, d, a, b, c, SET(5), 0x4787c62a, 12)
+               STEP(E, c, d, a, b, SET(6), 0xa8304613, 17)
+               STEP(E, b, c, d, a, SET(7), 0xfd469501, 22)
+               STEP(E, a, b, c, d, SET(8), 0x698098d8, 7)
+               STEP(E, d, a, b, c, SET(9), 0x8b44f7af, 12)
+               STEP(E, c, d, a, b, SET(10), 0xffff5bb1, 17)
+               STEP(E, b, c, d, a, SET(11), 0x895cd7be, 22)
+               STEP(E, a, b, c, d, SET(12), 0x6b901122, 7)
+               STEP(E, d, a, b, c, SET(13), 0xfd987193, 12)
+               STEP(E, c, d, a, b, SET(14), 0xa679438e, 17)
+               STEP(E, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+               STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+               STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+               STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+               STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+               STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+               STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+               STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+               STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+               STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+               STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+               STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+               STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+               STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+               STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+               STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+               STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+               STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+               STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+               STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+               STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+               STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+               STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+               STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+               STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+               STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+               STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+               STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+               STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+               STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+               STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+               STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+               STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+               STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+               STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+               STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+               STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+               STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+               STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+               STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+               STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+               STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+               STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+               STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+               STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+               STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+               STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+               STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+               STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+               a += saved_a;
+               b += saved_b;
+               c += saved_c;
+               d += saved_d;
+
+               ptr += 64;
+       } while (size -= 64);
+
+       ctx->a = a;
+       ctx->b = b;
+       ctx->c = c;
+       ctx->d = d;
+
+       return ptr;
+}
+
+void MD5::MD5Init(void *ctxBuf)
+{
+       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
+       ctx->a = 0x67452301;
+       ctx->b = 0xefcdab89;
+       ctx->c = 0x98badcfe;
+       ctx->d = 0x10325476;
+
+       ctx->lo = 0;
+       ctx->hi = 0;
+
+    memset(ctx->block, 0, sizeof(ctx->block));
+    memset(ctx->buffer, 0, sizeof(ctx->buffer));
+}
+
+void MD5::MD5Update(void *ctxBuf, const void *data, size_t size)
+{
+       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
+       MD5_u32plus saved_lo;
+       MD5_u32plus used, free;
+
+       saved_lo = ctx->lo;
+       if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
+               ctx->hi++;
+       }
+       ctx->hi += size >> 29;
+
+       used = saved_lo & 0x3f;
+
+       if (used) {
+               free = 64 - used;
+
+               if (size < free) {
+                       memcpy(&ctx->buffer[used], data, size);
+                       return;
+               }
+
+               memcpy(&ctx->buffer[used], data, free);
+               data = (unsigned char *)data + free;
+               size -= free;
+               body(ctx, ctx->buffer, 64);
+       }
+
+       if (size >= 64) {
+               data = body(ctx, data, size & ~(size_t)0x3f);
+               size &= 0x3f;
+       }
+
+       memcpy(ctx->buffer, data, size);
+}
+
+void MD5::MD5Final(unsigned char *result, void *ctxBuf)
+{
+       MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
+       MD5_u32plus used, free;
+
+       used = ctx->lo & 0x3f;
+
+       ctx->buffer[used++] = 0x80;
+
+       free = 64 - used;
+
+       if (free < 8) {
+               memset(&ctx->buffer[used], 0, free);
+               body(ctx, ctx->buffer, 64);
+               used = 0;
+               free = 64;
+       }
+
+       memset(&ctx->buffer[used], 0, free - 8);
+
+       ctx->lo <<= 3;
+       ctx->buffer[56] = ctx->lo;
+       ctx->buffer[57] = ctx->lo >> 8;
+       ctx->buffer[58] = ctx->lo >> 16;
+       ctx->buffer[59] = ctx->lo >> 24;
+       ctx->buffer[60] = ctx->hi;
+       ctx->buffer[61] = ctx->hi >> 8;
+       ctx->buffer[62] = ctx->hi >> 16;
+       ctx->buffer[63] = ctx->hi >> 24;
+
+       body(ctx, ctx->buffer, 64);
+
+       result[0] = ctx->a;
+       result[1] = ctx->a >> 8;
+       result[2] = ctx->a >> 16;
+       result[3] = ctx->a >> 24;
+       result[4] = ctx->b;
+       result[5] = ctx->b >> 8;
+       result[6] = ctx->b >> 16;
+       result[7] = ctx->b >> 24;
+       result[8] = ctx->c;
+       result[9] = ctx->c >> 8;
+       result[10] = ctx->c >> 16;
+       result[11] = ctx->c >> 24;
+       result[12] = ctx->d;
+       result[13] = ctx->d >> 8;
+       result[14] = ctx->d >> 16;
+       result[15] = ctx->d >> 24;
+
+       memset(ctx, 0, sizeof(*ctx));
+}
+unsigned char* MD5::make_hash(char *arg)
+{
+       MD5_CTX context;
+       unsigned char * hash = (unsigned char *) malloc(16);
+       MD5Init(&context);
+       MD5Update(&context, arg, strlen(arg));
+       MD5Final(hash, &context);
+       return hash;
+}
+unsigned char* MD5::make_hash(char *arg,size_t size)
+{
+       MD5_CTX context;
+       unsigned char * hash = (unsigned char *) malloc(16);
+       MD5Init(&context);
+       MD5Update(&context, arg, size);
+       MD5Final(hash, &context);
+       return hash;
+}
\ No newline at end of file
diff --git a/src/misc/MD5.h b/src/misc/MD5.h
new file mode 100644 (file)
index 0000000..3ec8d81
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef MD5_h
+#define MD5_h
+
+#include "Arduino.h"
+
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Written by Solar Designer <solar at openwall.com> in 2001, and placed
+ * in the public domain.  There's absolutely no warranty.
+ *
+ * This differs from Colin Plumb's older public domain implementation in
+ * that no 32-bit integer data type is required, there's no compile-time
+ * endianness configuration, and the function prototypes match OpenSSL's.
+ * The primary goals are portability and ease of use.
+ *
+ * This implementation is meant to be fast, but not as fast as possible.
+ * Some known optimizations are not included to reduce source code size
+ * and avoid compile-time configuration.
+ */
+
+/*
+ * Updated by Scott MacVicar for arduino
+ * <scott@macvicar.net>
+ */
+
+#include <string.h>
+
+typedef unsigned long MD5_u32plus;
+
+typedef struct {
+       MD5_u32plus lo, hi;
+       MD5_u32plus a, b, c, d;
+       unsigned char buffer[64];
+       MD5_u32plus block[16];
+} MD5_CTX;
+
+class MD5
+{
+public:
+       MD5();
+       static unsigned char* make_hash(char *arg);
+       static unsigned char* make_hash(char *arg,size_t size);
+       static char* make_digest(const unsigned char *digest, int len);
+       static const void *body(void *ctxBuf, const void *data, size_t size);
+       static void MD5Init(void *ctxBuf);
+       static void MD5Final(unsigned char *result, void *ctxBuf);
+       static void MD5Update(void *ctxBuf, const void *data, size_t size);
+};
+
+#endif
\ No newline at end of file