diff --git a/.github/ISSUE_TEMPLATE/🐛-bug-report.md b/.github/ISSUE_TEMPLATE/🐛-bug-report.md index 77ad6c2..ddb78fc 100644 --- a/.github/ISSUE_TEMPLATE/🐛-bug-report.md +++ b/.github/ISSUE_TEMPLATE/🐛-bug-report.md @@ -15,7 +15,11 @@ Before creating a bug report on this issue tracker, you **must** read the [Contr - After reading the [Contribution Guidelines](https://github.com/markqvist/Reticulum/blob/master/Contributing.md), delete this section from your bug report. **Describe the Bug** -A clear and concise description of what the bug is. +First of all: Is this really a bug? Is it reproducible? + +If this is a request for help because something is not working as you expected, stop right here, and go to the [discussions](https://github.com/markqvist/Reticulum/discussions) instead, where you can post your questions and get help from other users. + +If this really is a bug or issue with the software, remove this section of the template, and provide **a clear and concise description of what the bug is**. **To Reproduce** Describe in detail how to reproduce the bug. @@ -24,7 +28,7 @@ Describe in detail how to reproduce the bug. A clear and concise description of what you expected to happen. **Logs & Screenshots** -Please include any relevant log output. If applicable, also add screenshots to help explain your problem. +Please include any relevant log output. If applicable, also add screenshots to help explain your problem. In most cases, without any relevant log output, we will not be able to determine the cause of the bug, or reproduce it. **System Information** - OS and version diff --git a/BLESerial.cpp b/BLESerial.cpp new file mode 100644 index 0000000..2957755 --- /dev/null +++ b/BLESerial.cpp @@ -0,0 +1,171 @@ +// 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 . + +#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::disconnect() { + if (ble_server->getConnectedCount() > 0) { + uint16_t conn_id = ble_server->getConnId(); + // Serial.printf("Have connected: %d\n", conn_id); + ble_server->disconnect(conn_id); + // Serial.println("Disconnected"); + } else { + // Serial.println("No connected"); + } +} + +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(); + this->startAdvertising(); +} + +void BLESerial::startAdvertising() { + 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::stopAdvertising() { + ble_adv = BLEDevice::getAdvertising(); + ble_adv->stop(); +} + +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 \ No newline at end of file diff --git a/BLESerial.h b/BLESerial.h new file mode 100644 index 0000000..f845b56 --- /dev/null +++ b/BLESerial.h @@ -0,0 +1,136 @@ +// 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 . + +#include "Boards.h" + +#if PLATFORM != PLATFORM_NRF52 +#if HAS_BLE + +#include + +#include +#include +#include +#include + +template +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 disconnect(); + void startAdvertising(); + void stopAdvertising(); + 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_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 \ No newline at end of file diff --git a/Bluetooth.h b/Bluetooth.h index 38951f7..616c4b6 100644 --- a/Bluetooth.h +++ b/Bluetooth.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 @@ -14,7 +14,6 @@ // along with this program. If not, see . #if MCU_VARIANT == MCU_ESP32 - #elif MCU_VARIANT == MCU_NRF52 #endif @@ -28,19 +27,22 @@ #elif HAS_BLE == true #include "esp_bt_main.h" #include "esp_bt_device.h" - // TODO: Remove - #define SerialBT Serial + #include "BLESerial.h" + BLESerial SerialBT; #endif #elif MCU_VARIANT == MCU_NRF52 #include #include - BLEUart SerialBT; + #define BLE_RX_BUF 6144 + BLEUart SerialBT(BLE_RX_BUF); BLEDis bledis; BLEBas blebas; + bool SerialBT_init = false; #endif #define BT_PAIRING_TIMEOUT 35000 +#define BLE_FLUSH_TIMEOUT 20 uint32_t bt_pairing_started = 0; #define BT_DEV_ADDR_LEN 6 @@ -64,6 +66,7 @@ char bt_devname[11]; } void bt_stop() { + display_unblank(); if (bt_state != BT_STATE_OFF) { SerialBT.end(); bt_allow_pairing = false; @@ -72,6 +75,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; @@ -79,6 +83,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(); @@ -86,12 +91,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 { @@ -99,7 +106,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; @@ -162,29 +170,155 @@ 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; + bool bt_setup_hw(); void bt_security_setup(); + 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_start() { + // Serial.println("BT start"); + display_unblank(); + if (bt_state == BT_STATE_OFF) { + bt_state = BT_STATE_ON; + SerialBT.begin(bt_devname); + SerialBT.setTimeout(10); } } + void bt_stop() { + // Serial.println("BT stop"); + display_unblank(); + if (bt_state != BT_STATE_OFF) { + bt_allow_pairing = false; + bt_state = BT_STATE_OFF; + SerialBT.end(); + } + } + + bool bt_init() { + // Serial.println("BT init"); + bt_state = BT_STATE_OFF; + if (bt_setup_hw()) { + if (bt_enabled && !console_active) bt_start(); + return true; + } else { + return false; + } + } + + 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_enable_pairing() { + // Serial.println("BT enable pairing"); + display_unblank(); + if (bt_state == BT_STATE_OFF) bt_start(); + + bt_security_setup(); + + bt_allow_pairing = true; + bt_pairing_started = millis(); + bt_state = BT_STATE_PAIRING; + bt_ssp_pin = pairing_pin; + } + void bt_disable_pairing() { + // Serial.println("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) { - bt_state = BT_STATE_CONNECTED; + void bt_passkey_notify_callback(uint32_t passkey) { + // Serial.printf("Got passkey notification: %d\n", passkey); + if (bt_allow_pairing) { + bt_ssp_pin = passkey; + bt_pairing_started = millis(); + kiss_indicate_btpin(); + } else { + // Serial.println("Pairing not allowed, re-init"); + SerialBT.disconnect(); + } + } + + bool bt_confirm_pin_callback(uint32_t pin) { + // Serial.printf("Confirm PIN callback: %d\n", pin); + return true; + } + + 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; + } + + 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; + if (bt_state == BT_STATE_PAIRING) { + // Serial.println("Pairing complete, disconnecting"); + delay(2000); SerialBT.disconnect(); + } else { bt_state = BT_STATE_CONNECTED; } + } else { + // Serial.println("Authentication fail"); + ble_authenticated = false; + bt_state = BT_STATE_ON; + bt_update_passkey(); + 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; + if (bt_state != BT_STATE_PAIRING) { 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; } bool bt_setup_hw() { + // Serial.println("BT setup hw"); if (!bt_ready) { if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) { bt_enabled = true; @@ -205,8 +339,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; @@ -216,156 +350,237 @@ char bt_devname[11]; } else { return false; } } - void bt_start() { - if (bt_state == BT_STATE_OFF) { - bt_state = BT_STATE_ON; - // TODO: Implement - } - } + void bt_security_setup() { + // Serial.println("Executing BT security setup"); + if (pairing_pin == 0) { bt_update_passkey(); } + uint32_t passkey = pairing_pin; + // Serial.printf("Passkey is %d\n", passkey); - bool bt_init() { - bt_state = BT_STATE_OFF; - if (bt_setup_hw()) { - if (bt_enabled && !console_active) bt_start(); - return true; - } else { - return false; - } - } + 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; - 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; + 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)); } void update_bt() { 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); + uint32_t pairing_pin = 0; -void bt_stop() { - if (bt_state != BT_STATE_OFF) { - bt_allow_pairing = false; - bt_state = BT_STATE_OFF; + uint8_t eeprom_read(uint32_t mapped_addr); + + void bt_stop() { + // Serial.println("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_flush() { if (bt_state == BT_STATE_CONNECTED) { SerialBT.flushTXD(); } } -void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) { + void bt_disable_pairing() { + // Serial.println("BT Disable pairing"); + bt_allow_pairing = false; + pairing_pin = 0; + bt_ssp_pin = 0; + bt_state = BT_STATE_ON; + } + + void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) { + // Serial.println("BT pairing complete"); + BLEConnection* connection = Bluefruit.Connection(conn_handle); if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { - bt_disable_pairing(); - } else { - bt_ssp_pin = 0; - } -} + ble_gap_conn_sec_mode_t security = connection->getSecureMode(); + // Serial.println("Bonding success"); -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); + // On the NRF52 it is not possible with the Arduino library to reject + // requests from devices with no IO capabilities, which would allow + // bypassing pin entry through pairing using the "just works" mode. + // Therefore, we must check the security level of the connection after + // pairing to ensure "just works" has not been used. If it has, we need + // to disconnect, unpair and delete any bonding information immediately. + // Settings on the SerialBT service should prevent unauthorised access to + // the serial port anyway, but this is still wise to do regardless. + // + // Note: It may be nice to have this done in the BLESecurity class in the + // future, but as it stands right now I'd have to fork the BSP to do + // that, which I don't fancy doing. Impact on security is likely minimal. + // Requires investigation. + + if (security.sm == 1 && security.lv >= 3) { + // Serial.println("Auth level success"); + bt_state = BT_STATE_CONNECTED; + cable_state = CABLE_STATE_DISCONNECTED; + connection->disconnect(); + bt_disable_pairing(); + } else { + // Serial.println("Auth level failure, debonding"); + if (connection->bonded()) { connection->removeBondKey(); } + connection->disconnect(); + bt_disable_pairing(); + } + } else { + // Serial.println("Bonding failure"); + connection->disconnect(); + bt_disable_pairing(); } - kiss_indicate_btpin(); - if (match_request) { - if (bt_allow_pairing) { - return true; - } + } + + bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) { + // Serial.println("Passkey callback"); + if (bt_allow_pairing) { + return true; } return false; -} + } -void bt_connect_callback(uint16_t conn_handle) { + void bt_connect_callback(uint16_t conn_handle) { + // Serial.println("Connect callback"); bt_state = BT_STATE_CONNECTED; cable_state = CABLE_STATE_DISCONNECTED; -} -void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) { - bt_state = BT_STATE_ON; -} + BLEConnection* conn = Bluefruit.Connection(conn_handle); + conn->requestPHY(BLE_GAP_PHY_2MBPS); + conn->requestMtuExchange(512+3); + conn->requestDataLengthUpdate(); + } -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; + void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) { + // Serial.println("Disconnect callback"); + if (reason != BLE_GAP_SEC_STATUS_SUCCESS) { + bt_state = BT_STATE_ON; } - Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); - Bluefruit.autoConnLed(false); - if (Bluefruit.begin()) { - Bluefruit.setTxPower(4); // Check bluefruit.h for supported values - Bluefruit.Security.setIOCaps(true, true, false); - Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback); - Bluefruit.Periph.setConnectCallback(bt_connect_callback); - Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback); - Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete); - 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]; - } + } + + void bt_update_passkey() { + // Serial.println("Update passkey"); + pairing_pin = random(899999)+100000; + bt_ssp_pin = pairing_pin; + } + + uint32_t bt_get_passkey() { + // Serial.println("API passkey request"); + if (pairing_pin == 0) { bt_update_passkey(); } + return pairing_pin; + } + + bool bt_setup_hw() { + // Serial.println("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_enabled = true; + } else { + bt_enabled = false; + } + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.autoConnLed(false); + if (Bluefruit.begin()) { + uint32_t pin = bt_get_passkey(); + char pin_char[6]; + sprintf(pin_char,"%lu", pin); - bt_ready = true; - return true; + 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.Security.setPIN(pin_char); + 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() { + // Serial.println("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(); + blebas.begin(); - SerialBT.begin(); + // Guard to ensure SerialBT service is not duplicated through BT being power cycled + if (!SerialBT_init) { + SerialBT.bufferTXD(true); // enable buffering - blebas.begin(); + SerialBT.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // enable encryption for BLE serial + SerialBT.begin(); + SerialBT_init = true; + } - // non-connectable advertising - 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() { + // Serial.println("BT init"); bt_state = BT_STATE_OFF; if (bt_setup_hw()) { if (bt_enabled && !console_active) bt_start(); @@ -373,18 +588,28 @@ 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 update_bt() { - if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) { - bt_disable_pairing(); } -} + + void bt_enable_pairing() { + // Serial.println("BT enable pairing"); + if (bt_state == BT_STATE_OFF) bt_start(); + + uint32_t pin = bt_get_passkey(); + char pin_char[6]; + sprintf(pin_char,"%lu", pin); + Bluefruit.Security.setPIN(pin_char); + + bt_allow_pairing = true; + bt_pairing_started = millis(); + bt_state = BT_STATE_PAIRING; + kiss_indicate_btpin(); + } + + void bt_debond_all() { } + + void update_bt() { + if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) { + bt_disable_pairing(); + } + } #endif diff --git a/Boards.h b/Boards.h index 0eb5c2b..74b96d7 100644 --- a/Boards.h +++ b/Boards.h @@ -27,21 +27,96 @@ #define MCU_ESP32 0x81 #define MCU_NRF52 0x71 - #define BOARD_RNODE 0x31 - #define BOARD_HMBRW 0x32 + // Products, boards and models //// + #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_T3S3 0x42 // T3S3 devices + #define MODEL_A1 0xA1 // T3S3, 433 MHz with SX1268 + #define MODEL_A5 0xA5 // T3S3, 433 MHz with SX1278 + #define MODEL_A6 0xA6 // T3S3, 868 MHz with SX1262 + #define MODEL_AA 0xAA // T3S3, 868 MHz with SX1276 + #define MODEL_AC 0xAC // T3S3, 2.4 GHz with SX1280 and PA + + #define PRODUCT_TBEAM 0xE0 // T-Beam devices #define BOARD_TBEAM 0x33 + #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 + #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 + #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_XIAO_S3 0xEB + #define BOARD_XIAO_S3 0x3E + #define MODEL_DE 0xDE // Xiao ESP32S3 with Wio-SX1262 module, 433 MHz + #define MODEL_DD 0xDD // Xiao ESP32S3 with Wio-SX1262 module, 868 MHz + + #define PRODUCT_T32_10 0xB2 + #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 + #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 + #define BOARD_LORA32_V2_1 0x37 + #define MODEL_B4 0xB4 // LilyGO T3 v2.1, 433 MHz + #define MODEL_B9 0xB9 // LilyGO T3 v2.1, 868 MHz + + #define PRODUCT_H32_V2 0xC0 // Board code 0x38 + #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 + #define BOARD_HELTEC32_V3 0x3A + #define MODEL_C5 0xC5 // Heltec Lora32 v3, 433 MHz + #define MODEL_CA 0xCA // Heltec Lora32 v3, 868 MHz + + #define PRODUCT_HELTEC_T114 0xC2 // Heltec Mesh Node T114 + #define BOARD_HELTEC_T114 0x3C + #define MODEL_C6 0xC6 // Heltec Mesh Node T114, 470-510 MHz + #define MODEL_C7 0xC7 // Heltec Mesh Node T114, 863-928 MHz + + #define PRODUCT_TECHO 0x15 // LilyGO T-Echo devices + #define BOARD_TECHO 0x44 + #define MODEL_16 0x16 // T-Echo 433 MHz + #define MODEL_17 0x17 // T-Echo 868/915 MHz + + #define PRODUCT_RAK4631 0x10 + #define BOARD_RAK4631 0x51 + #define MODEL_11 0x11 // RAK4631, 433 Mhz + #define MODEL_12 0x12 // RAK4631, 868 Mhz + + #define PRODUCT_HMBRW 0xF0 + #define BOARD_HMBRW 0x32 #define BOARD_HUZZAH32 0x34 #define BOARD_GENERIC_ESP32 0x35 - #define BOARD_LORA32_V2_0 0x36 - #define BOARD_LORA32_V2_1 0x37 - #define BOARD_LORA32_V1_0 0x39 - #define BOARD_HELTEC32_V2 0x38 - #define BOARD_HELTEC32_V3 0x3A - #define BOARD_RNODE_NG_20 0x40 - #define BOARD_RNODE_NG_21 0x41 - #define BOARD_RNODE_NG_22 0x42 #define BOARD_GENERIC_NRF52 0x50 - #define BOARD_RAK4631 0x51 + #define MODEL_FE 0xFE // Homebrew board, max 17dBm output power + #define MODEL_FF 0xFF // Homebrew board, max 14dBm output power #if defined(__AVR_ATmega1284P__) #define PLATFORM PLATFORM_AVR @@ -149,13 +224,16 @@ #define HAS_DISPLAY true #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_SDA 21 #define I2C_SCL 22 #define PMU_IRQ 35 + + #define HAS_INPUT true + const int pin_btn_usr1 = 38; + const int pin_cs = 18; const int pin_reset = 23; const int pin_led_rx = 2; @@ -165,6 +243,7 @@ #define HAS_TCXO true #define HAS_BUSY true #define DIO2_AS_RF_SWITCH true + #define OCP_TUNED 0x18 const int pin_busy = 32; const int pin_dio = 33; const int pin_tcxo_enable = -1; @@ -185,7 +264,6 @@ #elif BOARD_MODEL == BOARD_LORA32_V1_0 #define HAS_DISPLAY true #define HAS_BLUETOOTH true - #define HAS_BLE true #define HAS_CONSOLE true #define HAS_EEPROM true const int pin_cs = 18; @@ -202,7 +280,6 @@ #elif BOARD_MODEL == BOARD_LORA32_V2_0 #define HAS_DISPLAY true #define HAS_BLUETOOTH true - #define HAS_BLE true #define HAS_CONSOLE true #define HAS_EEPROM true const int pin_cs = 18; @@ -219,7 +296,6 @@ #elif BOARD_MODEL == BOARD_LORA32_V2_1 #define HAS_DISPLAY true #define HAS_BLUETOOTH true - #define HAS_BLE true #define HAS_PMU true #define HAS_CONSOLE true #define HAS_EEPROM true @@ -242,6 +318,13 @@ #define HAS_BLUETOOTH true #define HAS_CONSOLE true #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_btn_usr1 = 0; + const int pin_cs = 18; const int pin_reset = 14; const int pin_dio = 26; @@ -258,12 +341,14 @@ #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 #define PIN_WAKEUP GPIO_NUM_0 #define WAKEUP_LEVEL 0 + #define OCP_TUNED 0x18 const int pin_btn_usr1 = 0; @@ -338,14 +423,82 @@ #endif #endif - #elif BOARD_MODEL == BOARD_RNODE_NG_22 + #elif BOARD_MODEL == BOARD_T3S3 + #define IS_ESP32S3 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 true + #define PIN_WAKEUP GPIO_NUM_0 + #define WAKEUP_LEVEL 0 + const int pin_btn_usr1 = 0; + + const int pin_cs = 7; + const int pin_reset = 8; + const int pin_sclk = 5; + const int pin_mosi = 6; + const int pin_miso = 3; + + #if MODEM == SX1262 + #define DIO2_AS_RF_SWITCH true + #define HAS_BUSY true + #define HAS_TCXO true + const int pin_busy = 34; + const int pin_dio = 33; + const int pin_tcxo_enable = -1; + #elif MODEM == SX1280 + #define CONFIG_QUEUE_SIZE 6144 + #define DIO2_AS_RF_SWITCH false + #define HAS_BUSY true + #define HAS_TCXO true + #define HAS_PA true + const int pa_max_input = 3; + + #define HAS_RF_SWITCH_RX_TX true + const int pin_rxen = 21; + const int pin_txen = 10; + + const int pin_busy = 36; + const int pin_dio = 9; + const int pin_tcxo_enable = -1; + #else + const int pin_dio = 9; + #endif + + const int pin_np = 38; + const int pin_dac = 25; + const int pin_adc = 1; + + const int SD_MISO = 2; + 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; + const int pin_led_tx = 37; + #else + const int pin_led_rx = 37; + const int pin_led_tx = 37; + #endif + #endif + + #elif BOARD_MODEL == BOARD_TDECK #define IS_ESP32S3 true #define MODEM SX1262 #define DIO2_AS_RF_SWITCH true #define HAS_BUSY true #define HAS_TCXO true - #define HAS_DISPLAY true + #define HAS_DISPLAY false #define HAS_CONSOLE false #define HAS_BLUETOOTH false #define HAS_BLE true @@ -358,35 +511,130 @@ #define HAS_SLEEP true #define PIN_WAKEUP GPIO_NUM_0 #define WAKEUP_LEVEL 0 - // #define PIN_DISP_SLEEP 21 - // #define DISP_SLEEP_LEVEL HIGH + + const int pin_poweron = 10; const int pin_btn_usr1 = 0; - const int pin_cs = 7; - const int pin_reset = 8; - const int pin_sclk = 5; - const int pin_mosi = 6; - const int pin_miso = 3; + 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 = 33; - const int pin_busy = 34; + const int pin_dio = 45; + const int pin_busy = 13; - const int pin_np = 38; - const int pin_dac = 25; - const int pin_adc = 1; + 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; - const int SD_MISO = 2; - 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; - const int pin_led_tx = 37; + const int pin_led_rx = 43; + const int pin_led_tx = 43; #else - const int pin_led_rx = 37; - const int pin_led_tx = 37; + const int pin_led_rx = 43; + const int pin_led_tx = 43; + #endif + #endif + + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + #define IS_ESP32S3 true + #define MODEM SX1262 + #define DIO2_AS_RF_SWITCH true + #define HAS_BUSY true + #define HAS_TCXO true + #define OCP_TUNED 0x18 + + #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 + + #elif BOARD_MODEL == BOARD_XIAO_S3 + #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 true + #define HAS_BLUETOOTH false + #define HAS_BLE 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_21 + #define WAKEUP_LEVEL 0 + + const int pin_btn_usr1 = 21; + const int pin_cs = 41; + const int pin_reset = 42; + const int pin_sclk = 7; + const int pin_mosi = 9; + const int pin_miso = 8; + const int pin_tcxo_enable = -1; + const int pin_dio = 39; + const int pin_busy = 40; + + #if HAS_NP == false + #if defined(EXTERNAL_LEDS) + const int pin_led_rx = 48; + const int pin_led_tx = 48; + #else + const int pin_led_rx = 48; + const int pin_led_tx = 48; #endif #endif @@ -397,7 +645,7 @@ #elif MCU_VARIANT == MCU_NRF52 #if BOARD_MODEL == BOARD_RAK4631 #define HAS_EEPROM false - #define HAS_DISPLAY false + #define HAS_DISPLAY true #define HAS_BLUETOOTH false #define HAS_BLE true #define HAS_CONSOLE false @@ -407,6 +655,7 @@ #define HAS_TCXO true #define HAS_RF_SWITCH_RX_TX true #define HAS_BUSY true + #define HAS_INPUT true #define DIO2_AS_RF_SWITCH true #define CONFIG_UART_BUFFER_SIZE 6144 #define CONFIG_QUEUE_SIZE 6144 @@ -416,8 +665,11 @@ #define BLE_MANUFACTURER "RAK Wireless" #define BLE_MODEL "RAK4640" + const int pin_btn_usr1 = 9; + // Following pins are for the sx1262 const int pin_rxen = 37; + const int pin_txen = -1; const int pin_reset = 38; const int pin_cs = 42; const int pin_sclk = 43; @@ -429,12 +681,154 @@ const int pin_led_tx = LED_GREEN; const int pin_tcxo_enable = -1; + #elif BOARD_MODEL == BOARD_TECHO + #define _PINNUM(port, pin) ((port) * 32 + (pin)) + #define MODEM SX1262 + #define HAS_EEPROM false + #define HAS_BLUETOOTH false + #define HAS_BLE true + #define HAS_CONSOLE false + #define HAS_PMU true + #define HAS_NP false + #define HAS_SD false + #define HAS_TCXO true + #define HAS_BUSY true + #define HAS_INPUT true + #define HAS_SLEEP true + #define BLE_MANUFACTURER "LilyGO" + #define BLE_MODEL "T-Echo" + + #define HAS_INPUT true + #define EEPROM_SIZE 296 + #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED + + #define CONFIG_UART_BUFFER_SIZE 32768 + #define CONFIG_QUEUE_SIZE 6144 + #define CONFIG_QUEUE_MAX_LENGTH 200 + + #define HAS_DISPLAY true + #define HAS_BACKLIGHT true + #define DISPLAY_SCALE 1 + + #define LED_ON LOW + #define LED_OFF HIGH + #define PIN_LED_GREEN _PINNUM(1, 1) + #define PIN_LED_RED _PINNUM(1, 3) + #define PIN_LED_BLUE _PINNUM(0, 14) + #define PIN_VEXT_EN _PINNUM(0, 12) + + const int pin_disp_cs = 30; + const int pin_disp_dc = 28; + const int pin_disp_reset = 2; + const int pin_disp_busy = 3; + const int pin_disp_en = -1; + const int pin_disp_sck = 31; + const int pin_disp_mosi = 29; + const int pin_disp_miso = -1; + const int pin_backlight = 43; + + const int pin_btn_usr1 = _PINNUM(1, 10); + const int pin_btn_touch = _PINNUM(0, 11); + + const int pin_reset = 25; + const int pin_cs = 24; + const int pin_sclk = 19; + const int pin_mosi = 22; + const int pin_miso = 23; + const int pin_busy = 17; + const int pin_dio = 20; + const int pin_tcxo_enable = 21; + const int pin_led_rx = PIN_LED_BLUE; + const int pin_led_tx = PIN_LED_RED; + + #elif BOARD_MODEL == BOARD_HELTEC_T114 + #define MODEM SX1262 + #define HAS_EEPROM false + #define HAS_DISPLAY true + #define HAS_BLUETOOTH false + #define HAS_BLE true + #define HAS_CONSOLE false + #define HAS_PMU true + #define HAS_NP true + #define HAS_SD false + #define HAS_TCXO true + #define HAS_BUSY true + #define HAS_INPUT true + #define HAS_SLEEP true + #define DIO2_AS_RF_SWITCH true + #define CONFIG_UART_BUFFER_SIZE 6144 + #define CONFIG_QUEUE_SIZE 6144 + #define CONFIG_QUEUE_MAX_LENGTH 200 + #define EEPROM_SIZE 296 + #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED + #define BLE_MANUFACTURER "Heltec" + #define BLE_MODEL "T114" + + #define PIN_T114_ADC_EN 6 + #define PIN_VEXT_EN 21 + + // LED + #define LED_T114_GREEN 3 + #define PIN_T114_LED 14 + #define NP_M 1 + const int pin_np = PIN_T114_LED; + + // SPI + #define PIN_T114_MOSI 22 + #define PIN_T114_MISO 23 + #define PIN_T114_SCK 19 + #define PIN_T114_SS 24 + + // SX1262 + #define PIN_T114_RST 25 + #define PIN_T114_DIO1 20 + #define PIN_T114_BUSY 17 + + // TFT + #define DISPLAY_SCALE 2 + #define PIN_T114_TFT_MOSI 9 + #define PIN_T114_TFT_MISO 11 // not connected + #define PIN_T114_TFT_SCK 8 + #define PIN_T114_TFT_SS 11 + #define PIN_T114_TFT_DC 12 + #define PIN_T114_TFT_RST 2 + #define PIN_T114_TFT_EN 3 + #define PIN_T114_TFT_BLGT 15 + + // pins for buttons on Heltec T114 + const int pin_btn_usr1 = 42; + + // pins for sx1262 on Heltec T114 + const int pin_reset = PIN_T114_RST; + const int pin_cs = PIN_T114_SS; + const int pin_sclk = PIN_T114_SCK; + const int pin_mosi = PIN_T114_MOSI; + const int pin_miso = PIN_T114_MISO; + const int pin_busy = PIN_T114_BUSY; + const int pin_dio = PIN_T114_DIO1; + const int pin_led_rx = 35; + const int pin_led_tx = 35; + const int pin_tcxo_enable = -1; + + // pins for ST7789 display on Heltec T114 + const int DISPLAY_DC = PIN_T114_TFT_DC; + const int DISPLAY_CS = PIN_T114_TFT_SS; + const int DISPLAY_MISO = PIN_T114_TFT_MISO; + const int DISPLAY_MOSI = PIN_T114_TFT_MOSI; + const int DISPLAY_CLK = PIN_T114_TFT_SCK; + const int DISPLAY_BL_PIN = PIN_T114_TFT_BLGT; + const int DISPLAY_RST = PIN_T114_TFT_RST; + #else #error An unsupported nRF board was selected. Cannot compile RNode firmware. #endif #endif + #ifndef DISPLAY_SCALE + #define DISPLAY_SCALE 1 + #endif + #ifndef HAS_RF_SWITCH_RX_TX const int pin_rxen = -1; const int pin_txen = -1; @@ -444,8 +838,26 @@ const int pin_busy = -1; #endif + #ifndef LED_ON + #define LED_ON HIGH + #endif + + #ifndef LED_OFF + #define LED_OFF LOW + #endif + #ifndef DIO2_AS_RF_SWITCH #define DIO2_AS_RF_SWITCH false #endif + // Default OCP value if not specified + // in board configuration + #ifndef OCP_TUNED + #define OCP_TUNED 0x18 + #endif + + #ifndef NP_M + #define NP_M 0.15 + #endif + #endif diff --git a/Config.h b/Config.h index 047a3fb..ee2dee2 100644 --- 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 0x48 + #define MIN_VERS 0x53 #define MODE_HOST 0x11 #define MODE_TNC 0x12 @@ -67,28 +67,55 @@ const int rssi_offset = 157; // Default LoRa settings - const int lora_rx_turnaround_ms = 66; - const int lora_post_tx_yield_slots = 6; - uint32_t post_tx_yield_timeout = 0; - #define LORA_PREAMBLE_SYMBOLS_HW 4 - #define LORA_PREAMBLE_SYMBOLS_MIN 18 - #define LORA_PREAMBLE_TARGET_MS 15 - #define LORA_CAD_SYMBOLS 3 - int csma_slot_ms = 50; - float csma_p_min = 0.1; - float csma_p_max = 0.8; - uint8_t csma_p = 0; + #define PHY_HEADER_LORA_SYMBOLS 20 + #define PHY_CRC_LORA_BITS 16 + #define LORA_PREAMBLE_SYMBOLS_MIN 18 + #define LORA_PREAMBLE_TARGET_MS 24 + #define LORA_PREAMBLE_FAST_DELTA 18 + #define LORA_FAST_THRESHOLD_BPS 30E3 + #define LORA_LIMIT_THRESHOLD_BPS 60E3 + long lora_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN; + long lora_preamble_time_ms = 0; + long lora_header_time_ms = 0; + float lora_symbol_time_ms = 0.0; + float lora_symbol_rate = 0.0; + float lora_us_per_byte = 0.0; + bool lora_low_datarate = false; + bool lora_limit_rate = false; - int lora_sf = 0; - int lora_cr = 5; - int lora_txp = 0xFF; - uint32_t lora_bw = 0; - uint32_t lora_freq = 0; - uint32_t lora_bitrate = 0; - long lora_preamble_symbols = 6; - float lora_symbol_time_ms = 0.0; - float lora_symbol_rate = 0.0; - float lora_us_per_byte = 0.0; + // CSMA Parameters + #define CSMA_SIFS_MS 0 + #define CSMA_POST_TX_YIELD_SLOTS 3 + #define CSMA_SLOT_MAX_MS 100 + #define CSMA_SLOT_MIN_MS 24 + #define CSMA_SLOT_MIN_FAST_DELTA 18 + #define CSMA_SLOT_SYMBOLS 12 + #define CSMA_CW_BANDS 4 + #define CSMA_CW_MIN 0 + #define CSMA_CW_PER_BAND_WINDOWS 15 + #define CSMA_BAND_1_MAX_AIRTIME 7 + #define CSMA_BAND_N_MIN_AIRTIME 85 + #define CSMA_INFR_THRESHOLD_DB 12 + bool interference_detected = false; + bool avoid_interference = true; + int csma_slot_ms = CSMA_SLOT_MIN_MS; + unsigned long difs_ms = CSMA_SIFS_MS + 2*csma_slot_ms; + unsigned long difs_wait_start = -1; + unsigned long cw_wait_start = -1; + unsigned long cw_wait_target = -1; + unsigned long cw_wait_passed = 0; + int csma_cw = -1; + uint8_t cw_band = 1; + uint8_t cw_min = 0; + uint8_t cw_max = CSMA_CW_PER_BAND_WINDOWS; + + // LoRa settings + int lora_sf = 0; + int lora_cr = 5; + int lora_txp = 0xFF; + uint32_t lora_bw = 0; + uint32_t lora_freq = 0; + uint32_t lora_bitrate = 0; // Operational variables bool radio_locked = true; @@ -100,12 +127,15 @@ bool pmu_ready = false; bool promisc = false; bool implicit = false; + bool memory_low = false; uint8_t implicit_l = 0; uint8_t op_mode = MODE_HOST; uint8_t model = 0x00; uint8_t hwrev = 0x00; + #define NOISE_FLOOR_SAMPLES 64 + int noise_floor = -292; int current_rssi = -292; int last_rssi = -292; uint8_t last_rssi_raw = 0x00; @@ -163,15 +193,11 @@ uint32_t last_status_update = 0; uint32_t last_dcd = 0; - // Status flags - const uint8_t SIG_DETECT = 0x01; - const uint8_t SIG_SYNCED = 0x02; - const uint8_t RX_ONGOING = 0x04; - // 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; @@ -181,15 +207,18 @@ uint8_t battery_state = 0x00; uint8_t display_intensity = 0xFF; uint8_t display_addr = 0xFF; + volatile bool display_updating = false; + bool display_blanking_enabled = false; bool display_diagnostics = true; bool device_init_done = false; bool eeprom_ok = false; bool firmware_update_mode = false; + bool serial_in_frame = 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 #endif diff --git a/Console.h b/Console.h index 4321d9f..c59d348 100644 --- 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 diff --git a/Console/Makefile b/Console/Makefile index 34f53d9..f5a674a 100644 --- a/Console/Makefile +++ b/Console/Makefile @@ -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 ../Config.h ../Console.h ../Device.h ../Display.h ../Framing.h ../Graphics.h ../LICENSE ../LoRa.cpp ../LoRa.h ../Makefile ../MD5.cpp ../MD5.h ../partition_hashes ../Power.h ../README.md ../release_hashes.py ../RNode_Firmware.ino ../ROM.h ../Utilities.h + zip --junk-paths -r build/pkg/rnode_firmware.zip ../arduino-cli.yaml ../BLESerial.cpp ../BLESerial.h ../Bluetooth.h ../Boards.h ../Config.h ../Console.h ../Device.h ../Display.h ../Framing.h ../Graphics.h ../LICENSE ../Makefile ../MD5.cpp ../MD5.h ../partition_hashes ../Power.h ../README.md ../release_hashes.py ../RNode_Firmware.ino ../ROM.h ../sx126x.cpp ../sx126x.h ../sx127x.cpp ../sx127x.h ../sx128x.cpp ../sx128x.h ../Utilities.h ../esp32_btbufs.py data: @echo Including assets... diff --git a/Console/assets/images/bg_h_1.webp b/Console/assets/images/bg_h_1.webp index e650d09..9fa57e3 100644 Binary files a/Console/assets/images/bg_h_1.webp and b/Console/assets/images/bg_h_1.webp differ diff --git a/Console/assets/stl/Handheld_RNode_Parts.7z b/Console/assets/stl/Handheld_RNode_Parts.7z index 52288ce..df861b6 100644 Binary files a/Console/assets/stl/Handheld_RNode_Parts.7z and b/Console/assets/stl/Handheld_RNode_Parts.7z differ diff --git a/Console/build.py b/Console/build.py index 5d8a119..3200d63 100644 --- a/Console/build.py +++ b/Console/build.py @@ -4,10 +4,10 @@ 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", - "rnsh": "rnsh-0.1.4-py3-none-any.whl", + "rns": "rns-1.0.0-py3-none-any.whl", + "nomadnet": "nomadnet-0.8.0-py3-none-any.whl", + "lxmf": "lxmf-0.8.0-py3-none-any.whl", + "rnsh": "rnsh-0.1.5-py3-none-any.whl", } DEFAULT_TITLE = "RNode Bootstrap Console" @@ -174,28 +174,39 @@ mf.write(help_redirect) mf.close() def optimise_manual(path): - pm = 110 + pm = 200 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 + input_file = fp + output_file = input_file + resize = "convert "+input_file+" -quality 25 -resize "+str(s)+" "+output_file print(resize) subprocess.call(shlex.split(resize), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # if output_file != input_file and os.path.isfile(input_file): os.unlink(input_file) remove_files = [ "objects.inv", @@ -205,9 +216,24 @@ 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", + "_images/board_rak4631.png", + "_images/board_rnodev2.png", + "_images/board_t114.png", + "_images/board_t3s3.png", + "_images/board_t3v10.png", + "_images/board_t3v20.png", + "_images/board_t3v21.png", + "_images/board_tbeam.png", + "_images/board_tdeck.png", + "_images/board_techo.png", + "_images/board_tbeam_supreme.png", + "_images/board_opencomxl.png", + "_images/board_heltec32v20.png", + "_images/board_heltec32v30.png", # "_static/pygments.css", # "_static/language_data.js", # "_static/searchtools.js", diff --git a/Console/source/guides/make_rnodes.md b/Console/source/guides/make_rnodes.md index 46a63f6..6190b3f 100644 --- a/Console/source/guides/make_rnodes.md +++ b/Console/source/guides/make_rnodes.md @@ -2,7 +2,6 @@ [title]: <> (How To Make Your Own RNodes) [image]: <> (images/g3p.webp) [excerpt]: <> (This article will outline the general process, and provide the information you need, for building your own RNode from a few basic modules. The RNode will be functionally identical to a commercially purchased board.) - # How To Make Your Own RNodes This article will outline the general process, and provide the information you need, for building your own RNode from a few basic modules. The RNode will be functionally identical to a purchased device. @@ -31,10 +30,9 @@ Currently, the RNode firmware supports a variety of different microcontrollers, Regarding the LoRa transceiver module, there is going to be an almost overwhelming amount of options to choose from. To narrow it down, here are the essential characteristics to look for: -- The RNode firmware needs a module based on the **Semtech SX1276** or **Semtech SX1278** LoRa transceiver IC. These come in several different variants, for all frequency bands from about 150 MHz to about 1100 MHz. -- Support for **SX1262**, **SX1268** and **SX1280**-based modules is coming soon, but until that is released, only **SX1276** and **SX1278** modules will work. +- The RNode firmware needs a module based on the **Semtech SX1276**, **Semtech SX1278**, **SX1262**, **SX1268** and **SX1280** LoRa transceiver ICs. These come in several different variants, for all frequency bands from about 150 MHz to 2500 MHz. - The module *must* expose the direct SPI bus to the transceiver chip. UART based modules that add their own communications layer will not work. -- The module must also expose the *reset* line of the chip, and provide the **DIO0** interrupt signal *from* the chip. +- The module must also expose the *reset* line of the chip, and provide the **DIO0** (or other relevant) interrupt signal *from* the chip. - As mentioned above, the module must be logic-level compatible with the microcontroller you are using, unless you want to add a level-shifter. Resistor divider arrays will most likely not work here, due to the bus speeds required. Keeping those things in mind, you should be able to select a suitable combination of microcontroller board and transceiver module. @@ -56,12 +54,17 @@ In the photo above I used an Adafruit Feather ESP32 board and a ModTronix inAir4 9. Connect the *DIO0* pin of the transceiver module to the *DIO0 interrupt pin* of the microcontroller board. 10. You can optionally connect transmit and receiver LEDs to the corresponding pins of the microcontroller board. -The pin layouts of your transceiver module and microcontroller board will vary, but you can look up the correct pin assignments for your processor type and board layout in the `Config.h` file of the [RNode Firmware]({ASSET_PATH}pkg/rnode_firmware.zip). +The pin layouts of your transceiver module and microcontroller board will vary, but you can look up the correct pin assignments for your processor type and board layout in the [Config.h](https://github.com/markqvist/RNode_Firmware/blob/master/Config.h) file of the [RNode Firmware](https://unsigned.io/rnode_firmware). -## Loading the Firmware -Once the hardware is assembled, you are ready to load the firmware onto the board and configure the configuration parameters in the boards EEPROM. Luckily, this process is completely automated by the [RNode Configuration Utility]({ASSET_PATH}m/using.html#the-rnodeconf-utility). +### Loading the Firmware +Once the hardware is assembled, you are ready to load the firmware onto the board and configure the configuration parameters in the boards EEPROM. Luckily, this process is completely automated by the [RNode Configuration Utility](https://markqvist.github.io/Reticulum/manual/using.html#the-rnodeconf-utility). To prepare for loading the firmware, make sure that `python` and `pip` is installed on your system, then install the `rns` package (which includes the `rnodeconf` program) by issuing the command: -The `rnodeconf` program is included in the `rns` package. Please read [these instructions]({ASSET_PATH}s_rns.html) for more information on how to install it from this repository, or from the Internet. If installation goes well, you can now move on to the next step. + +```txt +pip install rns +``` + +If installation goes well, you can now move on to the next step. > *Take Care*: A LoRa transceiver module **must** be connected to the board for the firmware to start and accept commands. If the firmware does not verify that the correct transceiver is available on the SPI bus, execution is stopped, and the board will not accept commands. If you find the board unresponsive after installing the firmware, or EEPROM configuration fails, double-check your transceiver module wiring! @@ -73,24 +76,6 @@ rnodeconf --autoinstall The installer will now ask you to insert the device you want to set up, scan for connected serial ports, and ask you a number of questions regarding the device. When it has the information it needs, it will install the correct firmware and configure the necessary parameters in the device EEPROM for it to function properly. -> **Please Note!** If you are connected to the Internet while installing, the autoinstaller will automatically download any needed firmware files to a local cache before installing. - -> If you do not have an active Internet connection while installing, you can extract and use the firmware from this device instead. This will **only** work if you are building the same type of RNode as the device you are extracting from, as the firmware has to match the targeted board and hardware configuration. - -If you need to extract the firmware from an existing RNode, run the following command: - -``` -rnodeconf --extract -``` - -If `rnodeconf` finds a working RNode, it will extract and save the firmware from the device for later use. You can then run the auto-installer with the `--use-extracted` option to use the locally extracted file: - -``` -rnodeconf --autoinstall --use-extracted -``` - -This also works for updating the firmware on existing RNodes, so you can extract a newer firmware from one RNode, and deploy it onto other RNodes using the same method. Just use the `--update` option instead of `--autoinstall`. - If the install goes well, you will be greated with a success message telling you that your device is now ready. To confirm everything is OK, you can query the device info with: ```txt diff --git a/Device.h b/Device.h index 944479d..e2a1d06 100644 --- 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 @@ -36,6 +36,8 @@ #define APPLICATION_START 0x26000 #define USER_DATA_START 0xED000 + +#define IMG_SIZE_START 0xFF008 #endif #endif @@ -141,6 +143,13 @@ void device_save_firmware_hash() { } #if MCU_VARIANT == MCU_NRF52 +uint32_t retrieve_application_size() { + uint8_t bytes[4]; + memcpy(bytes, (const void*)IMG_SIZE_START, 4); + uint32_t fw_len = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; + return fw_len; +} + void calculate_region_hash(unsigned long long start, unsigned long long end, uint8_t* return_hash) { // this function calculates the hash digest of a region of memory, // currently it is only designed to work for the application region @@ -152,16 +161,12 @@ void calculate_region_hash(unsigned long long start, unsigned long long end, uin hash.begin(CRYS_HASH_SHA256_mode); - bool finish = false; uint8_t size; - bool application = true; - int end_count = 0; - unsigned long length = 0; - while (start < end - 1 ) { + while (start < end ) { const void* src = (const void*)start; if (start + CHUNK_SIZE >= end) { - size = (end - 1) - start; + size = end - start; } else { size = CHUNK_SIZE; @@ -169,74 +174,9 @@ void calculate_region_hash(unsigned long long start, unsigned long long end, uin memcpy(chunk, src, CHUNK_SIZE); - // check if we've reached the end of the program - // if we're checking the application region - if (application) { - for (int i = 0; i < CHUNK_SIZE; i++) { - if (chunk[i] == 0xFF) { - bool matched = true; - end_count = 1; - // check if rest of chunk is FFs as well, only if FF is not - // at the end of chunk - if (i < CHUNK_SIZE - 1) { - for (int x = 0; x < CHUNK_SIZE - i; x++) { - if (chunk[i+x] != 0xFF) { - matched = false; - break; - } - end_count++; - } - } - - if (matched) { - while (end_count < END_SECTION_SIZE) { - // check if bytes in next chunk up to total - // required are also FFs - for (int x = 1; x <= ceil(END_SECTION_SIZE / CHUNK_SIZE); x++) { - const void* src_next = (const void*)start + CHUNK_SIZE*x; - if ((END_SECTION_SIZE - end_count) > CHUNK_SIZE) { - size = CHUNK_SIZE; - } else { - size = END_SECTION_SIZE - end_count; - } - memcpy(chunk_next, src_next, size); - for (int y = 0; y < size; y++) { - if (chunk_next[y] != 0xFF) { - matched = false; - break; - } - end_count++; - } - - if (!matched) { - break; - } - } - if (!matched) { - break; - } - } - - if (matched) { - finish = true; - size = i; - break; - } - } - } - } - } - - if (finish) { - hash.update(chunk, size); - length += size; - break; - } else { - hash.update(chunk, size); - } + hash.update(chunk, size); start += CHUNK_SIZE; - length += CHUNK_SIZE; } hash.end(return_hash); } @@ -257,7 +197,7 @@ void device_validate_partitions() { esp_partition_get_sha256(esp_ota_get_running_partition(), dev_firmware_hash); #elif MCU_VARIANT == MCU_NRF52 // todo, add bootloader, partition table, or softdevice? - calculate_region_hash(APPLICATION_START, USER_DATA_START, dev_firmware_hash); + calculate_region_hash(APPLICATION_START, APPLICATION_START+retrieve_application_size(), dev_firmware_hash); #endif #if VALIDATE_FIRMWARE for (uint8_t i = 0; i < DEV_HASH_LEN; i++) { diff --git a/Display.h b/Display.h index dd24475..c42283c 100644 --- 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 @@ -14,12 +14,35 @@ // along with this program. If not, see . #include "Graphics.h" -#include #include -#include + +#if BOARD_MODEL != BOARD_TECHO + #if BOARD_MODEL == BOARD_TDECK + #include + #elif BOARD_MODEL == BOARD_HELTEC_T114 + #include "ST7789.h" + #define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + #include + #else + #include + #include + #endif + +#else + void (*display_callback)(); + void display_add_callback(void (*callback)()) { display_callback = callback; } + void busyCallback(const void* p) { display_callback(); } + #define SSD1306_BLACK GxEPD_BLACK + #define SSD1306_WHITE GxEPD_WHITE + #include + #include +#endif + #include "Fonts/Org_01.h" #define DISP_W 128 #define DISP_H 64 + #if BOARD_MODEL == BOARD_RNODE_NG_20 || BOARD_MODEL == BOARD_LORA32_V2_0 #define DISP_RST -1 #define DISP_ADDR 0x3C @@ -37,14 +60,37 @@ #define DISP_ADDR 0x3C #define SCL_OLED 18 #define SDA_OLED 17 +#elif BOARD_MODEL == BOARD_RAK4631 + // RAK1921/SSD1306 + #define DISP_RST -1 + #define DISP_ADDR 0x3C + #define SCL_OLED 14 + #define SDA_OLED 13 #elif BOARD_MODEL == BOARD_RNODE_NG_21 #define DISP_RST -1 #define DISP_ADDR 0x3C -#elif BOARD_MODEL == BOARD_RNODE_NG_22 +#elif BOARD_MODEL == BOARD_T3S3 #define DISP_RST 21 #define DISP_ADDR 0x3C #define SCL_OLED 17 #define SDA_OLED 18 +#elif BOARD_MODEL == BOARD_TECHO + SPIClass displaySPI = SPIClass(NRF_SPIM0, pin_disp_miso, pin_disp_sck, pin_disp_mosi); + #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 +#elif BOARD_MODEL == BOARD_XIAO_S3 + #define DISP_RST -1 + #define DISP_ADDR 0x3C + #define SCL_OLED 6 + #define SDA_OLED 5 + #define DISP_CUSTOM_ADDR true #else #define DISP_RST -1 #define DISP_ADDR 0x3C @@ -53,18 +99,47 @@ #define SMALL_FONT &Org_01 -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); + #define SSD1306_WHITE ST77XX_WHITE + #define SSD1306_BLACK ST77XX_BLACK +#elif BOARD_MODEL == BOARD_HELTEC_T114 + ST7789Spi display(&SPI1, DISPLAY_RST, DISPLAY_DC, DISPLAY_CS); + #define SSD1306_WHITE ST77XX_WHITE + #define SSD1306_BLACK ST77XX_BLACK +#elif BOARD_MODEL == BOARD_TBEAM_S_V1 + Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1); + #define SSD1306_WHITE SH110X_WHITE + #define SSD1306_BLACK SH110X_BLACK +#elif BOARD_MODEL == BOARD_TECHO + GxEPD2_BW display(GxEPD2_154_D67(pin_disp_cs, pin_disp_dc, pin_disp_reset, pin_disp_busy)); + uint32_t last_epd_refresh = 0; + uint32_t last_epd_full_refresh = 0; + #define REFRESH_PERIOD 300000 +#else + Adafruit_SSD1306 display(DISP_W, DISP_H, &Wire, DISP_RST); +#endif + +float disp_target_fps = 7; +float epd_update_fps = 0.5; #define DISP_MODE_UNKNOWN 0x00 #define DISP_MODE_LANDSCAPE 0x01 #define DISP_MODE_PORTRAIT 0x02 #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; -uint8_t disp_target_fps = 7; +uint32_t last_unblank_event = 0; +uint32_t display_blanking_timeout = DISPLAY_BLANKING_TIMEOUT; +uint8_t display_unblank_intensity = display_intensity; +bool display_blanked = false; +bool display_tx = false; +bool recondition_display = false; int disp_update_interval = 1000/disp_target_fps; +int epd_update_interval = 1000/disp_target_fps; uint32_t last_page_flip = 0; int page_interval = 4000; bool device_signatures_ok(); @@ -82,25 +157,90 @@ int p_as_y = 0; GFXcanvas1 stat_area(64, 64); GFXcanvas1 disp_area(64, 64); +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t colour); + void update_area_positions() { - if (disp_mode == DISP_MODE_PORTRAIT) { - p_ad_x = 0; - p_ad_y = 0; - p_as_x = 0; - p_as_y = 64; - } else if (disp_mode == DISP_MODE_LANDSCAPE) { - p_ad_x = 0; - p_ad_y = 0; - p_as_x = 64; - p_as_y = 0; - } + #if BOARD_MODEL == BOARD_HELTEC_T114 + if (disp_mode == DISP_MODE_PORTRAIT) { + p_ad_x = 16; + p_ad_y = 64; + p_as_x = 16; + p_as_y = p_ad_y+126; + } else if (disp_mode == DISP_MODE_LANDSCAPE) { + p_ad_x = 0; + p_ad_y = 96; + p_as_x = 126; + p_as_y = p_ad_y; + } + #elif BOARD_MODEL == BOARD_TECHO + if (disp_mode == DISP_MODE_PORTRAIT) { + p_ad_x = 61; + p_ad_y = 36; + p_as_x = 64; + p_as_y = 64+36; + } else if (disp_mode == DISP_MODE_LANDSCAPE) { + p_ad_x = 0; + p_ad_y = 0; + p_as_x = 64; + p_as_y = 0; + } + #else + if (disp_mode == DISP_MODE_PORTRAIT) { + p_ad_x = 0 * DISPLAY_SCALE; + p_ad_y = 0 * DISPLAY_SCALE; + p_as_x = 0 * DISPLAY_SCALE; + p_as_y = 64 * DISPLAY_SCALE; + } else if (disp_mode == DISP_MODE_LANDSCAPE) { + p_ad_x = 0 * DISPLAY_SCALE; + p_ad_y = 0 * DISPLAY_SCALE; + p_as_x = 64 * DISPLAY_SCALE; + p_as_y = 0 * DISPLAY_SCALE; + } + #endif } uint8_t display_contrast = 0x00; -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_HELTEC_T114 + void set_contrast(ST7789Spi *display, uint8_t value) { } +#elif BOARD_MODEL == BOARD_TECHO + void set_contrast(void *display, uint8_t value) { + if (value == 0) { analogWrite(pin_backlight, 0); } + else { analogWrite(pin_backlight, value); } + } +#elif BOARD_MODEL == BOARD_TDECK + void set_contrast(Adafruit_ST7789 *display, uint8_t value) { + static uint8_t level = 0; + static uint8_t steps = 16; + if (value > 15) value = 15; + if (value == 0) { + digitalWrite(DISPLAY_BL_PIN, 0); + delay(3); + level = 0; + return; + } + if (level == 0) { + digitalWrite(DISPLAY_BL_PIN, 1); + level = steps; + delayMicroseconds(30); + } + int from = steps - level; + int to = steps - value; + int num = (steps + to - from) % steps; + for (int i = 0; i < num; i++) { + digitalWrite(DISPLAY_BL_PIN, 0); + digitalWrite(DISPLAY_BL_PIN, 1); + } + level = value; + } +#else + void set_contrast(Adafruit_SSD1306 *display, uint8_t contrast) { display->ssd1306_command(SSD1306_SETCONTRAST); display->ssd1306_command(contrast); -} + } +#endif bool display_init() { #if HAS_DISPLAY @@ -109,7 +249,7 @@ bool display_init() { digitalWrite(pin_display_en, LOW); delay(50); digitalWrite(pin_display_en, HIGH); - #elif BOARD_MODEL == BOARD_RNODE_NG_22 + #elif BOARD_MODEL == BOARD_T3S3 Wire.begin(SDA_OLED, SCL_OLED); #elif BOARD_MODEL == BOARD_HELTEC32_V2 Wire.begin(SDA_OLED, SCL_OLED); @@ -131,71 +271,172 @@ bool display_init() { delay(50); digitalWrite(pin_display_en, HIGH); Wire.begin(SDA_OLED, SCL_OLED); + #elif BOARD_MODEL == BOARD_HELTEC_T114 + pinMode(PIN_T114_TFT_EN, OUTPUT); + digitalWrite(PIN_T114_TFT_EN, LOW); + #elif BOARD_MODEL == BOARD_TECHO + display.init(0, true, 10, false, displaySPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); + display.setPartialWindow(0, 0, DISP_W, DISP_H); + display.epd2.setBusyCallback(busyCallback); + #if HAS_BACKLIGHT + pinMode(pin_backlight, OUTPUT); + analogWrite(pin_backlight, 0); + #endif + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + Wire.begin(SDA_OLED, SCL_OLED); + #elif BOARD_MODEL == BOARD_XIAO_S3 + Wire.begin(SDA_OLED, SCL_OLED); #endif + #if HAS_EEPROM + uint8_t display_rotation = EEPROM.read(eeprom_addr(ADDR_CONF_DROT)); + #elif MCU_VARIANT == MCU_NRF52 + uint8_t display_rotation = eeprom_read(eeprom_addr(ADDR_CONF_DROT)); + #endif + if (display_rotation < 0 or display_rotation > 3) display_rotation = 0xFF; + #if DISP_CUSTOM_ADDR == true #if HAS_EEPROM - uint8_t display_address = EEPROM.read(eeprom_addr(ADDR_CONF_DADR)); + uint8_t display_address = EEPROM.read(eeprom_addr(ADDR_CONF_DADR)); #elif MCU_VARIANT == MCU_NRF52 - uint8_t display_address = eeprom_read(eeprom_addr(ADDR_CONF_DADR)); + uint8_t display_address = eeprom_read(eeprom_addr(ADDR_CONF_DADR)); #endif if (display_address == 0xFF) display_address = DISP_ADDR; #else uint8_t display_address = DISP_ADDR; #endif + #if HAS_EEPROM + if (EEPROM.read(eeprom_addr(ADDR_CONF_BSET)) == CONF_OK_BYTE) { + uint8_t db_timeout = EEPROM.read(eeprom_addr(ADDR_CONF_DBLK)); + if (db_timeout == 0x00) { + display_blanking_enabled = false; + } else { + display_blanking_enabled = true; + display_blanking_timeout = db_timeout*1000; + } + } + #elif MCU_VARIANT == MCU_NRF52 + if (eeprom_read(eeprom_addr(ADDR_CONF_BSET)) == CONF_OK_BYTE) { + uint8_t db_timeout = eeprom_read(eeprom_addr(ADDR_CONF_DBLK)); + if (db_timeout == 0x00) { + display_blanking_enabled = false; + } else { + display_blanking_enabled = true; + display_blanking_timeout = db_timeout*1000; + } + } + #endif - if(!display.begin(SSD1306_SWITCHCAPVCC, display_address)) { + #if BOARD_MODEL == BOARD_TECHO + // Don't check if display is actually connected + if(false) { + #elif BOARD_MODEL == BOARD_TDECK + display.init(240, 320); + display.setSPISpeed(80e6); + #elif BOARD_MODEL == BOARD_HELTEC_T114 + display.init(); + // set white as default pixel colour for Heltec T114 + display.setRGB(COLOR565(0xFF, 0xFF, 0xFF)); + 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 { set_contrast(&display, display_contrast); - #if BOARD_MODEL == BOARD_RNODE_NG_20 - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(3); - #elif BOARD_MODEL == BOARD_RNODE_NG_21 - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(3); - #elif BOARD_MODEL == BOARD_LORA32_V1_0 - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(3); - #elif BOARD_MODEL == BOARD_LORA32_V2_0 - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(3); - #elif BOARD_MODEL == BOARD_LORA32_V2_1 - disp_mode = DISP_MODE_LANDSCAPE; - display.setRotation(0); - #elif BOARD_MODEL == BOARD_TBEAM - disp_mode = DISP_MODE_LANDSCAPE; - display.setRotation(0); - #elif BOARD_MODEL == BOARD_HELTEC32_V2 - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(1); - #elif BOARD_MODEL == BOARD_HELTEC32_V3 - disp_mode = DISP_MODE_PORTRAIT; - // Antenna conx up - display.setRotation(1); - // USB-C up - // display.setRotation(3); - #else - disp_mode = DISP_MODE_PORTRAIT; - display.setRotation(3); - #endif + if (display_rotation != 0xFF) { + if (display_rotation == 0 || display_rotation == 2) { + disp_mode = DISP_MODE_LANDSCAPE; + } else { + disp_mode = DISP_MODE_PORTRAIT; + } + display.setRotation(display_rotation); + } else { + #if BOARD_MODEL == BOARD_RNODE_NG_20 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #elif BOARD_MODEL == BOARD_RNODE_NG_21 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #elif BOARD_MODEL == BOARD_LORA32_V1_0 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #elif BOARD_MODEL == BOARD_LORA32_V2_0 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #elif BOARD_MODEL == BOARD_LORA32_V2_1 + disp_mode = DISP_MODE_LANDSCAPE; + display.setRotation(0); + #elif BOARD_MODEL == BOARD_TBEAM + disp_mode = DISP_MODE_LANDSCAPE; + display.setRotation(0); + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(1); + #elif BOARD_MODEL == BOARD_HELTEC32_V2 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(1); + #elif BOARD_MODEL == BOARD_HELTEC32_V3 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(1); + #elif BOARD_MODEL == BOARD_HELTEC_T114 + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(1); + #elif BOARD_MODEL == BOARD_RAK4631 + disp_mode = DISP_MODE_LANDSCAPE; + display.setRotation(0); + #elif BOARD_MODEL == BOARD_TDECK + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #elif BOARD_MODEL == BOARD_TECHO + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #else + disp_mode = DISP_MODE_PORTRAIT; + display.setRotation(3); + #endif + } update_area_positions(); - for (int i = 0; i < WATERFALL_SIZE; i++) { - waterfall[i] = 0; - } + + for (int i = 0; i < WATERFALL_SIZE; i++) { waterfall[i] = 0; } last_page_flip = millis(); stat_area.cp437(true); disp_area.cp437(true); + + #if BOARD_MODEL != BOARD_HELTEC_T114 display.cp437(true); + #endif #if HAS_EEPROM - uint8_t display_intensity = EEPROM.read(eeprom_addr(ADDR_CONF_DINT)); + display_intensity = EEPROM.read(eeprom_addr(ADDR_CONF_DINT)); #elif MCU_VARIANT == MCU_NRF52 - uint8_t display_intensity = eeprom_read(eeprom_addr(ADDR_CONF_DINT)); + display_intensity = eeprom_read(eeprom_addr(ADDR_CONF_DINT)); + #endif + display_unblank_intensity = display_intensity; + + #if BOARD_MODEL == BOARD_TECHO + #if HAS_BACKLIGHT + if (display_intensity == 0) { analogWrite(pin_backlight, 0); } + else { analogWrite(pin_backlight, display_intensity); } + #endif + #endif + + #if BOARD_MODEL == BOARD_TDECK + display.fillScreen(SSD1306_BLACK); + #endif + + #if BOARD_MODEL == BOARD_HELTEC_T114 + // Enable backlight led (display is always black without this) + fillRect(p_ad_x, p_ad_y, 128, 128, SSD1306_BLACK); + fillRect(p_as_x, p_as_y, 128, 128, SSD1306_BLACK); + pinMode(PIN_T114_TFT_BLGT, OUTPUT); + digitalWrite(PIN_T114_TFT_BLGT, LOW); #endif return true; @@ -205,6 +446,60 @@ bool display_init() { #endif } +// Draws a line on the screen +void drawLine(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t colour) { + #if BOARD_MODEL == BOARD_HELTEC_T114 + if(colour == SSD1306_WHITE){ + display.setColor(WHITE); + } else if(colour == SSD1306_BLACK) { + display.setColor(BLACK); + } + display.drawLine(x, y, width, height); + #else + display.drawLine(x, y, width, height, colour); + #endif +} + +// Draws a filled rectangle on the screen +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t colour) { + #if BOARD_MODEL == BOARD_HELTEC_T114 + if(colour == SSD1306_WHITE){ + display.setColor(WHITE); + } else if(colour == SSD1306_BLACK) { + display.setColor(BLACK); + } + display.fillRect(x, y, width, height); + #else + display.fillRect(x, y, width, height, colour); + #endif +} + +// Draws a bitmap to the display and auto scales it based on the boards configured DISPLAY_SCALE +void drawBitmap(int16_t startX, int16_t startY, const uint8_t* bitmap, int16_t bitmapWidth, int16_t bitmapHeight, uint16_t foregroundColour, uint16_t backgroundColour) { + #if DISPLAY_SCALE == 1 + display.drawBitmap(startX, startY, bitmap, bitmapWidth, bitmapHeight, foregroundColour, backgroundColour); + #else + for(int16_t row = 0; row < bitmapHeight; row++){ + for(int16_t col = 0; col < bitmapWidth; col++){ + + // determine index and bitmask + int16_t index = row * ((bitmapWidth + 7) / 8) + (col / 8); + uint8_t bitmask = 1 << (7 - (col % 8)); + + // check if the current pixel is set in the bitmap + if(bitmap[index] & bitmask){ + // draw a scaled rectangle for the foreground pixel + fillRect(startX + col * DISPLAY_SCALE, startY + row * DISPLAY_SCALE, DISPLAY_SCALE, DISPLAY_SCALE, foregroundColour); + } else { + // draw a scaled rectangle for the background pixel + fillRect(startX + col * DISPLAY_SCALE, startY + row * DISPLAY_SCALE, DISPLAY_SCALE, DISPLAY_SCALE, backgroundColour); + } + + } + } + #endif +} + void draw_cable_icon(int px, int py) { if (cable_state == CABLE_STATE_DISCONNECTED) { stat_area.drawBitmap(px, py, bm_cable+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK); @@ -248,15 +543,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) { stat_area.fillRect(px-2, py-2, 18, 7, SSD1306_BLACK); stat_area.drawBitmap(px-2, py-2, bm_plug, 17, 7, SSD1306_WHITE, SSD1306_BLACK); } else { @@ -292,49 +595,64 @@ void draw_battery_bars(int px, int py) { #define Q_SNR_MIN_BASE -9.0 #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)lora_sf*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; - stat_area.fillRect(px, py, 13, 7, SSD1306_BLACK); - // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality); - if (quality > 0) stat_area.drawLine(px+0*2, py+7, px+0*2, py+6, SSD1306_WHITE); - if (quality > 15) stat_area.drawLine(px+1*2, py+7, px+1*2, py+5, SSD1306_WHITE); - if (quality > 30) stat_area.drawLine(px+2*2, py+7, px+2*2, py+4, SSD1306_WHITE); - if (quality > 45) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE); - if (quality > 60) stat_area.drawLine(px+4*2, py+7, px+4*2, py+2, SSD1306_WHITE); - if (quality > 75) stat_area.drawLine(px+5*2, py+7, px+5*2, py+1, SSD1306_WHITE); - if (quality > 90) stat_area.drawLine(px+6*2, py+7, px+6*2, py+0, SSD1306_WHITE); + if (radio_online) { + signed char t_snr = (signed int)last_snr_raw; + int snr_int = (int)t_snr; + float snr_min = Q_SNR_MIN_BASE-(int)lora_sf*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; + + // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality); + if (quality > 0) stat_area.drawLine(px+0*2, py+7, px+0*2, py+6, SSD1306_WHITE); + if (quality > 15) stat_area.drawLine(px+1*2, py+7, px+1*2, py+5, SSD1306_WHITE); + if (quality > 30) stat_area.drawLine(px+2*2, py+7, px+2*2, py+4, SSD1306_WHITE); + if (quality > 45) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE); + if (quality > 60) stat_area.drawLine(px+4*2, py+7, px+4*2, py+2, SSD1306_WHITE); + if (quality > 75) stat_area.drawLine(px+5*2, py+7, px+5*2, py+1, SSD1306_WHITE); + if (quality > 90) stat_area.drawLine(px+6*2, py+7, px+6*2, py+0, SSD1306_WHITE); + } } -#define S_RSSI_MIN -135.0 -#define S_RSSI_MAX -75.0 +#if MODEM == SX1280 + #define S_RSSI_MIN -105.0 + #define S_RSSI_MAX -65.0 +#else + #define S_RSSI_MIN -135.0 + #define S_RSSI_MAX -75.0 +#endif #define S_RSSI_SPAN (S_RSSI_MAX-S_RSSI_MIN) void draw_signal_bars(int px, int py) { - 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; - stat_area.fillRect(px, py, 13, 7, SSD1306_BLACK); - // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality); - if (signal > 85) stat_area.drawLine(px+0*2, py+7, px+0*2, py+0, SSD1306_WHITE); - if (signal > 72) stat_area.drawLine(px+1*2, py+7, px+1*2, py+1, SSD1306_WHITE); - if (signal > 59) stat_area.drawLine(px+2*2, py+7, px+2*2, py+2, SSD1306_WHITE); - if (signal > 46) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE); - if (signal > 33) stat_area.drawLine(px+4*2, py+7, px+4*2, py+4, SSD1306_WHITE); - if (signal > 20) stat_area.drawLine(px+5*2, py+7, px+5*2, py+5, SSD1306_WHITE); - if (signal > 7) stat_area.drawLine(px+6*2, py+7, px+6*2, py+6, SSD1306_WHITE); + + if (radio_online) { + int rssi_val = last_rssi; + if (rssi_val < S_RSSI_MIN) rssi_val = S_RSSI_MIN; + if (rssi_val > S_RSSI_MAX) rssi_val = S_RSSI_MAX; + 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; + + // Serial.printf("Last SNR: %.2f\n, quality: %.2f\n", snr, quality); + if (signal > 85) stat_area.drawLine(px+0*2, py+7, px+0*2, py+0, SSD1306_WHITE); + if (signal > 72) stat_area.drawLine(px+1*2, py+7, px+1*2, py+1, SSD1306_WHITE); + if (signal > 59) stat_area.drawLine(px+2*2, py+7, px+2*2, py+2, SSD1306_WHITE); + if (signal > 46) stat_area.drawLine(px+3*2, py+7, px+3*2, py+3, SSD1306_WHITE); + if (signal > 33) stat_area.drawLine(px+4*2, py+7, px+4*2, py+4, SSD1306_WHITE); + if (signal > 20) stat_area.drawLine(px+5*2, py+7, px+5*2, py+5, SSD1306_WHITE); + if (signal > 7) stat_area.drawLine(px+6*2, py+7, px+6*2, py+6, SSD1306_WHITE); + } } +#if MODEM == SX1280 + #define WF_TX_SIZE 5 +#else + #define WF_TX_SIZE 5 +#endif #define WF_RSSI_MAX -60 #define WF_RSSI_MIN -135 #define WF_RSSI_SPAN (WF_RSSI_MAX-WF_RSSI_MIN) @@ -344,9 +662,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[waterfall_head++] = rssi_normalised; - if (waterfall_head >= WATERFALL_SIZE) waterfall_head = 0; + if (display_tx) { + for (uint8_t i = 0; i < WF_TX_SIZE; i++) { + waterfall[waterfall_head++] = -1; + if (waterfall_head >= WATERFALL_SIZE) waterfall_head = 0; + } + display_tx = false; + } else { + waterfall[waterfall_head++] = rssi_normalised; + if (waterfall_head >= WATERFALL_SIZE) waterfall_head = 0; + } stat_area.fillRect(px,py,WF_PIXEL_WIDTH, WATERFALL_SIZE, SSD1306_BLACK); for (int i = 0; i < WATERFALL_SIZE; i++){ @@ -354,6 +679,11 @@ void draw_waterfall(int px, int py) { int ws = waterfall[wi]; if (ws > 0) { stat_area.drawLine(px, py+i, px+ws-1, py+i, SSD1306_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, SSD1306_WHITE); + } } } } @@ -371,9 +701,9 @@ void draw_stat_area() { draw_lora_icon(45, 8); draw_mw_icon(45, 30); draw_battery_bars(4, 58); + draw_quality_bars(28, 56); + draw_signal_bars(44, 56); if (radio_online) { - draw_quality_bars(28, 56); - draw_signal_bars(44, 56); draw_waterfall(27, 4); } } @@ -381,22 +711,22 @@ void draw_stat_area() { void update_stat_area() { if (eeprom_ok && !firmware_update_mode && !console_active) { - + draw_stat_area(); if (disp_mode == DISP_MODE_PORTRAIT) { - display.drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); + drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); } else if (disp_mode == DISP_MODE_LANDSCAPE) { - display.drawBitmap(p_as_x+2, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); - if (device_init_done && !disp_ext_fb) display.drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE); + drawBitmap(p_as_x+2, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); + if (device_init_done && !disp_ext_fb) drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE); } } else { if (firmware_update_mode) { - display.drawBitmap(p_as_x, p_as_y, bm_updating, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE); + drawBitmap(p_as_x, p_as_y, bm_updating, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE); } else if (console_active && device_init_done) { - display.drawBitmap(p_as_x, p_as_y, bm_console, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE); + drawBitmap(p_as_x, p_as_y, bm_console, stat_area.width(), stat_area.height(), SSD1306_BLACK, SSD1306_WHITE); if (disp_mode == DISP_MODE_LANDSCAPE) { - display.drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE); + drawLine(p_as_x, 0, p_as_x, 64, SSD1306_WHITE); } } } @@ -493,7 +823,7 @@ void draw_disp_area() { if (!modem_installed) { disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK); } else { - disp_area.drawBitmap(0, 37, bm_hwfail, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK); + disp_area.drawBitmap(0, 37, bm_conf_missing, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK); } } } else if (bt_state == BT_STATE_PAIRING and bt_ssp_pin != 0) { @@ -556,35 +886,142 @@ void draw_disp_area() { void update_disp_area() { draw_disp_area(); - display.drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK); + + drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK); if (disp_mode == DISP_MODE_LANDSCAPE) { if (device_init_done && !firmware_update_mode && !disp_ext_fb) { - display.drawLine(0, 0, 0, 63, SSD1306_WHITE); + drawLine(0, 0, 0, 63, SSD1306_WHITE); } } } -void update_display(bool blank = false) { - if (blank) { - if (display_contrast != display_intensity) { - display_contrast = display_intensity; - set_contrast(&display, display_contrast); +void display_recondition() { + #if PLATFORM == PLATFORM_ESP32 + for (uint8_t iy = 0; iy < disp_area.height(); iy++) { + unsigned char rand_seg [] = {random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF),random(0xFF)}; + stat_area.drawBitmap(0, iy, rand_seg, 64, 1, SSD1306_WHITE, SSD1306_BLACK); + disp_area.drawBitmap(0, iy, rand_seg, 64, 1, SSD1306_WHITE, SSD1306_BLACK); } - display.clearDisplay(); - display.display(); + + drawBitmap(p_ad_x, p_ad_y, disp_area.getBuffer(), disp_area.width(), disp_area.height(), SSD1306_WHITE, SSD1306_BLACK); + if (disp_mode == DISP_MODE_PORTRAIT) { + drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); + } else if (disp_mode == DISP_MODE_LANDSCAPE) { + drawBitmap(p_as_x, p_as_y, stat_area.getBuffer(), stat_area.width(), stat_area.height(), SSD1306_WHITE, SSD1306_BLACK); + } + #endif +} + +bool epd_blanked = false; +#if BOARD_MODEL == BOARD_TECHO + void epd_blank(bool full_update = true) { + display.setFullWindow(); + display.fillScreen(SSD1306_WHITE); + display.display(full_update); + } + + void epd_black(bool full_update = true) { + display.setFullWindow(); + display.fillScreen(SSD1306_BLACK); + display.display(full_update); + } +#endif + +void update_display(bool blank = false) { + display_updating = true; + if (blank == true) { + last_disp_update = millis()-disp_update_interval-1; } else { + if (display_blanking_enabled && millis()-last_unblank_event >= display_blanking_timeout) { + blank = true; + display_blanked = true; + if (display_intensity != 0) { + display_unblank_intensity = display_intensity; + } + display_intensity = 0; + } else { + display_blanked = false; + if (display_unblank_intensity != 0x00) { + display_intensity = display_unblank_intensity; + display_unblank_intensity = 0x00; + } + } + } + + if (blank) { if (millis()-last_disp_update >= disp_update_interval) { if (display_contrast != display_intensity) { display_contrast = display_intensity; set_contrast(&display, display_contrast); } - display.clearDisplay(); - update_stat_area(); - update_disp_area(); - display.display(); + + #if BOARD_MODEL == BOARD_TECHO + if (!epd_blanked) { + epd_blank(); + epd_blanked = true; + } + #endif + + #if BOARD_MODEL == BOARD_HELTEC_T114 + display.clear(); + display.display(); + #elif BOARD_MODEL != BOARD_TDECK && BOARD_MODEL != BOARD_TECHO + display.clearDisplay(); + display.display(); + #else + // TODO: Clear screen + #endif + + last_disp_update = millis(); + } + + } else { + if (millis()-last_disp_update >= disp_update_interval) { + uint32_t current = millis(); + if (display_contrast != display_intensity) { + display_contrast = display_intensity; + set_contrast(&display, display_contrast); + } + + #if BOARD_MODEL == BOARD_HELTEC_T114 + display.clear(); + #elif BOARD_MODEL != BOARD_TDECK && BOARD_MODEL != BOARD_TECHO + display.clearDisplay(); + #endif + + if (recondition_display) { + disp_target_fps = 30; + disp_update_interval = 1000/disp_target_fps; + display_recondition(); + } else { + #if BOARD_MODEL == BOARD_TECHO + display.setFullWindow(); + display.fillScreen(SSD1306_WHITE); + #endif + + update_stat_area(); + update_disp_area(); + } + + #if BOARD_MODEL == BOARD_TECHO + if (current-last_epd_refresh >= epd_update_interval) { + if (current-last_epd_full_refresh >= REFRESH_PERIOD) { display.display(false); last_epd_full_refresh = millis(); } + else { display.display(true); } + last_epd_refresh = millis(); + epd_blanked = false; + } + #elif BOARD_MODEL != BOARD_TDECK + display.display(); + #endif + last_disp_update = millis(); } } + display_updating = false; +} + +void display_unblank() { + last_unblank_event = millis(); } void ext_fb_enable() { diff --git a/Framing.h b/Framing.h index 0236aa9..9346f70 100644 --- 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 @@ -45,6 +45,7 @@ #define CMD_STAT_CHTM 0x25 #define CMD_STAT_PHYPRM 0x26 #define CMD_STAT_BAT 0x27 + #define CMD_STAT_CSMA 0x28 #define CMD_BLINK 0x30 #define CMD_RANDOM 0x40 @@ -52,10 +53,17 @@ #define CMD_FB_READ 0x42 #define CMD_FB_WRITE 0x43 #define CMD_FB_READL 0x44 + #define CMD_DISP_READ 0x66 #define CMD_DISP_INT 0x45 #define CMD_DISP_ADDR 0x63 + #define CMD_DISP_BLNK 0x64 + #define CMD_DISP_ROT 0x67 + #define CMD_DISP_RCND 0x68 + #define CMD_NP_INT 0x65 #define CMD_BT_CTRL 0x46 + #define CMD_BT_UNPAIR 0x70 #define CMD_BT_PIN 0x62 + #define CMD_DIS_IA 0x69 #define CMD_BOARD 0x47 #define CMD_PLATFORM 0x48 @@ -75,6 +83,11 @@ #define CMD_RESET 0x55 #define CMD_RESET_BYTE 0xF8 + #define CMD_LOG 0x80 + #define CMD_TIME 0x81 + #define CMD_MUX_CHAIN 0x82 + #define CMD_MUX_DSCVR 0x83 + #define DETECT_REQ 0x73 #define DETECT_RESP 0x46 @@ -91,6 +104,8 @@ #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; diff --git a/Graphics.h b/Graphics.h index 3f1daf5..3da21f1 100644 --- a/Graphics.h +++ b/Graphics.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 @@ -311,6 +311,23 @@ const unsigned char bm_hwfail [] PROGMEM = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +const unsigned char bm_conf_missing [] PROGMEM = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x33, 0x87, 0x0c, 0xcc, 0xe1, 0xff, 0xff, + 0xe2, 0x33, 0x3e, 0x7c, 0xc4, 0xcf, 0xff, 0xff, 0xe0, 0x33, 0x8f, 0x1c, 0xc0, 0xc9, 0xff, 0xff, + 0xe5, 0x33, 0xe7, 0xcc, 0xc8, 0xc9, 0xff, 0xff, 0xe7, 0x33, 0x0e, 0x1c, 0xcc, 0xe3, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xf1, 0xe3, 0x99, 0x86, 0x70, 0xff, 0xf6, 0xff, + 0xe4, 0xc9, 0x89, 0x9e, 0x67, 0xff, 0xf6, 0xff, 0xe7, 0xc9, 0x81, 0x86, 0x64, 0xff, 0xef, 0x7f, + 0xe4, 0xc9, 0x91, 0x9e, 0x64, 0xff, 0xe9, 0x7f, 0xf1, 0xe3, 0x99, 0x9e, 0x71, 0xff, 0xd9, 0xbf, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xef, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + const unsigned char bm_no_radio [] PROGMEM = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc7, 0x0e, 0x71, 0xfc, 0xce, 0x38, 0x7f, diff --git a/Makefile b/Makefile index a855129..b6f5a7b 100644 --- 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,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -ESP_IDF_VER = 2.0.15 +# Version 2.0.17 of the Arduino ESP core is based on ESP-IDF v4.4.7 +ARDUINO_ESP_CORE_VER = 2.0.17 + +# Version 3.2.0 of the Arduino ESP core is based on ESP-IDF v5.4.1 +# ARDUINO_ESP_CORE_VER = 3.2.0 all: release @@ -30,8 +34,11 @@ prep-avr: prep-esp32: arduino-cli core update-index --config-file arduino-cli.yaml - arduino-cli core install esp32:esp32 --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" @@ -42,6 +49,11 @@ prep-samd: prep-nrf: arduino-cli core update-index --config-file arduino-cli.yaml arduino-cli core install rakwireless:nrf52 --config-file arduino-cli.yaml + arduino-cli core install Heltec_nRF52:Heltec_nRF52 --config-file arduino-cli.yaml + arduino-cli core install adafruit:nrf52 --config-file arduino-cli.yaml + arduino-cli lib install "GxEPD2" + arduino-cli config set library.enable_unsafe_install true + arduino-cli lib install --git-url https://github.com/liamcottle/esp8266-oled-ssd1306#e16cee124fe26490cb14880c679321ad8ac89c95 pip install adafruit-nrfutil --upgrade console-site: @@ -56,62 +68,86 @@ upload-spiffs: @echo Deploying SPIFFS image... python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin +check_bt_buffers: + @./esp32_btbufs.py ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/libraries/BluetoothSerial/src/BluetoothSerial.cpp + firmware: - arduino-cli compile --fqbn unsignedio:avr:rnode + arduino-cli compile --log --fqbn unsignedio:avr:rnode firmware-mega2560: - arduino-cli compile --fqbn arduino:avr:mega + arduino-cli compile --log --fqbn arduino:avr:mega -firmware-tbeam: - arduino-cli compile --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\"" +firmware-tbeam: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\"" -firmware-tbeam_sx126x: - arduino-cli compile --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\"" +firmware-tbeam_sx126x: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\"" firmware-t3s3: - arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\"" + arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x03\"" -firmware-lora32_v10: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\"" +firmware-t3s3_sx127x: + arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x01\"" -firmware-lora32_v10_extled: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\"" +firmware-t3s3_sx1280_pa: + arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x04\"" -firmware-lora32_v20: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\"" +firmware-tdeck: + arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3B\"" -firmware-lora32_v21: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\"" +firmware-tbeam_supreme: + arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x3D" -firmware-lora32_v21_extled: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\"" +firmware-lora32_v10: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\"" -firmware-lora32_v21_tcxo: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\"" +firmware-lora32_v10_extled: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\"" -firmware-heltec32_v2: - arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\"" +firmware-lora32_v20: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\"" -firmware-heltec32_v2_extled: - arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\"" +firmware-lora32_v21: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\"" + +firmware-lora32_v21_extled: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\"" + +firmware-lora32_v21_tcxo: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\"" + +firmware-heltec32_v2: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\"" + +firmware-heltec32_v2_extled: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\"" firmware-heltec32_v3: - arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\"" + arduino-cli compile --log --fqbn esp32:esp32:heltec_wifi_lora_32_V3 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\"" -firmware-rnode_ng_20: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\"" +firmware-rnode_ng_20: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\"" -firmware-rnode_ng_21: - arduino-cli compile --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\"" +firmware-rnode_ng_21: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x41\"" -firmware-featheresp32: - arduino-cli compile --fqbn esp32:esp32:featheresp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\"" +firmware-featheresp32: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:featheresp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x34\"" -firmware-genericesp32: - arduino-cli compile --fqbn esp32:esp32:esp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\"" +firmware-genericesp32: check_bt_buffers + arduino-cli compile --log --fqbn esp32:esp32:esp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\"" firmware-rak4631: - arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\"" + arduino-cli compile --log --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\"" + +firmware-heltec_t114: + arduino-cli compile --log --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\"" + +firmware-techo: + arduino-cli compile --log --fqbn adafruit:nrf52:pca10056 -e --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\"" + +firmware-xiao_s3: + arduino-cli compile --log --fqbn "esp32:esp32:XIAO_ESP32S3" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3E\"" upload: arduino-cli upload -p /dev/ttyUSB0 --fqbn unsignedio:avr:rnode @@ -120,11 +156,18 @@ upload-mega2560: arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:mega upload-tbeam: + arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:t-beam + @sleep 1 + rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware.ino.bin) + @sleep 3 + python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin + +upload-tbeam_sx1262: arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:t-beam @sleep 1 rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.t-beam/RNode_Firmware.ino.bin) - @sleep 3 - python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin + #@sleep 3 + #python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-lora32_v10: arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:ttgo-lora32 @@ -148,18 +191,32 @@ upload-lora32_v21: python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-heltec32_v2: - arduino-cli upload -p /dev/ttyUSB1 --fqbn esp32:esp32:heltec_wifi_lora_32_V2 + arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:heltec_wifi_lora_32_V2 @sleep 1 - rnodeconf /dev/ttyUSB1 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bin) + rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V2/RNode_Firmware.ino.bin) @sleep 3 python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyUSB1 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-heltec32_v3: - arduino-cli upload -p /dev/ttyUSB1 --fqbn esp32:esp32:heltec_wifi_lora_32_V3 + arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:heltec_wifi_lora_32_V3 @sleep 1 - rnodeconf /dev/ttyUSB1 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.bin) + rnodeconf /dev/ttyUSB0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.heltec_wifi_lora_32_V3/RNode_Firmware.ino.bin) @sleep 3 - python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyUSB1 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin + python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin + +upload-tdeck: + arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3 + @sleep 1 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin) + @sleep 3 + python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin + +upload-tbeam_supreme: + arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3 + @sleep 1 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin) + @sleep 3 + python ./Release/esptool/esptool.py --chip esp32-s3 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-rnode_ng_20: arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:ttgo-lora32 @@ -176,17 +233,11 @@ upload-rnode_ng_21: python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-t3s3: - @echo - @echo Put board into flashing mode by holding BOOT button while momentarily pressing the RESET button. Hit enter when done. - @read arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:esp32s3 - @sleep 2 - python ./Release/esptool/esptool.py --chip esp32s3 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin - @echo - @echo Press the RESET button on the board now, and hit enter - @read @sleep 1 rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin) + @sleep 3 + python ./Release/esptool/esptool.py --chip esp32s3 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin upload-featheresp32: arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:featheresp32 @@ -197,12 +248,29 @@ upload-featheresp32: upload-rak4631: arduino-cli upload -p /dev/ttyACM0 --fqbn rakwireless:nrf52:WisCoreRAK4631Board + @sleep 1 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0) +upload-heltec_t114: + arduino-cli upload -p /dev/ttyACM0 --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 + @sleep 1 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0) +upload-techo: + arduino-cli upload -p /dev/ttyACM0 --fqbn adafruit:nrf52:pca10056 + @sleep 6 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0) + +upload-xiao_s3: + arduino-cli upload -p /dev/ttyACM0 --fqbn esp32:esp32:XIAO_ESP32S3 + @sleep 1 + rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.XIAO_ESP32S3/RNode_Firmware.ino.bin) + @sleep 3 + python ./Release/esptool/esptool.py --chip esp32s3 --port /dev/ttyACM0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin release: release-all -release-all: console-site spiffs-image release-rnode 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-hashes +release-all: release-tbeam release-tbeam_sx1262 release-lora32_v10 release-lora32_v20 release-lora32_v21 release-lora32_v10_extled release-lora32_v20_extled release-lora32_v21_extled release-lora32_v21_tcxo release-featheresp32 release-genericesp32 release-heltec32_v2 release-heltec32_v3 release-heltec32_v2_extled release-heltec_t114 release-techo release-rnode_ng_20 release-rnode_ng_21 release-t3s3 release-t3s3_sx127x release-t3s3_sx1280_pa release-tdeck release-tbeam_supreme release-rak4631 release-xiao_s3 release-hashes release-hashes: python ./release_hashes.py > ./Release/release.json @@ -212,126 +280,126 @@ release-rnode: cp build/unsignedio.avr.rnode/RNode_Firmware.ino.hex Release/rnode_firmware.hex rm -r build -release-tbeam: +release-tbeam: check_bt_buffers arduino-cli compile --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam.boot_app0 cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bin build/rnode_firmware_tbeam.bin cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tbeam.bootloader cp build/esp32.esp32.t-beam/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tbeam.partitions 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: +release-tbeam_sx1262: check_bt_buffers arduino-cli compile --fqbn esp32:esp32:t-beam -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x33\" \"-DMODEM=0x03\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam_sx1262.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tbeam_sx1262.boot_app0 cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bin build/rnode_firmware_tbeam_sx1262.bin cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tbeam_sx1262.bootloader cp build/esp32.esp32.t-beam/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tbeam_sx1262.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v10.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v10.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v10.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v10.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v20.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v20.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v20.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v20.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v21.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v21.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v21.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\" \"-DEXTERNAL_LEDS=true\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v10.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v10.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v10.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v10.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v10.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x36\" \"-DEXTERNAL_LEDS=true\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v20.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v20.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v20.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v20.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v20.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DEXTERNAL_LEDS=true\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v21.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v21.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v21.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x37\" \"-DENABLE_TCXO=true\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21_tcxo.boot_app0 + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_lora32v21_tcxo.boot_app0 cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_lora32v21_tcxo.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_lora32v21_tcxo.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_lora32v21_tcxo.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.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-heltec32_v3: +release-heltec32_v3: check_bt_buffers arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V3 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3A\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v3.boot_app0 + 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0 + 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.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 @@ -339,26 +407,62 @@ release-rnode_ng_21: rm -r build release-t3s3: - arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\"" - cp ~/.arduino15/packages/esp32/hardware/esp32/$(ESP_IDF_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3.boot_app0 + arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x03\"" + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3.boot_app0 cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3.bin cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3.bootloader cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3.partitions zip --junk-paths ./Release/rnode_firmware_t3s3.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3.boot_app0 build/rnode_firmware_t3s3.bin build/rnode_firmware_t3s3.bootloader build/rnode_firmware_t3s3.partitions rm -r build -release-featheresp32: +release-t3s3_sx1280_pa: + arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x04\"" + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx1280_pa.boot_app0 + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3_sx1280_pa.bin + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3_sx1280_pa.bootloader + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3_sx1280_pa.partitions + zip --junk-paths ./Release/rnode_firmware_t3s3_sx1280_pa.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx1280_pa.boot_app0 build/rnode_firmware_t3s3_sx1280_pa.bin build/rnode_firmware_t3s3_sx1280_pa.bootloader build/rnode_firmware_t3s3_sx1280_pa.partitions + rm -r build + +release-t3s3_sx127x: + arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DMODEM=0x01\"" + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx127x.boot_app0 + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_t3s3_sx127x.bin + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_t3s3_sx127x.bootloader + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_t3s3_sx127x.partitions + zip --junk-paths ./Release/rnode_firmware_t3s3_sx127x.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx127x.boot_app0 build/rnode_firmware_t3s3_sx127x.bin build/rnode_firmware_t3s3_sx127x.bootloader build/rnode_firmware_t3s3_sx127x.partitions + rm -r build + +release-tdeck: + arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3B\"" + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_tdeck.boot_app0 + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_tdeck.bin + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_tdeck.bootloader + cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_tdeck.partitions + zip --junk-paths ./Release/rnode_firmware_tdeck.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_tdeck.boot_app0 build/rnode_firmware_tdeck.bin build/rnode_firmware_tdeck.bootloader build/rnode_firmware_tdeck.partitions + rm -r build + +release-tbeam_supreme: + arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3D\"" + 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_featheresp32.boot_app0 cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bin build/rnode_firmware_featheresp32.bin cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_featheresp32.bootloader cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_featheresp32.partitions 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 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --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 ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_esp32_generic.boot_app0 cp build/esp32.esp32.esp32/RNode_Firmware.ino.bin build/rnode_firmware_esp32_generic.bin cp build/esp32.esp32.esp32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_esp32_generic.bootloader cp build/esp32.esp32.esp32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_esp32_generic.partitions @@ -374,3 +478,22 @@ release-rak4631: arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\"" cp build/rakwireless.nrf52.WisCoreRAK4631Board/RNode_Firmware.ino.hex build/rnode_firmware_rak4631.hex adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_rak4631.hex Release/rnode_firmware_rak4631.zip + +release-heltec_t114: + arduino-cli compile --fqbn Heltec_nRF52:Heltec_nRF52:HT-n5262 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3C\"" + cp build/Heltec_nRF52.Heltec_nRF52.HT-n5262/RNode_Firmware.ino.hex build/rnode_firmware_heltec_t114.hex + adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_heltec_t114.hex Release/rnode_firmware_heltec_t114.zip + +release-techo: + arduino-cli compile --log --fqbn adafruit:nrf52:pca10056 -e --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\"" + cp build/adafruit.nrf52.pca10056/RNode_Firmware.ino.hex build/rnode_firmware_techo.hex + adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_techo.hex Release/rnode_firmware_techo.zip + +release-xiao_s3: + arduino-cli compile --fqbn "esp32:esp32:XIAO_ESP32S3" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3E\"" + cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_xiao_esp32s3.boot_app0 + cp build/esp32.esp32.XIAO_ESP32S3/RNode_Firmware.ino.bin build/rnode_firmware_xiao_esp32s3.bin + cp build/esp32.esp32.XIAO_ESP32S3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_xiao_esp32s3.bootloader + cp build/esp32.esp32.XIAO_ESP32S3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_xiao_esp32s3.partitions + zip --junk-paths ./Release/rnode_firmware_xiao_esp32s3.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_xiao_esp32s3.boot_app0 build/rnode_firmware_xiao_esp32s3.bin build/rnode_firmware_xiao_esp32s3.bootloader build/rnode_firmware_xiao_esp32s3.partitions + rm -r build \ No newline at end of file diff --git a/Power.h b/Power.h index 07eef08..d6af640 100644 --- 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 . + +#if BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_TBEAM_S_V1 #include 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 @@ -44,6 +61,89 @@ 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_T3S3 + #define BAT_V_MIN 3.15 + #define BAT_V_MAX 4.217 + #define BAT_V_CHG 4.48 + #define BAT_V_FLOAT 4.33 + #define BAT_SAMPLES 5 + const uint8_t pin_vbat = 1; + float bat_p_samples[BAT_SAMPLES]; + float bat_v_samples[BAT_SAMPLES]; + uint8_t bat_samples_count = 0; + int bat_discharging_samples = 0; + int bat_charging_samples = 0; + int bat_charged_samples = 0; + bool bat_voltage_dropping = false; + float bat_delay_v = 0; + float bat_state_change_v = 0; +#elif BOARD_MODEL == BOARD_TDECK + #define BAT_V_MIN 3.15 + #define BAT_V_MAX 4.3 + #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; +#elif BOARD_MODEL == BOARD_HELTEC_T114 + #define BAT_V_MIN 3.15 + #define BAT_V_MAX 4.165 + #define BAT_V_CHG 4.48 + #define BAT_V_FLOAT 4.33 + #define BAT_SAMPLES 7 + const uint8_t pin_vbat = 4; + const uint8_t pin_ctrl = 6; + 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_TECHO + #define BAT_V_MIN 3.15 + #define BAT_V_MAX 4.16 + #define BAT_V_CHG 4.48 + #define BAT_V_FLOAT 4.33 + #define BAT_SAMPLES 7 + 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; #endif uint32_t last_pmu_update = 0; @@ -54,10 +154,23 @@ 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 || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TECHO 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; + #elif BOARD_MODEL == BOARD_T3S3 + float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*6.7828; + #elif BOARD_MODEL == BOARD_HELTEC_T114 + float battery_measurement = (float)(analogRead(pin_vbat)) * 0.017165; + #elif BOARD_MODEL == BOARD_TECHO + float battery_measurement = (float)(analogRead(pin_vbat)) * 0.007067; + #else + float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*7.26; + #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++; @@ -80,41 +193,61 @@ 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 MCU_VARIANT == MCU_NRF52 + if (bt_state != BT_STATE_OFF) { blebas.write(battery_percent); } + #endif // 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); - // } else { - // SerialBT.print(" Voltage is not dropping.\n"); - // } + // if (bat_voltage_dropping) { SerialBT.printf(" Voltage is dropping. Percentage %.1f%%.", battery_percent); } + // else { 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; @@ -152,7 +285,7 @@ void measure_battery() { } } } else { - battery_state = BATTERY_STATE_DISCHARGING; + battery_state = BATTERY_STATE_UNKNOWN; battery_percent = 0.0; battery_voltage = 0.0; } @@ -211,31 +344,33 @@ 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 || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_TECHO 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_HELTEC_T114 + pinMode(pin_ctrl,OUTPUT); + digitalWrite(pin_ctrl, HIGH); + 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"); } } @@ -340,6 +475,86 @@ bool init_pmu() { // Set the time of pressing the button to turn off PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S); + 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; diff --git a/README.md b/README.md index 34a7cdb..d6cf721 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +***Important!** This repository is currently functioning as a stable reference for the default RNode Firmware, and only receives bugfix and security updates. Further development, new features and expanded board support is now happening at the [RNode Firmware Community Edition](https://github.com/liberatedsystems/RNode_Firmware_CE) repository, and is maintained by [Liberated Embedded Systems](https://github.com/liberatedsystems). Thanks for all contributions so far!* + # RNode Firmware This is the open firmware that powers RNode devices. @@ -14,14 +16,7 @@ The RNode system is primarily software, which *transforms* different kinds of av ## Latest Release -The latest release, installable through `rnodeconf`, is version `1.68`. This release brings the following changes: - -- Fixed a bug in high-bandwidth optimisation -- Fixed a potential memory overflow during firmware update -- Fixed a bug in LoRa modem status updates -- Updated bootstrap console with latest packages - -You must have at least version `2.1.3` of `rnodeconf` installed to update the RNode Firmware to version `1.67`. Get it by updating the `rns` package to at least version `0.6.4`. +The latest release, installable through `rnodeconf`, is version `1.82`. You must have at least version `2.4.1` of `rnodeconf` installed to update the RNode Firmware to version `1.82`. Get it by updating the `rns` package to at least version `0.9.4`. ## A Self-Replicating System @@ -67,24 +62,29 @@ It's easy to create your own RNodes from one of the supported development boards The RNode Firmware supports the following boards: -- Handheld v2.x RNodes from [unsigned.io](https://unsigned.io/shop/product/handheld-rnode) -- Original v1.x RNodes from [unsigned.io](https://unsigned.io/shop/product/rnode) - LilyGO T-Beam v1.1 devices with SX1276/8 LoRa chips - LilyGO T-Beam v1.1 devices with SX1262/8 LoRa chips +- LilyGO T-Beam Supreme devices +- LilyGO T-Deck devices (currently display is disabled) - LilyGO LoRa32 v1.0 devices - LilyGO LoRa32 v2.0 devices - LilyGO LoRa32 v2.1 devices (with and without TCXO) +- LilyGO T3S3 devices with SX1276/8 LoRa chips +- LilyGO T3S3 devices with SX1262/8 LoRa chips +- LilyGO T3S3 devices with SX1280 LoRa chips +- LilyGO T-Echo devices - Heltec LoRa32 v2 devices - Heltec LoRa32 v3 devices +- Heltec T114 devices +- RAK4631 devices +- SeeedStudio XIAO ESP32S3 devices (with Wio-SX1262) - Homebrew RNodes based on ATmega1284p boards - Homebrew RNodes based on ATmega2560 boards - Homebrew RNodes based on Adafruit Feather ESP32 boards - Homebrew RNodes based on generic ESP32 boards ## Supported Transceiver Modules -The RNode Firmware supports all transceiver modules based on **Semtech SX1276** or **Semtech SX1278** chips, that have an **SPI interface** and expose the **DIO_0** interrupt pin from the chip. - -Support for **SX1262**, **SX1268** and **SX1280** is being implemented. Please support the project with donations if you want this faster! +The RNode Firmware supports all transceiver modules based on Semtech **SX1276**, **SX1278**, **SX1262**, **SX1268** and **SX1280** chips, that have an **SPI interface** and expose the relevant **DIO** interrupt pins from the chip. ## Getting Started Fast You can download and flash the firmware to all the supported boards using the [RNode Config Utility](https://github.com/markqvist/rnodeconfigutil). All firmware releases are now handled and installed directly through the `rnodeconf` utility, which is included in the `rns` package. It can be installed via `pip`: @@ -97,6 +97,8 @@ pip install rns --upgrade rnodeconf --autoinstall ``` +For most of the supported device types, it is also possible to use [Liam Cottle's Web-based RNode Flasher](https://liamcottle.github.io/rnode-flasher/). This option may be easier if you're not familiar with using a command line interface. + For more detailed instruction and in-depth guides, you can have a look at some of these resources: - Create a [basic RNode from readily available development boards](https://unsigned.io/guides/2022_01_25_installing-rnode-firmware-on-supported-devices.html) @@ -104,7 +106,12 @@ For more detailed instruction and in-depth guides, you can have a look at some o - Learn the basics on how to [create and build your own RNode designs](https://unsigned.io/guides/2022_01_26_how-to-make-your-own-rnodes.html) from scratch - Once you've got the hang of it, start building RNodes for your community, or [even for selling them](https://unsigned.io/sell_rnodes.html) -If you would rather just buy a pre-made unit, you can visit [my shop](https://unsigned.io/shop) and purchase my particular version of the [Handheld RNode](https://unsigned.io/shop/handheld-rnode/), which I can assure you is made to the highest quality, and with a lot of care. +If you would rather just buy a pre-made unit, you can visit one of the community vendors that produce and sell RNodes: + +- [Liberated Embedded Systems](https://store.liberatedsystems.co.uk/) +- [Simply Equipped](https://simplyequipped.com/) + +If you'd like to have your shop added to this list, let me know. ## Support RNode Development You can help support the continued development of open, free and private communications systems by donating via one of the following channels: @@ -113,16 +120,17 @@ You can help support the continued development of open, free and private communi ``` 84FpY1QbxHcgdseePYNmhTHcrgMX4nFfBYtz2GKYToqHVVhJp8Eaw1Z1EedRnKD19b3B8NiLCGVxzKV17UMmmeEsCrPyA5w ``` -- Ethereum - ``` - 0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73 - ``` - Bitcoin ``` - 35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH + bc1p4a6axuvl7n9hpapfj8sv5reqj8kz6uxa67d5en70vzrttj0fmcusgxsfk5 + ``` +- Ethereum + ``` + 0xae89F3B94fC4AD6563F0864a55F9a697a90261ff ``` - Ko-Fi: https://ko-fi.com/markqvist + ## License & Use The RNode Firmware is Copyright © 2024 Mark Qvist / [unsigned.io](https://unsigned.io), and is made available under the **GNU General Public License v3.0**. The source code includes an SX1276 driver that is released under MIT License, and Copyright © 2018 Sandeep Mistry / Mark Qvist. diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 4410a91..d2437f1 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -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 @@ -41,6 +41,17 @@ volatile bool serial_buffering = false; #include "Console.h" #endif +#if PLATFORM == PLATFORM_ESP32 || PLATFORM == PLATFORM_NRF52 + #define MODEM_QUEUE_SIZE 4 + typedef struct { + size_t len; + int rssi; + int snr_raw; + uint8_t data[]; + } modem_packet_t; + static xQueueHandle modem_packet_queue = NULL; +#endif + char sbuf[128]; #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 @@ -52,23 +63,55 @@ 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 - if (!eeprom_begin()) { - Serial.write("EEPROM initialisation failed.\r\n"); - } + #if BOARD_MODEL == BOARD_TECHO + delay(200); + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, HIGH); + pinMode(pin_btn_usr1, INPUT_PULLUP); + pinMode(pin_btn_touch, INPUT_PULLUP); + pinMode(PIN_LED_RED, OUTPUT); + pinMode(PIN_LED_GREEN, OUTPUT); + pinMode(PIN_LED_BLUE, OUTPUT); + delay(200); + #endif + + if (!eeprom_begin()) { Serial.write("EEPROM initialisation failed.\r\n"); } #endif // Seed the PRNG for CSMA R-value selection - # if MCU_VARIANT == MCU_ESP32 + #if MCU_VARIANT == MCU_ESP32 // On ESP32, get the seed value from the // hardware RNG - int seed_val = (int)esp_random(); + unsigned long seed_val = (unsigned long)esp_random(); + #elif MCU_VARIANT == MCU_NRF52 + // On nRF, get the seed value from the + // hardware RNG + unsigned long seed_val = get_rng_seed(); #else // Otherwise, get a pseudo-random seed // value from an unconnected analog pin - int seed_val = analogRead(0); + // + // CAUTION! If you are implementing the + // firmware on a platform that does not + // have a hardware RNG, you MUST take + // care to get a seed value with enough + // entropy at each device reset! + unsigned long seed_val = analogRead(0); #endif randomSeed(seed_val); @@ -78,11 +121,19 @@ void setup() { Serial.begin(serial_baudrate); - #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_RNODE_NG_22 - // Some boards need to wait until the hardware UART is set up before booting - // the full firmware. In the case of the RAK4631, the line below will wait - // until a serial connection is actually established with a master. Thus, it - // is disabled on this platform. + #if HAS_NP + led_init(); + #endif + + #if MCU_VARIANT == MCU_NRF52 && HAS_NP == true + boot_seq(); + #endif + + #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TBEAM_S_V1 + // Some boards need to wait until the hardware UART is set up before booting + // the full firmware. In the case of the RAK4631 and Heltec T114, the line below will wait + // until a serial connection is actually established with a master. Thus, it + // is disabled on this platform. while (!Serial); #endif @@ -117,6 +168,10 @@ void setup() { memset(packet_lengths_buf, 0, sizeof(packet_starts_buf)); fifo16_init(&packet_lengths, packet_lengths_buf, CONFIG_QUEUE_MAX_LENGTH); + #if PLATFORM == PLATFORM_ESP32 || PLATFORM == PLATFORM_NRF52 + modem_packet_queue = xQueueCreate(MODEM_QUEUE_SIZE, sizeof(modem_packet_t*)); + #endif + // Set chip select, reset and interrupt // pins for the LoRa module #if MODEM == SX1276 || MODEM == SX1278 @@ -130,24 +185,45 @@ void setup() { #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 init_channel_stats(); + #if BOARD_MODEL == BOARD_T3S3 + #if MODEM == SX1280 + delay(300); + LoRa->reset(); + delay(100); + #endif + #endif + + #if BOARD_MODEL == BOARD_XIAO_S3 + // Improve wakeup from sleep + delay(300); + LoRa->reset(); + delay(100); + #endif + // Check installed transceiver chip and // probe boot parameters. if (LoRa->preInit()) { modem_installed = true; - uint32_t lfr = LoRa->getFrequency(); - if (lfr == 0) { - // Normal boot - } else if (lfr == M_FRQ_R) { - // Quick reboot - #if HAS_CONSOLE - if (rtc_get_reset_reason(0) == POWERON_RESET) { - console_active = true; - } - #endif - } else { - // Unknown boot - } - LoRa->setFrequency(M_FRQ_S); + + #if HAS_INPUT + // Skip quick-reset console activation + #else + uint32_t lfr = LoRa->getFrequency(); + if (lfr == 0) { + // Normal boot + } else if (lfr == M_FRQ_R) { + // Quick reboot + #if HAS_CONSOLE + if (rtc_get_reset_reason(0) == POWERON_RESET) { + console_active = true; + } + #endif + } else { + // Unknown boot + } + LoRa->setFrequency(M_FRQ_S); + #endif + } else { modem_installed = false; } @@ -164,8 +240,17 @@ void setup() { if (eeprom_read(eeprom_addr(ADDR_CONF_DSET)) != CONF_OK_BYTE) { #endif eeprom_update(eeprom_addr(ADDR_CONF_DSET), CONF_OK_BYTE); - eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF); + #if BOARD_MODEL == BOARD_TECHO + eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0x03); + #else + eeprom_update(eeprom_addr(ADDR_CONF_DINT), 0xFF); + #endif } + #if BOARD_MODEL == BOARD_TECHO + display_add_callback(work_while_waiting); + #endif + + display_unblank(); disp_ready = display_init(); update_display(); #endif @@ -191,6 +276,22 @@ void setup() { } #endif + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + #if MODEM == SX1280 + avoid_interference = false; + #else + #if HAS_EEPROM + uint8_t ia_conf = EEPROM.read(eeprom_addr(ADDR_CONF_DIA)); + if (ia_conf == 0x00) { avoid_interference = true; } + else { avoid_interference = false; } + #elif MCU_VARIANT == MCU_NRF52 + uint8_t ia_conf = eeprom_read(eeprom_addr(ADDR_CONF_DIA)); + if (ia_conf == 0x00) { avoid_interference = true; } + else { avoid_interference = false; } + #endif + #endif + #endif + // Validate board health, EEPROM and config validate_status(); @@ -208,26 +309,54 @@ void lora_receive() { inline void kiss_write_packet() { serial_write(FEND); serial_write(CMD_DATA); + 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; + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 packet_ready = false; #endif + + #if MCU_VARIANT == MCU_ESP32 + #if HAS_BLE + bt_flush(); + #endif + #endif } inline void getPacketData(uint16_t len) { - while (len-- && read_len < MTU) { - pbuf[read_len++] = LoRa->read(); - } + #if MCU_VARIANT != MCU_NRF52 + while (len-- && read_len < MTU) { + pbuf[read_len++] = LoRa->read(); + } + #else + BaseType_t int_mask = taskENTER_CRITICAL_FROM_ISR(); + while (len-- && read_len < MTU) { + pbuf[read_len++] = LoRa->read(); + } + taskEXIT_CRITICAL_FROM_ISR(int_mask); + #endif } void ISR_VECT receive_callback(int packet_size) { + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + BaseType_t int_mask; + #endif + if (!promisc) { // The standard operating mode allows large // packets with a payload up to 500 bytes, @@ -242,7 +371,12 @@ void ISR_VECT receive_callback(int packet_size) { // 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; + #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 = sequence; #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 @@ -256,15 +390,12 @@ void ISR_VECT receive_callback(int packet_size) { // This is the second part of a split // packet, so we add it to the buffer // and set the ready flag. - - #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 last_rssi = (last_rssi+LoRa->packetRssi())/2; last_snr_raw = (last_snr_raw+LoRa->packetSnrRaw())/2; #endif getPacketData(packet_size); - seq = SEQ_UNSET; ready = true; @@ -273,7 +404,11 @@ void ISR_VECT receive_callback(int packet_size) { // same sequence id, so we must assume // that we are seeing the first part of // a new split packet. - read_len = 0; + #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 = sequence; #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 @@ -291,7 +426,11 @@ void ISR_VECT receive_callback(int packet_size) { if (seq != SEQ_UNSET) { // If we already had part of a split // packet in the buffer, we clear it. - read_len = 0; + #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 = SEQ_UNSET; } @@ -313,8 +452,27 @@ void ISR_VECT receive_callback(int packet_size) { // And then write the entire packet kiss_write_packet(); + #else - packet_ready = true; + // 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; } + + // Get packet RSSI and SNR + #if MCU_VARIANT == MCU_ESP32 + modem_packet->snr_raw = LoRa->packetSnrRaw(); + modem_packet->rssi = LoRa->packetRssi(modem_packet->snr_raw); + #endif + + // 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); + } #endif } } else { @@ -366,9 +524,7 @@ bool startRadio() { getFrequency(); LoRa->enableCrc(); - LoRa->onReceive(receive_callback); - lora_receive(); // Flash an info pattern to indicate @@ -408,17 +564,13 @@ void update_radio_lock() { } } -bool queueFull() { - return (queue_height >= CONFIG_QUEUE_MAX_LENGTH || queued_bytes >= CONFIG_QUEUE_SIZE); -} +bool queue_full() { return (queue_height >= CONFIG_QUEUE_MAX_LENGTH || queued_bytes >= CONFIG_QUEUE_SIZE); } volatile bool queue_flushing = false; -void flushQueue(void) { +void flush_queue(void) { if (!queue_flushing) { queue_flushing = true; - - led_tx_on(); - uint16_t processed = 0; + led_tx_on(); uint16_t processed = 0; #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 while (!fifo16_isempty(&packet_starts)) { @@ -435,36 +587,102 @@ void flushQueue(void) { tbuf[i] = packet_queue[pos]; } - transmit(length); - processed++; + transmit(length); processed++; } } - lora_receive(); - led_tx_off(); - post_tx_yield_timeout = millis()+(lora_post_tx_yield_slots*csma_slot_ms); + lora_receive(); led_tx_off(); } queue_height = 0; queued_bytes = 0; + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 update_airtime(); #endif + queue_flushing = false; + + #if HAS_DISPLAY + display_tx = true; + #endif +} + +void pop_queue() { + if (!queue_flushing) { + queue_flushing = true; + led_tx_on(); uint16_t processed = 0; + + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + if (!fifo16_isempty(&packet_starts)) { + #else + if (!fifo16_isempty_locked(&packet_starts)) { + #endif + + uint16_t start = fifo16_pop(&packet_starts); + uint16_t length = fifo16_pop(&packet_lengths); + if (length >= MIN_L && length <= MTU) { + for (uint16_t i = 0; i < length; i++) { + uint16_t pos = (start+i)%CONFIG_QUEUE_SIZE; + tbuf[i] = packet_queue[pos]; + } + + transmit(length); processed++; + } + queue_height -= processed; + queued_bytes -= length; + } + + lora_receive(); led_tx_off(); + } + + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + update_airtime(); + #endif + + queue_flushing = false; + + #if HAS_DISPLAY + display_tx = true; + #endif } -#define PHY_HEADER_LORA_SYMBOLS 8 void add_airtime(uint16_t written) { #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + float lora_symbols = 0; float packet_cost_ms = 0.0; - float payload_cost_ms = ((float)written * lora_us_per_byte)/1000.0; - packet_cost_ms += payload_cost_ms; - packet_cost_ms += (lora_preamble_symbols+4.25)*lora_symbol_time_ms; - packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * lora_symbol_time_ms; + int ldr_opt = 0; if (lora_low_datarate) ldr_opt = 1; + + #if MODEM == SX1276 || MODEM == SX1278 + lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*lora_sf + 8 + PHY_HEADER_LORA_SYMBOLS); + lora_symbols /= 4*(lora_sf-2*ldr_opt); + lora_symbols *= lora_cr; + lora_symbols += lora_preamble_symbols + 0.25 + 8; + packet_cost_ms += lora_symbols * lora_symbol_time_ms; + + #elif MODEM == SX1262 || MODEM == SX1280 + if (lora_sf < 7) { + lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*lora_sf + PHY_HEADER_LORA_SYMBOLS); + lora_symbols /= 4*lora_sf; + lora_symbols *= lora_cr; + lora_symbols += lora_preamble_symbols + 2.25 + 8; + packet_cost_ms += lora_symbols * lora_symbol_time_ms; + + } else { + lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*lora_sf + 8 + PHY_HEADER_LORA_SYMBOLS); + lora_symbols /= 4*(lora_sf-2*ldr_opt); + lora_symbols *= lora_cr; + lora_symbols += lora_preamble_symbols + 0.25 + 8; + packet_cost_ms += lora_symbols * lora_symbol_time_ms; + } + + #endif + uint16_t cb = current_airtime_bin(); uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } airtime_bins[cb] += packet_cost_ms; airtime_bins[nb] = 0; + #endif } @@ -473,24 +691,20 @@ void update_airtime() { uint16_t cb = current_airtime_bin(); uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; } uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } - airtime_bins[nb] = 0; - airtime = (float)(airtime_bins[cb]+airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); + airtime_bins[nb] = 0; airtime = (float)(airtime_bins[cb]+airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); uint32_t longterm_airtime_sum = 0; - for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { - longterm_airtime_sum += airtime_bins[bin]; - } + for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { longterm_airtime_sum += airtime_bins[bin]; } longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS; float longterm_channel_util_sum = 0.0; - for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { - longterm_channel_util_sum += longterm_bins[bin]; - } + for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { longterm_channel_util_sum += longterm_bins[bin]; } longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS; #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 - update_csma_p(); + update_csma_parameters(); #endif + kiss_indicate_channel_stats(); #endif } @@ -500,62 +714,51 @@ void transmit(uint16_t size) { if (!promisc) { uint16_t written = 0; uint8_t header = random(256) & 0xF0; - - if (size > SINGLE_MTU - HEADER_L) { - header = header | FLAG_SPLIT; - } + if (size > SINGLE_MTU - HEADER_L) { header = header | FLAG_SPLIT; } LoRa->beginPacket(); LoRa->write(header); written++; for (uint16_t i=0; i < size; i++) { - LoRa->write(tbuf[i]); + LoRa->write(tbuf[i]); written++; - written++; + if (written == 255 && isSplitPacket(header)) { + if (!LoRa->endPacket()) { + kiss_indicate_error(ERROR_MODEM_TIMEOUT); + kiss_indicate_error(ERROR_TXFAILED); + led_indicate_error(5); + hard_reset(); + } - if (written == 255) { - LoRa->endPacket(); add_airtime(written); + add_airtime(written); LoRa->beginPacket(); LoRa->write(header); written = 1; } } - LoRa->endPacket(); add_airtime(written); + if (!LoRa->endPacket()) { + kiss_indicate_error(ERROR_MODEM_TIMEOUT); + kiss_indicate_error(ERROR_TXFAILED); + led_indicate_error(5); + hard_reset(); + } + + add_airtime(written); + } else { - // In promiscuous mode, we only send out - // plain raw LoRa packets with a maximum - // payload of 255 bytes - led_tx_on(); - uint16_t written = 0; - - // Cap packets at 255 bytes - if (size > SINGLE_MTU) { - size = SINGLE_MTU; - } - - // If implicit header mode has been set, - // set packet length to payload data length - if (!implicit) { - LoRa->beginPacket(); - } else { - LoRa->beginPacket(size); - } - - for (uint16_t i=0; i < size; i++) { - LoRa->write(tbuf[i]); - - written++; - } + led_tx_on(); uint16_t written = 0; + if (size > SINGLE_MTU) { size = SINGLE_MTU; } + if (!implicit) { LoRa->beginPacket(); } + else { LoRa->beginPacket(size); } + for (uint16_t i=0; i < size; i++) { LoRa->write(tbuf[i]); written++; } LoRa->endPacket(); add_airtime(written); } - } else { - kiss_indicate_error(ERROR_TXFAILED); - led_indicate_error(5); - } + + } else { kiss_indicate_error(ERROR_TXFAILED); led_indicate_error(5); } } -void serialCallback(uint8_t sbyte) { +void serial_callback(uint8_t sbyte) { if (IN_FRAME && sbyte == FEND && command == CMD_DATA) { IN_FRAME = false; @@ -564,21 +767,15 @@ void serialCallback(uint8_t sbyte) { int16_t e = queue_cursor-1; if (e == -1) e = CONFIG_QUEUE_SIZE-1; uint16_t l; - if (s != e) { - l = (s < e) ? e - s + 1 : CONFIG_QUEUE_SIZE - s + e + 1; - } else { - l = 1; - } + if (s != e) { l = (s < e) ? e - s + 1 : CONFIG_QUEUE_SIZE - s + e + 1; } + else { l = 1; } if (l >= MIN_L) { queue_height++; - fifo16_push(&packet_starts, s); fifo16_push(&packet_lengths, l); - current_packet_start = queue_cursor; } - } } else if (sbyte == FEND) { @@ -590,7 +787,9 @@ void serialCallback(uint8_t sbyte) { if (frame_len == 0 && command == CMD_UNKNOWN) { command = sbyte; } else if (command == CMD_DATA) { - if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED; + if (bt_state != BT_STATE_CONNECTED) { + cable_state = CABLE_STATE_CONNECTED; + } if (sbyte == FESC) { ESCAPE = true; } else { @@ -658,6 +857,12 @@ void serialCallback(uint8_t sbyte) { int txp = sbyte; #if MODEM == SX1262 if (txp > 22) txp = 22; + #elif MODEM == SX1280 + #if HAS_PA + if (txp > 20) txp = 20; + #else + if (txp > 13) txp = 13; + #endif #else if (txp > 17) txp = 17; #endif @@ -695,6 +900,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; @@ -702,7 +908,10 @@ void serialCallback(uint8_t sbyte) { last_snr_raw = 0x80; } } else if (command == CMD_RADIO_STATE) { - if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED; + if (bt_state != BT_STATE_CONNECTED) { + cable_state = CABLE_STATE_CONNECTED; + display_unblank(); + } if (sbyte == 0xFF) { kiss_indicate_radiostate(); } else if (sbyte == 0x00) { @@ -784,7 +993,7 @@ void serialCallback(uint8_t sbyte) { } kiss_indicate_promisc(); } else if (command == CMD_READY) { - if (!queueFull()) { + if (!queue_full()) { kiss_indicate_ready(); } else { kiss_indicate_not_ready(); @@ -858,9 +1067,9 @@ void serialCallback(uint8_t sbyte) { } #endif } else if (command == CMD_FB_READ) { - if (sbyte != 0x00) { - kiss_indicate_fb(); - } + if (sbyte != 0x00) { kiss_indicate_fb(); } + } else if (command == CMD_DISP_READ) { + if (sbyte != 0x00) { kiss_indicate_disp(); } } else if (command == CMD_DEV_HASH) { #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 if (sbyte != 0x00) { @@ -930,9 +1139,19 @@ 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_BT_UNPAIR) { + #if HAS_BLE + if (sbyte == 0x01) { bt_debond_all(); } + #endif } else if (command == CMD_DISP_INT) { #if HAS_DISPLAY if (sbyte == FESC) { @@ -945,8 +1164,8 @@ void serialCallback(uint8_t sbyte) { } display_intensity = sbyte; di_conf_save(display_intensity); + display_unblank(); } - #endif } else if (command == CMD_DISP_ADDR) { #if HAS_DISPLAY @@ -962,6 +1181,74 @@ 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_DISP_ROT) { + #if HAS_DISPLAY + if (sbyte == FESC) { + ESCAPE = true; + } else { + if (ESCAPE) { + if (sbyte == TFEND) sbyte = FEND; + if (sbyte == TFESC) sbyte = FESC; + ESCAPE = false; + } + drot_conf_save(sbyte); + display_unblank(); + } + #endif + } else if (command == CMD_DIS_IA) { + if (sbyte == FESC) { + ESCAPE = true; + } else { + if (ESCAPE) { + if (sbyte == TFEND) sbyte = FEND; + if (sbyte == TFESC) sbyte = FESC; + ESCAPE = false; + } + dia_conf_save(sbyte); + } + } else if (command == CMD_DISP_RCND) { + #if HAS_DISPLAY + if (sbyte == FESC) { + ESCAPE = true; + } else { + if (ESCAPE) { + if (sbyte == TFEND) sbyte = FEND; + if (sbyte == TFESC) sbyte = FESC; + ESCAPE = false; + } + if (sbyte > 0x00) recondition_display = true; + } + #endif + } else if (command == CMD_NP_INT) { + #if HAS_NP + if (sbyte == FESC) { + ESCAPE = true; + } else { + if (ESCAPE) { + if (sbyte == TFEND) sbyte = FEND; + if (sbyte == TFESC) sbyte = FESC; + ESCAPE = false; + } + sbyte; + led_set_intensity(sbyte); + np_int_conf_save(sbyte); + } + #endif } } @@ -971,14 +1258,46 @@ void serialCallback(uint8_t sbyte) { portMUX_TYPE update_lock = portMUX_INITIALIZER_UNLOCKED; #endif -void updateModemStatus() { +bool medium_free() { + update_modem_status(); + if (avoid_interference && interference_detected) { return false; } + return !dcd; +} + +bool noise_floor_sampled = false; +int noise_floor_sample = 0; +int noise_floor_buffer[NOISE_FLOOR_SAMPLES] = {0}; +void update_noise_floor() { + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + if (!dcd) { + if (!noise_floor_sampled || current_rssi < noise_floor + CSMA_INFR_THRESHOLD_DB) { + noise_floor_buffer[noise_floor_sample] = current_rssi; + noise_floor_sample = noise_floor_sample+1; + if (noise_floor_sample >= NOISE_FLOOR_SAMPLES) { + noise_floor_sample %= NOISE_FLOOR_SAMPLES; + noise_floor_sampled = true; + } + + if (noise_floor_sampled) { + noise_floor = 0; + for (int ni = 0; ni < NOISE_FLOOR_SAMPLES; ni++) { noise_floor += noise_floor_buffer[ni]; } + noise_floor /= NOISE_FLOOR_SAMPLES; + } + } + } + #endif +} + +#define LED_ID_TRIG 16 +uint8_t led_id_filter = 0; +void update_modem_status() { #if MCU_VARIANT == MCU_ESP32 portENTER_CRITICAL(&update_lock); #elif MCU_VARIANT == MCU_NRF52 portENTER_CRITICAL(); #endif - uint8_t status = LoRa->modemStatus(); + bool carrier_detected = LoRa->dcd(); current_rssi = LoRa->currentRssi(); last_status_update = millis(); @@ -988,52 +1307,28 @@ void updateModemStatus() { portEXIT_CRITICAL(); #endif - if ((status & SIG_DETECT) == SIG_DETECT) { stat_signal_detected = true; } else { stat_signal_detected = false; } - if ((status & SIG_SYNCED) == SIG_SYNCED) { stat_signal_synced = true; } else { stat_signal_synced = false; } - if ((status & RX_ONGOING) == RX_ONGOING) { stat_rx_ongoing = true; } else { stat_rx_ongoing = false; } + interference_detected = !carrier_detected && (current_rssi > (noise_floor+CSMA_INFR_THRESHOLD_DB)); + if (interference_detected) { if (led_id_filter < LED_ID_TRIG) { led_id_filter += 1; } } + else { if (led_id_filter > 0) {led_id_filter -= 1; } } - // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) { - if (stat_signal_detected || stat_signal_synced) { - if (stat_rx_ongoing) { - if (dcd_count < dcd_threshold) { - dcd_count++; - } else { - last_dcd = last_status_update; - dcd_led = true; - dcd = true; - } - } - } else { - #define DCD_LED_STEP_D 3 - if (dcd_count == 0) { - dcd_led = false; - } else if (dcd_count > DCD_LED_STEP_D) { - dcd_count -= DCD_LED_STEP_D; + if (carrier_detected) { dcd = true; } else { dcd = false; } + + dcd_led = dcd; + if (dcd_led) { led_rx_on(); } + else { + if (interference_detected) { + if (led_id_filter >= LED_ID_TRIG && noise_floor_sampled) { led_id_on(); } } else { - dcd_count = 0; - } - - if (last_status_update > last_dcd+csma_slot_ms) { - dcd = false; - dcd_led = false; - dcd_count = 0; - } - } - - if (dcd_led) { - led_rx_on(); - } else { - if (airtime_lock) { - led_indicate_airtime_lock(); - } else { - led_rx_off(); + if (airtime_lock) { led_indicate_airtime_lock(); } + else { led_rx_off(); led_id_off(); } } } } -void checkModemStatus() { +void check_modem_status() { if (millis()-last_status_update >= status_interval_ms) { - updateModemStatus(); + update_modem_status(); + update_noise_floor(); #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 util_samples[dcd_sample] = dcd; @@ -1130,7 +1425,7 @@ void validate_status() { #endif } 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; @@ -1146,6 +1441,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; @@ -1155,6 +1451,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; @@ -1164,6 +1461,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; @@ -1185,22 +1483,70 @@ void validate_status() { } #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 - #define _e 2.71828183 - #define _S 10.0 - float csma_slope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); } - void update_csma_p() { - csma_p = (uint8_t)((1.0-(csma_p_min+(csma_p_max-csma_p_min)*csma_slope(airtime)))*255.0); -} + void update_csma_parameters() { + int airtime_pct = (int)(airtime*100); + int new_cw_band = cw_band; + + if (airtime_pct <= CSMA_BAND_1_MAX_AIRTIME) { new_cw_band = 1; } + else { + int at = airtime_pct + CSMA_BAND_1_MAX_AIRTIME; + new_cw_band = map(at, CSMA_BAND_1_MAX_AIRTIME, CSMA_BAND_N_MIN_AIRTIME, 2, CSMA_CW_BANDS); + } + + if (new_cw_band > CSMA_CW_BANDS) { new_cw_band = CSMA_CW_BANDS; } + if (new_cw_band != cw_band) { + cw_band = (uint8_t)(new_cw_band); + cw_min = (cw_band-1) * CSMA_CW_PER_BAND_WINDOWS; + cw_max = (cw_band) * CSMA_CW_PER_BAND_WINDOWS - 1; + kiss_indicate_csma_stats(); + } + } #endif +void tx_queue_handler() { + if (!airtime_lock && queue_height > 0) { + if (csma_cw == -1) { + csma_cw = random(cw_min, cw_max); + cw_wait_target = csma_cw * csma_slot_ms; + } + + if (difs_wait_start == -1) { // DIFS wait not yet started + if (medium_free()) { difs_wait_start = millis(); return; } // Set DIFS wait start time + else { return; } } // Medium not yet free, continue waiting + + else { // We are waiting for DIFS or CW to pass + if (!medium_free()) { difs_wait_start = -1; cw_wait_start = -1; return; } // Medium became occupied while in DIFS wait, restart waiting when free again + else { // Medium is free, so continue waiting + if (millis() < difs_wait_start+difs_ms) { return; } // DIFS has not yet passed, continue waiting + else { // DIFS has passed, and we are now in CW wait + if (cw_wait_start == -1) { cw_wait_start = millis(); return; } // If we haven't started counting CW wait time, do it from now + else { // If we are already counting CW wait time, add it to the counter + cw_wait_passed += millis()-cw_wait_start; cw_wait_start = millis(); + if (cw_wait_passed < cw_wait_target) { return; } // Contention window wait time has not yet passed, continue waiting + else { // Wait time has passed, flush the queue + if (!lora_limit_rate) { flush_queue(); } else { pop_queue(); } + cw_wait_passed = 0; csma_cw = -1; difs_wait_start = -1; } + } + } + } + } + } +} + +void work_while_waiting() { loop(); } + void loop() { if (radio_online) { #if MCU_VARIANT == MCU_ESP32 - if (packet_ready) { - portENTER_CRITICAL(&update_lock); - last_rssi = LoRa->packetRssi(); - last_snr_raw = LoRa->packetSnrRaw(); - portEXIT_CRITICAL(&update_lock); + 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; + 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(); @@ -1211,7 +1557,13 @@ void loop() { if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true; #elif MCU_VARIANT == MCU_NRF52 - if (packet_ready) { + 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; + free(modem_packet); + modem_packet = NULL; + portENTER_CRITICAL(); last_rssi = LoRa->packetRssi(); last_snr_raw = LoRa->packetSnrRaw(); @@ -1224,51 +1576,11 @@ void loop() { airtime_lock = false; if (st_airtime_limit != 0.0 && airtime >= st_airtime_limit) airtime_lock = true; if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true; + #endif - checkModemStatus(); - if (!airtime_lock) { - if (queue_height > 0) { - #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 - long check_time = millis(); - if (check_time > post_tx_yield_timeout) { - if (dcd_waiting && (check_time >= dcd_wait_until)) { dcd_waiting = false; } - if (!dcd_waiting) { - for (uint8_t dcd_i = 0; dcd_i < dcd_threshold*2; dcd_i++) { - delay(STATUS_INTERVAL_MS); updateModemStatus(); - } - - if (!dcd) { - uint8_t csma_r = (uint8_t)random(256); - if (csma_p >= csma_r) { - flushQueue(); - } else { - dcd_waiting = true; - dcd_wait_until = millis()+csma_slot_ms; - } - } - } - } - - #else - if (!dcd_waiting) updateModemStatus(); - - if (!dcd && !dcd_led) { - if (dcd_waiting) delay(lora_rx_turnaround_ms); - - updateModemStatus(); - - if (!dcd) { - dcd_waiting = false; - flushQueue(); - } - - } else { - dcd_waiting = true; - } - #endif - } - } + tx_queue_handler(); + check_modem_status(); } else { if (hw_ready) { @@ -1294,7 +1606,7 @@ void loop() { #endif #if HAS_DISPLAY - if (disp_ready) update_display(); + if (disp_ready && !display_updating) update_display(); #endif #if HAS_PMU @@ -1308,27 +1620,98 @@ 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 sleep_now() { #if HAS_SLEEP == true - #if BOARD_MODEL == BOARD_RNODE_NG_22 - display_intensity = 0; - update_display(true); + stopRadio(); // TODO: Check this on all platforms + #if PLATFORM == PLATFORM_ESP32 + #if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_XIAO_S3 + #if HAS_DISPLAY + display_intensity = 0; + update_display(true); + #endif + #endif + #if PIN_DISP_SLEEP >= 0 + pinMode(PIN_DISP_SLEEP, OUTPUT); + digitalWrite(PIN_DISP_SLEEP, DISP_SLEEP_LEVEL); + #endif + #if HAS_BLUETOOTH + if (bt_state == BT_STATE_CONNECTED) { + bt_stop(); + delay(100); + } + #endif + esp_sleep_enable_ext0_wakeup(PIN_WAKEUP, WAKEUP_LEVEL); + esp_deep_sleep_start(); + #elif PLATFORM == PLATFORM_NRF52 + #if BOARD_MODEL == BOARD_HELTEC_T114 + npset(0,0,0); + digitalWrite(PIN_VEXT_EN, LOW); + digitalWrite(PIN_T114_TFT_BLGT, HIGH); + digitalWrite(PIN_T114_TFT_EN, HIGH); + #elif BOARD_MODEL == BOARD_TECHO + for (uint8_t i = display_intensity; i > 0; i--) { analogWrite(pin_backlight, i-1); delay(1); } + epd_black(true); delay(300); epd_black(true); delay(300); epd_black(false); + delay(2000); + analogWrite(PIN_VEXT_EN, 0); + delay(100); + #endif + sd_power_gpregret_set(0, 0x6d); + nrf_gpio_cfg_sense_input(pin_btn_usr1, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); + NRF_POWER->SYSTEMOFF = 1; #endif - #if PIN_DISP_SLEEP >= 0 - pinMode(PIN_DISP_SLEEP, OUTPUT); - digitalWrite(PIN_DISP_SLEEP, DISP_SLEEP_LEVEL); - #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 MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + 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 + } + } + #endif } volatile bool serial_polling = false; @@ -1341,7 +1724,7 @@ void serial_poll() { while (!fifo_isempty(&serialFIFO)) { #endif char sbyte = fifo_pop(&serialFIFO); - serialCallback(sbyte); + serial_callback(sbyte); } serial_polling = false; @@ -1403,7 +1786,6 @@ void serial_interrupt_init() { // Buffer incoming frames every 1ms ICR3 = 16000; - TIMSK3 = _BV(ICIE3); #elif MCU_VARIANT == MCU_2560 @@ -1417,7 +1799,6 @@ void serial_interrupt_init() { // Buffer incoming frames every 1ms ICR3 = 16000; - TIMSK3 = _BV(ICIE3); #elif MCU_VARIANT == MCU_ESP32 @@ -1427,7 +1808,5 @@ void serial_interrupt_init() { } #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560 - ISR(TIMER3_CAPT_vect) { - buffer_serial(); - } + ISR(TIMER3_CAPT_vect) { buffer_serial(); } #endif diff --git a/ROM.h b/ROM.h index 7a343c0..27c38bb 100644 --- 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 @@ -14,71 +14,42 @@ // along with this program. If not, see . #ifndef ROM_H - #define ROM_H + #define ROM_H + #define CHECKSUMMED_SIZE 0x0B - #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 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 MODEL_11 0x11 - #define MODEL_12 0x12 - #define MODEL_A1 0xA1 - #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_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_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_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_DROT 0xB8 + #define ADDR_CONF_PSET 0xB5 + #define ADDR_CONF_PINT 0xB6 + #define ADDR_CONF_BSET 0xB7 + #define ADDR_CONF_DIA 0xB9 - #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 INFO_LOCK_BYTE 0x73 - #define CONF_OK_BYTE 0x73 - #define BT_ENABLE_BYTE 0x73 - - #define EEPROM_RESERVED 200 + #define EEPROM_RESERVED 200 + ////////////////////////////////// #endif diff --git a/Release/console_image.bin b/Release/console_image.bin index ee7e5b1..264d234 100644 Binary files a/Release/console_image.bin and b/Release/console_image.bin differ diff --git a/ST7789.h b/ST7789.h new file mode 100644 index 0000000..85012e8 --- /dev/null +++ b/ST7789.h @@ -0,0 +1,440 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2024 by Heltec AutoMation + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef ST7789Spi_h +#define ST7789Spi_h + +#include "OLEDDisplay.h" +#include + + +#define ST_CMD_DELAY 0x80 // special signifier for command lists + +#define ST77XX_NOP 0x00 +#define ST77XX_SWRESET 0x01 +#define ST77XX_RDDID 0x04 +#define ST77XX_RDDST 0x09 + +#define ST77XX_SLPIN 0x10 +#define ST77XX_SLPOUT 0x11 +#define ST77XX_PTLON 0x12 +#define ST77XX_NORON 0x13 + +#define ST77XX_INVOFF 0x20 +#define ST77XX_INVON 0x21 +#define ST77XX_DISPOFF 0x28 +#define ST77XX_DISPON 0x29 +#define ST77XX_CASET 0x2A +#define ST77XX_RASET 0x2B +#define ST77XX_RAMWR 0x2C +#define ST77XX_RAMRD 0x2E + +#define ST77XX_PTLAR 0x30 +#define ST77XX_TEOFF 0x34 +#define ST77XX_TEON 0x35 +#define ST77XX_MADCTL 0x36 +#define ST77XX_COLMOD 0x3A + +#define ST77XX_MADCTL_MY 0x80 +#define ST77XX_MADCTL_MX 0x40 +#define ST77XX_MADCTL_MV 0x20 +#define ST77XX_MADCTL_ML 0x10 +#define ST77XX_MADCTL_RGB 0x00 + +#define ST77XX_RDID1 0xDA +#define ST77XX_RDID2 0xDB +#define ST77XX_RDID3 0xDC +#define ST77XX_RDID4 0xDD + +// Some ready-made 16-bit ('565') color settings: +#define ST77XX_BLACK 0x0000 +#define ST77XX_WHITE 0xFFFF +#define ST77XX_RED 0xF800 +#define ST77XX_GREEN 0x07E0 +#define ST77XX_BLUE 0x001F +#define ST77XX_CYAN 0x07FF +#define ST77XX_MAGENTA 0xF81F +#define ST77XX_YELLOW 0xFFE0 +#define ST77XX_ORANGE 0xFC00 + +#define LED_A_ON LOW + +#ifdef ESP_PLATFORM +#undef LED_A_ON +#define LED_A_ON HIGH +#define rtos_free free +#define rtos_malloc malloc +//SPIClass SPI1(HSPI); +#endif +class ST7789Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + uint8_t _ledA; + int _miso; + int _mosi; + int _clk; + SPIClass * _spi; + SPISettings _spiSettings; + uint16_t _RGB=0xFFFF; + uint8_t _buffheight; + public: + /* pass _cs as -1 to indicate "do not use CS pin", for cases where it is hard wired low */ + ST7789Spi(SPIClass *spiClass,uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_RAWMODE,uint16_t width=240,uint16_t height=320,int mosi=-1,int miso=-1,int clk=-1) { + this->_spi = spiClass; + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + this->_mosi=mosi; + this->_miso=miso; + this->_clk=clk; + //this->_ledA = _ledA; + _spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); + setGeometry(g,width,height); + } + + bool connect(){ + this->_buffheight=displayHeight / 8; + this->_buffheight+=displayHeight % 8 ? 1:0; + pinMode(_cs, OUTPUT); + pinMode(_dc, OUTPUT); + //pinMode(_ledA, OUTPUT); + if (_cs != (uint8_t) -1) { + pinMode(_cs, OUTPUT); + } + pinMode(_rst, OUTPUT); + +#ifdef ESP_PLATFORM + _spi->begin(_clk,_miso,_mosi,-1); +#else + _spi->begin(); +#endif + _spi->setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + _spi->begin (); + //digitalWrite(_ledA, LED_A_ON); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + + uint16_t minBoundY = UINT16_MAX; + uint16_t maxBoundY = 0; + + uint16_t minBoundX = UINT16_MAX; + uint16_t maxBoundX = 0; + + uint16_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < _buffheight; y++) { + for (x = 0; x < displayWidth; x++) { + //Serial.printf("x %d y %d\r\n",x,y); + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = min(minBoundY, y); + maxBoundY = max(maxBoundY, y); + minBoundX = min(minBoundX, x); + maxBoundX = max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT16_MAX) return; + + set_CS(LOW); + _spi->beginTransaction(_spiSettings); + + for (y = minBoundY; y <= maxBoundY; y++) + { + for(int temp = 0; temp<8;temp++) + { + //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); + setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); + //setAddrWindow(y*8+temp,minBoundX,1,maxBoundX-minBoundX+1); + uint32_t const pixbufcount = maxBoundX-minBoundX+1; + uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); + for (x = minBoundX; x <= maxBoundX; x++) + { + pixbuf[x-minBoundX] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; + } +#ifdef ESP_PLATFORM + _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); +#else + _spi->transfer(pixbuf, NULL, 2 * pixbufcount); +#endif + rtos_free(pixbuf); + } + } + _spi->endTransaction(); + set_CS(HIGH); + + #else + set_CS(LOW); + _spi->beginTransaction(_spiSettings); + uint8_t x, y; + for (y = 0; y < _buffheight; y++) + { + for(int temp = 0; temp<8;temp++) + { + //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); + //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); + setAddrWindow(y*8+temp,0,1,displayWidth); + uint32_t const pixbufcount = displayWidth; + uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); + for (x = 0; x < displayWidth; x++) + { + pixbuf[x] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; + } +#ifdef ESP_PLATFORM + _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); +#else + _spi->transfer(pixbuf, NULL, 2 * pixbufcount); +#endif + rtos_free(pixbuf); + } + } + _spi->endTransaction(); + set_CS(HIGH); + + #endif + } + + virtual void resetOrientation() { + uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; + sendCommand(ST77XX_MADCTL); + WriteData(madctl); + delay(10); + } + + virtual void flipScreenVertically() { + uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; + sendCommand(ST77XX_MADCTL); + WriteData(madctl); + delay(10); + } + + virtual void mirrorScreen() { + uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX|ST77XX_MADCTL_MY; + sendCommand(ST77XX_MADCTL); + WriteData(madctl); + delay(10); + } + + virtual void setRotation(uint8_t r) { + uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; + if (r == 1) { madctl = 0xC0; } + if (r == 2) { madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; } + if (r == 3) { madctl = 0x00; } + sendCommand(ST77XX_MADCTL); + WriteData(madctl); + delay(10); + } + + void setRGB(uint16_t c) + { + + this->_RGB=0x00|c>>8|c<<8&0xFF00; + } + + void displayOn(void) { + //sendCommand(DISPLAYON); + } + + void displayOff(void) { + //sendCommand(DISPLAYOFF); + } + +//#define ST77XX_MADCTL_MY 0x80 +//#define ST77XX_MADCTL_MX 0x40 +//#define ST77XX_MADCTL_MV 0x20 +//#define ST77XX_MADCTL_ML 0x10 + protected: + // Send all the init commands + virtual void sendInitCommands() + { + sendCommand(ST77XX_SWRESET); // 1: Software reset, no args, w/delay + delay(150); + + sendCommand(ST77XX_SLPOUT); // 2: Out of sleep mode, no args, w/delay + delay(10); + + sendCommand(ST77XX_COLMOD); // 3: Set color mode, 16-bit color + WriteData(0x55); + delay(10); + + sendCommand(ST77XX_MADCTL); // 4: Mem access ctrl (directions), Row/col addr, bottom-top refresh + WriteData(0x08); + + sendCommand(ST77XX_CASET); // 5: Column addr set, + WriteData(0x00); + WriteData(0x00); // XSTART = 0 + WriteData(0x00); + WriteData(240); // XEND = 240 + + sendCommand(ST77XX_RASET); // 6: Row addr set, + WriteData(0x00); + WriteData(0x00); // YSTART = 0 + WriteData(320>>8); + WriteData(320&0xFF); // YSTART = 320 + + sendCommand(ST77XX_SLPOUT); // 7: hack + delay(10); + + sendCommand(ST77XX_NORON); // 8: Normal display on, no args, w/delay + delay(10); + + sendCommand(ST77XX_DISPON); // 9: Main screen turn on, no args, delay + delay(10); + + sendCommand(ST77XX_INVON); // 10: invert + delay(10); + + //uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MX; + uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; + sendCommand(ST77XX_MADCTL); + WriteData(madctl); + delay(10); + setRGB(ST77XX_GREEN); + } + + + private: + + void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + x += (320-displayWidth)/2; + y += (240-displayHeight)/2; + uint32_t xa = ((uint32_t)x << 16) | (x + w - 1); + uint32_t ya = ((uint32_t)y << 16) | (y + h - 1); + + writeCommand(ST77XX_CASET); // Column addr set + SPI_WRITE32(xa); + + writeCommand(ST77XX_RASET); // Row addr set + SPI_WRITE32(ya); + + writeCommand(ST77XX_RAMWR); // write to RAM + } + int getBufferOffset(void) { + return 0; + } + inline void set_CS(bool level) { + if (_cs != (uint8_t) -1) { + digitalWrite(_cs, level); + } + }; + inline void sendCommand(uint8_t com) __attribute__((always_inline)){ + set_CS(HIGH); + digitalWrite(_dc, LOW); + set_CS(LOW); + _spi->beginTransaction(_spiSettings); + _spi->transfer(com); + _spi->endTransaction(); + set_CS(HIGH); + digitalWrite(_dc, HIGH); + } + + inline void WriteData(uint8_t data) __attribute__((always_inline)){ + digitalWrite(_cs, LOW); + _spi->beginTransaction(_spiSettings); + _spi->transfer(data); + _spi->endTransaction(); + digitalWrite(_cs, HIGH); + } + void SPI_WRITE32(uint32_t l) + { + _spi->transfer(l >> 24); + _spi->transfer(l >> 16); + _spi->transfer(l >> 8); + _spi->transfer(l); + } + void writeCommand(uint8_t cmd) { + digitalWrite(_dc, LOW); + _spi->transfer(cmd); + digitalWrite(_dc, HIGH); + } + +// Private functions + void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { + this->geometry = g; + + switch (g) { + case GEOMETRY_128_128: + this->displayWidth = 128; + this->displayHeight = 128; + break; + case GEOMETRY_128_64: + this->displayWidth = 128; + this->displayHeight = 64; + break; + case GEOMETRY_128_32: + this->displayWidth = 128; + this->displayHeight = 32; + break; + case GEOMETRY_64_48: + this->displayWidth = 64; + this->displayHeight = 48; + break; + case GEOMETRY_64_32: + this->displayWidth = 64; + this->displayHeight = 32; + break; + case GEOMETRY_RAWMODE: + this->displayWidth = width > 0 ? width : 128; + this->displayHeight = height > 0 ? height : 64; + break; + } + uint8_t tmp=displayHeight % 8; + uint8_t _buffheight=displayHeight / 8; + + if(tmp!=0) + _buffheight++; + this->displayBufferSize = displayWidth * _buffheight ; + } + + + +}; + +#endif \ No newline at end of file diff --git a/Utilities.h b/Utilities.h index 090e5d5..cfd523c 100644 --- a/Utilities.h +++ b/Utilities.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 @@ -15,9 +15,10 @@ #include "Config.h" -#if HAS_EEPROM +#if HAS_EEPROM #include #elif PLATFORM == PLATFORM_NRF52 + #include #include #include using namespace Adafruit_LittleFS_Namespace; @@ -49,6 +50,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 @@ -71,10 +75,10 @@ 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_RNODE_NG_22 + #elif BOARD_MODEL == BOARD_T3S3 + #include "hal/wdt_hal.h" + #else #include "hal/wdt_hal.h" - #else BOARD_MODEL != BOARD_RNODE_NG_22 - #include "soc/rtc_wdt.h" #endif #define ISR_VECT IRAM_ATTR #else @@ -100,16 +104,63 @@ uint8_t boot_vector = 0x00; // TODO: Get NRF52 boot flags #endif +#if MCU_VARIANT == MCU_NRF52 + unsigned long get_rng_seed() { + nrf_rng_error_correction_enable(NRF_RNG); + nrf_rng_shorts_disable(NRF_RNG, NRF_RNG_SHORT_VALRDY_STOP_MASK); + nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_START); + while (!nrf_rng_event_check(NRF_RNG, NRF_RNG_EVENT_VALRDY)); + uint8_t rb_a = nrf_rng_random_value_get(NRF_RNG); + nrf_rng_event_clear(NRF_RNG, NRF_RNG_EVENT_VALRDY); + while (!nrf_rng_event_check(NRF_RNG, NRF_RNG_EVENT_VALRDY)); + uint8_t rb_b = nrf_rng_random_value_get(NRF_RNG); + nrf_rng_event_clear(NRF_RNG, NRF_RNG_EVENT_VALRDY); + while (!nrf_rng_event_check(NRF_RNG, NRF_RNG_EVENT_VALRDY)); + uint8_t rb_c = nrf_rng_random_value_get(NRF_RNG); + nrf_rng_event_clear(NRF_RNG, NRF_RNG_EVENT_VALRDY); + while (!nrf_rng_event_check(NRF_RNG, NRF_RNG_EVENT_VALRDY)); + uint8_t rb_d = nrf_rng_random_value_get(NRF_RNG); + nrf_rng_event_clear(NRF_RNG, NRF_RNG_EVENT_VALRDY); + nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_STOP); + return rb_a << 24 | rb_b << 16 | rb_c << 8 | rb_d; + } +#endif + #if HAS_NP == true #include #define NUMPIXELS 1 - #define NP_M 0.15 Adafruit_NeoPixel pixels(NUMPIXELS, pin_np, NEO_GRB + NEO_KHZ800); 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 BOARD_MODEL == BOARD_HELTEC_T114 + // Enable vext power supply to neopixel + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, HIGH); + #endif + + #if MCU_VARIANT == MCU_NRF52 + 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); + } + #else + 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); + } + #endif + } + void npset(uint8_t r, uint8_t g, uint8_t b) { if (pixels_started != true) { pixels.begin(); @@ -118,7 +169,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(); } } @@ -143,43 +194,73 @@ uint8_t boot_vector = 0x00; void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #elif MCU_VARIANT == MCU_ESP32 #if HAS_NP == true void led_rx_on() { npset(0, 0, 0xFF); } void led_rx_off() { npset(0, 0, 0); } void led_tx_on() { npset(0xFF, 0x50, 0x00); } void led_tx_off() { npset(0, 0, 0); } + void led_id_on() { npset(0x90, 0, 0x70); } + void led_id_off() { npset(0, 0, 0); } #elif BOARD_MODEL == BOARD_RNODE_NG_20 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_RNODE_NG_21 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } - #elif BOARD_MODEL == BOARD_RNODE_NG_22 + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_T3S3 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_TBEAM void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, LOW); } void led_tx_off() { digitalWrite(pin_led_tx, HIGH); } + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_TDECK + void led_rx_on() { } + void led_rx_off() { } + void led_tx_on() { } + void led_tx_off() { } + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + void led_rx_on() { } + void led_rx_off() { } + void led_tx_on() { } + void led_tx_off() { } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_LORA32_V1_0 #if defined(EXTERNAL_LEDS) void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #else void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #endif #elif BOARD_MODEL == BOARD_LORA32_V2_0 #if defined(EXTERNAL_LEDS) @@ -187,11 +268,15 @@ uint8_t boot_vector = 0x00; void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #else void led_rx_on() { digitalWrite(pin_led_rx, LOW); } void led_rx_off() { digitalWrite(pin_led_rx, HIGH); } void led_tx_on() { digitalWrite(pin_led_tx, LOW); } void led_tx_off() { digitalWrite(pin_led_tx, HIGH); } + void led_id_on() { } + void led_id_off() { } #endif #elif BOARD_MODEL == BOARD_HELTEC32_V2 #if defined(EXTERNAL_LEDS) @@ -199,40 +284,83 @@ uint8_t boot_vector = 0x00; void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #else void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #endif #elif BOARD_MODEL == BOARD_HELTEC32_V3 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_LORA32_V2_1 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_XIAO_S3 + void led_rx_on() { digitalWrite(pin_led_rx, LED_ON); } + void led_rx_off() { digitalWrite(pin_led_rx, LED_OFF); } + void led_tx_on() { digitalWrite(pin_led_tx, LED_ON); } + void led_tx_off() { digitalWrite(pin_led_tx, LED_OFF); } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_HUZZAH32 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #elif BOARD_MODEL == BOARD_GENERIC_ESP32 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } + void led_id_on() { } + void led_id_off() { } #endif #elif MCU_VARIANT == MCU_NRF52 - #if BOARD_MODEL == BOARD_RAK4631 + #if HAS_NP == true + void led_rx_on() { npset(0, 0, 0xFF); } + void led_rx_off() { npset(0, 0, 0); } + void led_tx_on() { npset(0xFF, 0x50, 0x00); } + void led_tx_off() { npset(0, 0, 0); } + void led_id_on() { npset(0x90, 0, 0x70); } + void led_id_off() { npset(0, 0, 0); } + #elif BOARD_MODEL == BOARD_RAK4631 void led_rx_on() { digitalWrite(pin_led_rx, HIGH); } void led_rx_off() { digitalWrite(pin_led_rx, LOW); } void led_tx_on() { digitalWrite(pin_led_tx, HIGH); } void led_tx_off() { digitalWrite(pin_led_tx, LOW); } - #endif + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_HELTEC_T114 + // Heltec T114 pulls pins LOW to turn on + void led_rx_on() { digitalWrite(pin_led_rx, LOW); } + void led_rx_off() { digitalWrite(pin_led_rx, HIGH); } + void led_tx_on() { digitalWrite(pin_led_tx, LOW); } + void led_tx_off() { digitalWrite(pin_led_tx, HIGH); } + void led_id_on() { } + void led_id_off() { } + #elif BOARD_MODEL == BOARD_TECHO + void led_rx_on() { digitalWrite(pin_led_rx, LED_ON); } + void led_rx_off() { digitalWrite(pin_led_rx, LED_OFF); } + void led_tx_on() { digitalWrite(pin_led_tx, LED_ON); } + void led_tx_off() { digitalWrite(pin_led_tx, LED_OFF); } + void led_id_on() { } + void led_id_off() { } + #endif #endif void hard_reset(void) { @@ -244,7 +372,7 @@ void hard_reset(void) { #elif MCU_VARIANT == MCU_ESP32 ESP.restart(); #elif MCU_VARIANT == MCU_NRF52 - NVIC_SystemReset(); + NVIC_SystemReset(); #endif } @@ -385,6 +513,19 @@ void led_indicate_warning(int cycles) { } led_rx_off(); } + #elif BOARD_MODEL == BOARD_TECHO + void led_indicate_info(int cycles) { + bool forever = (cycles == 0) ? true : false; + cycles = forever ? 1 : cycles; + while(cycles > 0) { + led_rx_off(); + delay(100); + led_rx_on(); + delay(100); + if (!forever) cycles--; + } + led_rx_off(); + } #else void led_indicate_info(int cycles) { bool forever = (cycles == 0) ? true : false; @@ -411,8 +552,8 @@ unsigned long led_standby_ticks = 0; #elif 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; @@ -437,6 +578,8 @@ unsigned long led_standby_ticks = 0; #endif #elif MCU_VARIANT == MCU_NRF52 + int led_standby_lng = 200; + int led_standby_cut = 100; uint8_t led_standby_min = 200; uint8_t led_standby_max = 255; uint8_t led_notready_min = 0; @@ -492,7 +635,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); } } @@ -536,9 +679,17 @@ int8_t led_standby_direction = 0; } led_standby_value += led_standby_direction; if (led_standby_value > 253) { - led_tx_on(); + #if BOARD_MODEL == BOARD_TECHO + led_rx_on(); + #else + led_tx_on(); + #endif } else { - led_tx_off(); + #if BOARD_MODEL == BOARD_TECHO + led_rx_off(); + #else + led_tx_off(); + #endif } #if BOARD_MODEL == BOARD_LORA32_V2_1 #if defined(EXTERNAL_LEDS) @@ -642,6 +793,12 @@ 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 } #else Serial.write(byte); @@ -696,7 +853,7 @@ void kiss_indicate_stat_tx() { } void kiss_indicate_stat_rssi() { - uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); + uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); serial_write(FEND); serial_write(CMD_STAT_RSSI); escaped_serial_write(packet_rssi_val); @@ -784,11 +941,14 @@ void kiss_indicate_lt_alock() { } void kiss_indicate_channel_stats() { - #if MCU_VARIANT == MCU_ESP32 + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 uint16_t ats = (uint16_t)(airtime*100*100); uint16_t atl = (uint16_t)(longterm_airtime*100*100); uint16_t cls = (uint16_t)(total_channel_util*100*100); uint16_t cll = (uint16_t)(longterm_channel_util*100*100); + uint8_t crs = (uint8_t)(current_rssi+rssi_offset); + uint8_t nfl = (uint8_t)(noise_floor+rssi_offset); + uint8_t ntf = 0xFF; if (interference_detected) { ntf = (uint8_t)(current_rssi+rssi_offset); } serial_write(FEND); serial_write(CMD_STAT_CHTM); escaped_serial_write(ats>>8); @@ -799,35 +959,46 @@ void kiss_indicate_channel_stats() { escaped_serial_write(cls); escaped_serial_write(cll>>8); escaped_serial_write(cll); + escaped_serial_write(crs); + escaped_serial_write(nfl); + escaped_serial_write(ntf); + serial_write(FEND); + #endif +} + +void kiss_indicate_csma_stats() { + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 + serial_write(FEND); + serial_write(CMD_STAT_CSMA); + escaped_serial_write(cw_band); + escaped_serial_write(cw_min); + escaped_serial_write(cw_max); serial_write(FEND); #endif } void kiss_indicate_phy_stats() { - #if MCU_VARIANT == MCU_ESP32 + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 uint16_t lst = (uint16_t)(lora_symbol_time_ms*1000); uint16_t lsr = (uint16_t)(lora_symbol_rate); - uint16_t prs = (uint16_t)(lora_preamble_symbols+4); - uint16_t prt = (uint16_t)((lora_preamble_symbols+4)*lora_symbol_time_ms); + uint16_t prs = (uint16_t)(lora_preamble_symbols); + uint16_t prt = (uint16_t)(lora_preamble_time_ms); uint16_t cst = (uint16_t)(csma_slot_ms); + uint16_t dft = (uint16_t)(difs_ms); serial_write(FEND); serial_write(CMD_STAT_PHYPRM); - escaped_serial_write(lst>>8); - escaped_serial_write(lst); - escaped_serial_write(lsr>>8); - escaped_serial_write(lsr); - escaped_serial_write(prs>>8); - escaped_serial_write(prs); - escaped_serial_write(prt>>8); - escaped_serial_write(prt); - escaped_serial_write(cst>>8); - escaped_serial_write(cst); + escaped_serial_write(lst>>8); escaped_serial_write(lst); + escaped_serial_write(lsr>>8); escaped_serial_write(lsr); + escaped_serial_write(prs>>8); escaped_serial_write(prs); + escaped_serial_write(prt>>8); escaped_serial_write(prt); + escaped_serial_write(cst>>8); escaped_serial_write(cst); + escaped_serial_write(dft>>8); escaped_serial_write(dft); serial_write(FEND); #endif } void kiss_indicate_battery() { - #if MCU_VARIANT == MCU_ESP32 + #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 serial_write(FEND); serial_write(CMD_STAT_BAT); escaped_serial_write(battery_state); @@ -940,6 +1111,20 @@ void kiss_indicate_fb() { serial_write(FEND); } +void kiss_indicate_disp() { + serial_write(FEND); + serial_write(CMD_DISP_READ); + #if HAS_DISPLAY + uint8_t *da = disp_area.getBuffer(); + uint8_t *sa = stat_area.getBuffer(); + for (int i = 0; i < 512; i++) { escaped_serial_write(da[i]); } + for (int i = 0; i < 512; i++) { escaped_serial_write(sa[i]); } + #else + serial_write(0xFF); + #endif + serial_write(FEND); +} + void kiss_indicate_ready() { serial_write(FEND); serial_write(CMD_READY); @@ -1016,22 +1201,33 @@ void setPreamble() { void updateBitrate() { #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 - if (radio_online) { + if (!radio_online) { lora_bitrate = 0; } + else { lora_symbol_rate = (float)lora_bw/(float)(pow(2, lora_sf)); lora_symbol_time_ms = (1.0/lora_symbol_rate)*1000.0; lora_bitrate = (uint32_t)(lora_sf * ( (4.0/(float)lora_cr) / ((float)(pow(2, lora_sf))/((float)lora_bw/1000.0)) ) * 1000.0); lora_us_per_byte = 1000000.0/((float)lora_bitrate/8.0); - // csma_slot_ms = lora_symbol_time_ms*10; - float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW; - if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) { - target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN; - } else { - target_preamble_symbols = ceil(target_preamble_symbols); - } - lora_preamble_symbols = (long)target_preamble_symbols; - setPreamble(); - } else { - lora_bitrate = 0; + + bool fast_rate = lora_bitrate > LORA_FAST_THRESHOLD_BPS; + lora_limit_rate = lora_bitrate > LORA_LIMIT_THRESHOLD_BPS; + + int csma_slot_min_ms = CSMA_SLOT_MIN_MS; + float lora_preamble_target_ms = LORA_PREAMBLE_TARGET_MS; + if (fast_rate) { csma_slot_min_ms -= CSMA_SLOT_MIN_FAST_DELTA; + lora_preamble_target_ms -= LORA_PREAMBLE_FAST_DELTA; } + + csma_slot_ms = lora_symbol_time_ms*CSMA_SLOT_SYMBOLS; + if (csma_slot_ms > CSMA_SLOT_MAX_MS) { csma_slot_ms = CSMA_SLOT_MAX_MS; } + if (csma_slot_ms < CSMA_SLOT_MIN_MS) { csma_slot_ms = csma_slot_min_ms; } + difs_ms = CSMA_SIFS_MS + 2*csma_slot_ms; + + float target_preamble_symbols = lora_preamble_target_ms/lora_symbol_time_ms; + if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) { target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN; } + else { target_preamble_symbols = (ceil)(target_preamble_symbols); } + + lora_preamble_symbols = (long)target_preamble_symbols; setPreamble(); + lora_preamble_time_ms = (ceil)(lora_preamble_symbols * lora_symbol_time_ms); + lora_header_time_ms = (ceil)(PHY_HEADER_LORA_SYMBOLS * lora_symbol_time_ms); } #endif } @@ -1062,15 +1258,26 @@ int getTxPower() { void setTXPower() { if (radio_online) { + if (model == MODEL_11) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_12) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + + if (model == MODEL_C6) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_C7) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_A1) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A2) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A3) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); if (model == MODEL_A4) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_A5) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A6) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A7) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_AA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_AC) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_BA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_BB) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); @@ -1078,6 +1285,17 @@ void setTXPower() { if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_C5) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_CA) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + + if (model == MODEL_D4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_D9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + + if (model == MODEL_DB) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_DC) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + + if (model == MODEL_DD) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_DE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); @@ -1117,13 +1335,7 @@ void setFrequency() { } } -uint8_t getRandom() { - if (radio_online) { - return LoRa->random(); - } else { - return 0x00; - } -} +uint8_t getRandom() { return random(0xFF); } void promisc_enable() { promisc = true; @@ -1134,43 +1346,40 @@ void promisc_disable() { } #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 - bool eeprom_begin() { - InternalFS.begin(); + bool eeprom_begin() { + InternalFS.begin(); - file.open(EEPROM_FILE, FILE_O_READ); - - // if file doesn't exist - if (!file) { - if (file.open(EEPROM_FILE, FILE_O_WRITE)) { - // initialise the file with empty content - uint8_t empty_content[EEPROM_SIZE] = {0}; - file.write(empty_content, EEPROM_SIZE); - return true; - } else { - return false; - } - } else { - file.close(); - file.open(EEPROM_FILE, FILE_O_WRITE); - return true; - } + file.open(EEPROM_FILE, FILE_O_READ); + if (!file) { + if (file.open(EEPROM_FILE, FILE_O_WRITE)) { + for (uint32_t mapped_addr = 0; mapped_addr < EEPROM_SIZE; mapped_addr++) { file.seek(mapped_addr); file.write(0xFF); } + eeprom_flush(); + return true; + } else { + return false; + } + } else { + file.close(); + file.open(EEPROM_FILE, FILE_O_WRITE); + return true; } + } - uint8_t eeprom_read(uint32_t mapped_addr) { - uint8_t byte; - void* byte_ptr = &byte; - file.seek(mapped_addr); - file.read(byte_ptr, 1); - return byte; - } + uint8_t eeprom_read(uint32_t mapped_addr) { + uint8_t byte; + void* byte_ptr = &byte; + file.seek(mapped_addr); + file.read(byte_ptr, 1); + return byte; + } #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 { @@ -1220,7 +1429,6 @@ void kiss_dump_eeprom() { #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 void eeprom_flush() { - // sync file contents to flash file.close(); file.open(EEPROM_FILE, FILE_O_WRITE); written_bytes = 0; @@ -1235,31 +1443,19 @@ void eeprom_update(int mapped_addr, uint8_t byte) { EEPROM.write(mapped_addr, byte); EEPROM.commit(); } - #elif !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 - // todo: clean up this implementation, writing one byte and syncing - // each time is really slow, but this is also suboptimal - uint8_t read_byte; - void* read_byte_ptr = &read_byte; - file.seek(mapped_addr); - file.read(read_byte_ptr, 1); - file.seek(mapped_addr); - if (read_byte != byte) { - file.write(byte); - } - written_bytes++; - - if ((mapped_addr - eeprom_addr(0)) == ADDR_INFO_LOCK) { - #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 - // have to do a flush because we're only writing 1 byte and it syncs after 4 - eeprom_flush(); - #endif - } - - if (written_bytes >= 4) { - file.close(); - file.open(EEPROM_FILE, FILE_O_WRITE); - written_bytes = 0; - } + #elif !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 + // todo: clean up this implementation, writing one byte and syncing + // each time is really slow, but this is also suboptimal + uint8_t read_byte; + void* read_byte_ptr = &read_byte; + file.seek(mapped_addr); + file.read(read_byte_ptr, 1); + file.seek(mapped_addr); + if (read_byte != byte) { + file.write(byte); + } + written_bytes++; + eeprom_flush(); #endif } @@ -1272,9 +1468,13 @@ void eeprom_write(uint8_t addr, uint8_t byte) { } void eeprom_erase() { - for (int addr = 0; addr < EEPROM_RESERVED; addr++) { - eeprom_update(eeprom_addr(addr), 0xFF); - } + #if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52 + InternalFS.format(); + #else + for (int addr = 0; addr < EEPROM_RESERVED; addr++) { + eeprom_update(eeprom_addr(addr), 0xFF); + } + #endif hard_reset(); } @@ -1291,18 +1491,18 @@ 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_AVR if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) { #elif PLATFORM == PLATFORM_ESP32 - if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3) { + if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3 || rval == PRODUCT_TDECK_V1 || rval == PRODUCT_TBEAM_S_V1 || rval == PRODUCT_XIAO_S3) { #elif PLATFORM == PLATFORM_NRF52 - if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW) { + if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HELTEC_T114 || rval == PRODUCT_TECHO || rval == PRODUCT_HMBRW) { #else if (false) { #endif @@ -1324,12 +1524,20 @@ 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) { + #elif BOARD_MODEL == BOARD_T3S3 + if (model == MODEL_A1 || model == MODEL_A6 || model == MODEL_A5 || model == MODEL_AA || model == MODEL_AC) { #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_TDECK + if (model == MODEL_D4 || model == MODEL_D9) { + #elif BOARD_MODEL == BOARD_TECHO + if (model == MODEL_16 || model == MODEL_17) { + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + if (model == MODEL_DB || model == MODEL_DC) { + #elif BOARD_MODEL == BOARD_XIAO_S3 + if (model == MODEL_DD || model == MODEL_DE) { #elif BOARD_MODEL == BOARD_LORA32_V1_0 if (model == MODEL_BA || model == MODEL_BB) { #elif BOARD_MODEL == BOARD_LORA32_V2_0 @@ -1340,6 +1548,8 @@ bool eeprom_model_valid() { if (model == MODEL_C4 || model == MODEL_C9) { #elif BOARD_MODEL == BOARD_HELTEC32_V3 if (model == MODEL_C5 || model == MODEL_CA) { + #elif BOARD_MODEL == BOARD_HELTEC_T114 + if (model == MODEL_C6 || model == MODEL_C7) { #elif BOARD_MODEL == BOARD_RAK4631 if (model == MODEL_11 || model == MODEL_12) { #elif BOARD_MODEL == BOARD_HUZZAH32 @@ -1401,16 +1611,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 } } @@ -1422,6 +1632,40 @@ 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 drot_conf_save(uint8_t val) { + #if HAS_DISPLAY + if (val >= 0x00 and val <= 0x03) { + eeprom_update(eeprom_addr(ADDR_CONF_DROT), val); + hard_reset(); + } + #endif +} + +void dia_conf_save(uint8_t val) { + if (val > 0x00) { eeprom_update(eeprom_addr(ADDR_CONF_DIA), 0x01); } + else { eeprom_update(eeprom_addr(ADDR_CONF_DIA), 0x00); } + hard_reset(); +} + +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) { diff --git a/arduino-cli.yaml b/arduino-cli.yaml index cfbe258..6dd5f8d 100644 --- a/arduino-cli.yaml +++ b/arduino-cli.yaml @@ -2,4 +2,6 @@ board_manager: additional_urls: - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - https://raw.githubusercontent.com/RAKwireless/RAKwireless-Arduino-BSP-Index/main/package_rakwireless_index.json + - https://github.com/HelTecAutomation/Heltec_nRF52/releases/download/1.7.0/package_heltec_nrf_index.json + - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json - http://unsigned.io/arduino/package_unsignedio_UnsignedBoards_index.json diff --git a/esp32_btbufs.py b/esp32_btbufs.py new file mode 100755 index 0000000..ad0ae70 --- /dev/null +++ b/esp32_btbufs.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import sys +import re + +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 = re.sub(" +", " ", 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 = re.sub(" +", " ", line).split(" ") + try: + txbuf_size = int(ents[2]) + tx_line_index = line_index + except Exception as e: + print(f"Could not parse Bluetooth TX_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 diff --git a/partition_hashes b/partition_hashes index b520250..9d8db4b 100755 --- a/partition_hashes +++ b/partition_hashes @@ -1,6 +1,6 @@ #!/usr/bin/env python -# 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,6 +20,7 @@ import sys import RNS import json import hashlib +import subprocess major_version = None minor_version = None @@ -27,9 +28,23 @@ target_version = None target_file = os.path.join(sys.argv[1]) -firmware_data = open(target_file, "rb").read() -calc_hash = hashlib.sha256(firmware_data[0:-32]).digest() -part_hash = firmware_data[-32:] +if sys.argv[1] == "from_device": + from_device = True +else: + from_device = False -if calc_hash == part_hash: - print(RNS.hexrep(part_hash, delimit=False)) +if not from_device: + firmware_data = open(target_file, "rb").read() + calc_hash = hashlib.sha256(firmware_data[0:-32]).digest() + part_hash = firmware_data[-32:] + + if calc_hash == part_hash: + print(RNS.hexrep(part_hash, delimit=False)) + +else: + try: + cmdresult = subprocess.run(["rnodeconf", sys.argv[2], "-L"], stdout=subprocess.PIPE).stdout.decode('utf-8') + part_hash = cmdresult.split("The actual firmware hash is: ")[1].replace("\n", "") + print(part_hash) + except Exception as e: + print("Could not get partition hash from device: "+str(e)) diff --git a/release_hashes.py b/release_hashes.py index ce28a0c..3c08ca9 100644 --- a/release_hashes.py +++ b/release_hashes.py @@ -1,6 +1,6 @@ #!/bin/python -# 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/sx126x.cpp b/sx126x.cpp index d371251..30c22d9 100644 --- a/sx126x.cpp +++ b/sx126x.cpp @@ -1,9 +1,6 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - #include "Boards.h" #if MODEM == SX1262 @@ -24,19 +21,19 @@ #define OP_TX_6X 0x83 #define OP_RX_6X 0x82 #define OP_PA_CONFIG_6X 0x95 -#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as - // preamble detection, etc for - // knowing when it's safe to switch - // antenna modes +#define OP_SET_IRQ_FLAGS_6X 0x08 // Also provides info such as + // preamble detection, etc for + // knowing when it's safe to switch + // antenna modes #define OP_CLEAR_IRQ_STATUS_6X 0x02 #define OP_GET_IRQ_STATUS_6X 0x12 #define OP_RX_BUFFER_STATUS_6X 0x13 -#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet +#define OP_PACKET_STATUS_6X 0x14 // Get snr & rssi of last packet #define OP_CURRENT_RSSI_6X 0x15 -#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc. -#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc. +#define OP_MODULATION_PARAMS_6X 0x8B // BW, SF, CR, etc. +#define OP_PACKET_PARAMS_6X 0x8C // CRC, preamble, payload length, etc. #define OP_STATUS_6X 0xC0 -#define OP_TX_PARAMS_6X 0x8E // set dbm, etc +#define OP_TX_PARAMS_6X 0x8E // Set dbm, etc #define OP_PACKET_TYPE_6X 0x8A #define OP_BUFFER_BASE_ADDR_6X 0x8F #define OP_READ_REGISTER_6X 0x1D @@ -63,7 +60,7 @@ #define OP_FIFO_WRITE_6X 0x0E #define OP_FIFO_READ_6X 0x1E #define REG_OCP_6X 0x08E7 -#define REG_LNA_6X 0x08AC // no agc in sx1262 +#define REG_LNA_6X 0x08AC // No agc in sx1262 #define REG_SYNC_WORD_MSB_6X 0x0740 #define REG_SYNC_WORD_LSB_6X 0x0741 #define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98 @@ -87,20 +84,24 @@ #define SYNC_WORD_6X 0x1424 #define XTAL_FREQ_6X (double)32000000 -#define FREQ_DIV_6X (double)pow(2.0, 25.0) +#define FREQ_DIV_6X (double)pow(2.0, 25.0) #define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) -#if defined(NRF52840_XXAA) +#if BOARD_MODEL == BOARD_TECHO + SPIClass spim3 = SPIClass(NRF_SPIM3, pin_miso, pin_sclk, pin_mosi) ; + #define SPI spim3 + +#elif defined(NRF52840_XXAA) extern SPIClass spiModem; #define SPI spiModem #endif extern SPIClass SPI; -#define MAX_PKT_LENGTH 255 +#define MAX_PKT_LENGTH 255 sx126x::sx126x() : - _spiSettings(8E6, MSBFIRST, SPI_MODE0), + _spiSettings(16E6, MSBFIRST, SPI_MODE0), _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _frequency(0), _txp(0), @@ -118,22 +119,22 @@ sx126x::sx126x() : _packet({0}), _preinit_done(false), _onReceive(NULL) -{ - // overide Stream timeout value - setTimeout(0); -} +{ setTimeout(0); } bool sx126x::preInit() { pinMode(_ss, OUTPUT); digitalWrite(_ss, HIGH); - #if BOARD_MODEL == BOARD_RNODE_NG_22 || BOARD_MODEL == BOARD_HELTEC32_V3 + #if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3 SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); + #elif BOARD_MODEL == BOARD_TECHO + SPI.setPins(pin_miso, pin_sclk, pin_mosi); + SPI.begin(); #else SPI.begin(); #endif - // check version (retry for up to 2 seconds) + // Check version (retry for up to 2 seconds) // TODO: Actually read version registers, not syncwords long start = millis(); uint8_t syncmsb; @@ -154,190 +155,129 @@ bool sx126x::preInit() { return true; } -uint8_t ISR_VECT sx126x::readRegister(uint16_t address) -{ +uint8_t ISR_VECT sx126x::readRegister(uint16_t address) { return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); } -void sx126x::writeRegister(uint16_t address, uint8_t value) -{ - singleTransfer(OP_WRITE_REGISTER_6X, address, value); +void sx126x::writeRegister(uint16_t address, uint8_t value) { + singleTransfer(OP_WRITE_REGISTER_6X, address, value); } -uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) -{ - waitOnBusy(); +uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) { + waitOnBusy(); + + uint8_t response; + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer((address & 0xFF00) >> 8); + SPI.transfer(address & 0x00FF); + if (opcode == OP_READ_REGISTER_6X) { SPI.transfer(0x00); } + response = SPI.transfer(value); + SPI.endTransaction(); - uint8_t response; + digitalWrite(_ss, HIGH); - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer((address & 0xFF00) >> 8); - SPI.transfer(address & 0x00FF); - if (opcode == OP_READ_REGISTER_6X) { - SPI.transfer(0x00); - } - response = SPI.transfer(value); - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - - return response; + return response; } -void sx126x::rxAntEnable() -{ - if (_rxen != -1) { - digitalWrite(_rxen, HIGH); - } +void sx126x::rxAntEnable() { + if (_rxen != -1) { digitalWrite(_rxen, HIGH); } } void sx126x::loraMode() { - // enable lora mode on the SX1262 chip - uint8_t mode = MODE_LONG_RANGE_MODE_6X; - executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); + // Enable lora mode on the SX1262 chip + uint8_t mode = MODE_LONG_RANGE_MODE_6X; + executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); } void sx126x::waitOnBusy() { - unsigned long time = millis(); - if (_busy != -1) { - while (digitalRead(_busy) == HIGH) - { - if (millis() >= (time + 100)) { - break; - } - // do nothing - } + unsigned long time = millis(); + if (_busy != -1) { + while (digitalRead(_busy) == HIGH) { + if (millis() >= (time + 100)) { break; } } + } } -void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); +void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) { + waitOnBusy(); + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); } + SPI.endTransaction(); + digitalWrite(_ss, HIGH); } -void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); +void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) { + waitOnBusy(); + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer(0x00); + for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } + SPI.endTransaction(); + digitalWrite(_ss, HIGH); } -void sx126x::writeBuffer(const uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_WRITE_6X); - SPI.transfer(_fifo_tx_addr_ptr); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - _fifo_tx_addr_ptr++; - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); +void sx126x::writeBuffer(const uint8_t* buffer, size_t size) { + waitOnBusy(); + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_WRITE_6X); + SPI.transfer(_fifo_tx_addr_ptr); + for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; } + SPI.endTransaction(); + digitalWrite(_ss, HIGH); } -void sx126x::readBuffer(uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_READ_6X); - SPI.transfer(_fifo_rx_addr_ptr); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); +void sx126x::readBuffer(uint8_t* buffer, size_t size) { + waitOnBusy(); + digitalWrite(_ss, LOW); + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_READ_6X); + SPI.transfer(_fifo_rx_addr_ptr); + SPI.transfer(0x00); + for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } + SPI.endTransaction(); + digitalWrite(_ss, HIGH); } void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { - // because there is no access to these registers on the sx1262, we have + // Because there is no access to these registers on the sx1262, we have // to set all these parameters at once or not at all. uint8_t buf[8]; - buf[0] = sf; buf[1] = bw; buf[2] = cr; - // low data rate toggle - buf[3] = ldro; - // unused params in LoRa mode - buf[4] = 0x00; + buf[3] = ldro; // Low data rate toggle + buf[4] = 0x00; // Unused params in LoRa mode buf[5] = 0x00; buf[6] = 0x00; buf[7] = 0x00; - executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8); } -void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) { - // because there is no access to these registers on the sx1262, we have +void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) { + // Because there is no access to these registers on the sx1262, we have // to set all these parameters at once or not at all. uint8_t buf[9]; - - buf[0] = uint8_t((preamble & 0xFF00) >> 8); - buf[1] = uint8_t((preamble & 0x00FF)); + buf[0] = uint8_t((preamble_symbols & 0xFF00) >> 8); + buf[1] = uint8_t((preamble_symbols & 0x00FF)); buf[2] = headermode; - buf[3] = length; + buf[3] = payload_length; buf[4] = crc; - // standard IQ setting (no inversion) - buf[5] = 0x00; - // unused params - buf[6] = 0x00; + buf[5] = 0x00; // standard IQ setting (no inversion) + buf[6] = 0x00; // unused params buf[7] = 0x00; buf[8] = 0x00; - executeOpcode(OP_PACKET_PARAMS_6X, buf, 9); } void sx126x::reset(void) { if (_reset != -1) { pinMode(_reset, OUTPUT); - - // perform reset digitalWrite(_reset, LOW); delay(10); digitalWrite(_reset, HIGH); @@ -350,7 +290,7 @@ void sx126x::calibrate(void) { uint8_t mode_byte = MODE_STDBY_RC_6X; executeOpcode(OP_STANDBY_6X, &mode_byte, 1); - // calibrate RC64k, RC13M, PLL, ADC and image + // Calibrate RC64k, RC13M, PLL, ADC and image uint8_t calibrate = MASK_CALIBRATE_ALL; executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); @@ -360,55 +300,25 @@ void sx126x::calibrate(void) { void sx126x::calibrate_image(long frequency) { uint8_t image_freq[2] = {0}; - - if (frequency >= 430E6 && frequency <= 440E6) { - image_freq[0] = 0x6B; - image_freq[1] = 0x6F; - } - else if (frequency >= 470E6 && frequency <= 510E6) { - image_freq[0] = 0x75; - image_freq[1] = 0x81; - } - else if (frequency >= 779E6 && frequency <= 787E6) { - image_freq[0] = 0xC1; - image_freq[1] = 0xC5; - } - else if (frequency >= 863E6 && frequency <= 870E6) { - image_freq[0] = 0xD7; - image_freq[1] = 0xDB; - } - else if (frequency >= 902E6 && frequency <= 928E6) { - image_freq[0] = 0xE1; - image_freq[1] = 0xE9; - } - + if (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; } + else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; } + else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; } + else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; } + else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; } // TODO: Allow higher freq calibration executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2); waitOnBusy(); } -int sx126x::begin(long frequency) -{ +int sx126x::begin(long frequency) { reset(); - - if (_busy != -1) { - pinMode(_busy, INPUT); - } - - if (!_preinit_done) { - if (!preInit()) { - return false; - } - } - - if (_rxen != -1) { - pinMode(_rxen, OUTPUT); - } + + if (_busy != -1) { pinMode(_busy, INPUT); } + if (!_preinit_done) { if (!preInit()) { return false; } } + if (_rxen != -1) { pinMode(_rxen, OUTPUT); } calibrate(); calibrate_image(frequency); - enableTCXO(); - loraMode(); standby(); @@ -422,18 +332,11 @@ int sx126x::begin(long frequency) #endif rxAntEnable(); - setFrequency(frequency); - - // set output power to 2 dBm setTxPower(2); enableCrc(); - - // set LNA boost - writeRegister(REG_LNA_6X, 0x96); - - // set base addresses - uint8_t basebuf[2] = {0}; + writeRegister(REG_LNA_6X, 0x96); // Set LNA boost + uint8_t basebuf[2] = {0}; // Set base addresses executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); setModulationParams(_sf, _bw, _cr, _ldro); @@ -442,26 +345,12 @@ int sx126x::begin(long frequency) return 1; } -void sx126x::end() -{ - // put in sleep mode - sleep(); +void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; } - // stop SPI - SPI.end(); - - _preinit_done = false; -} - -int sx126x::beginPacket(int implicitHeader) -{ +int sx126x::beginPacket(int implicitHeader) { standby(); - - if (implicitHeader) { - implicitHeaderMode(); - } else { - explicitHeaderMode(); - } + if (implicitHeader) { implicitHeaderMode(); } + else { explicitHeaderMode(); } _payloadLength = 0; _fifo_tx_addr_ptr = 0; @@ -470,146 +359,144 @@ int sx126x::beginPacket(int implicitHeader) return 1; } -int sx126x::endPacket() -{ - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +int sx126x::endPacket() { + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + uint8_t timeout[3] = {0}; // Put in single TX mode + 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]; - - buf[0] = 0x00; - buf[1] = 0x00; - - executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - - // 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 - - 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() { - // imitate the register status from the sx1276 / 78 - uint8_t buf[2] = {0}; + uint8_t buf[2]; + buf[0] = 0x00; + buf[1] = 0x00; + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + // Wait for TX done + bool timed_out = false; + uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS; + while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0)) { + buf[0] = 0x00; + buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - uint8_t clearbuf[2] = {0}; - uint8_t byte = 0x00; + yield(); + } - if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) { - byte = byte | 0x01 | 0x04; - // clear register after reading - clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; - } + if (!(millis() < w_timeout)) { timed_out = true; } - if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { - byte = byte | 0x02 | 0x04; - } - - executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); - - return byte; + // Clear IRQs + uint8_t mask[2]; + mask[0] = 0x00; + mask[1] = IRQ_TX_DONE_MASK_6X; + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); + if (timed_out) { return 0; } else { return 1; } } +unsigned long preamble_detected_at = 0; +extern long lora_preamble_time_ms; +extern long lora_header_time_ms; +bool false_preamble_detected = false; + +bool sx126x::dcd() { + uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + uint32_t now = millis(); + + bool header_detected = false; + bool carrier_detected = false; + + if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { header_detected = true; carrier_detected = true; } + else { header_detected = false; } + + if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) { + carrier_detected = true; + if (preamble_detected_at == 0) { preamble_detected_at = now; } + if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) { + preamble_detected_at = 0; + if (!header_detected) { false_preamble_detected = true; } + uint8_t clearbuf[2] = {0}; + clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); + } + } + + // TODO: Maybe there's a way of unlatching the RSSI + // status without re-activating receive mode? + if (false_preamble_detected) { sx126x_modem.receive(); false_preamble_detected = false; } + return carrier_detected; +} uint8_t sx126x::currentRssiRaw() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); - return byte; + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); + return byte; } int ISR_VECT sx126x::currentRssi() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); - int rssi = -(int(byte)) / 2; - return rssi; + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); + int rssi = -(int(byte)) / 2; + return rssi; } uint8_t sx126x::packetRssiRaw() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return buf[2]; + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return buf[2]; } int ISR_VECT sx126x::packetRssi() { - // may need more calculations here - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - int pkt_rssi = -buf[0] / 2; - return pkt_rssi; + // TODO: May need more calculations here + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + int pkt_rssi = -buf[0] / 2; + return pkt_rssi; +} + +int ISR_VECT sx126x::packetRssi(uint8_t pkt_snr_raw) { + // TODO: May need more calculations here + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + int pkt_rssi = -buf[0] / 2; + return pkt_rssi; } uint8_t ISR_VECT sx126x::packetSnrRaw() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return buf[1]; + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return buf[1]; } float ISR_VECT sx126x::packetSnr() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return float(buf[1]) * 0.25; + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return float(buf[1]) * 0.25; } -long sx126x::packetFrequencyError() -{ - // todo: implement this, no idea how to check it on the sx1262 - const float fError = 0.0; - return static_cast(fError); +long sx126x::packetFrequencyError() { + // TODO: Implement this, no idea how to check it on the sx1262 + const float fError = 0.0; + return static_cast(fError); } -size_t sx126x::write(uint8_t byte) -{ - return write(&byte, sizeof(byte)); +size_t sx126x::write(uint8_t byte) { return write(&byte, sizeof(byte)); } +size_t sx126x::write(const uint8_t *buffer, size_t size) { + if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; } + writeBuffer(buffer, size); + _payloadLength = _payloadLength + size; + return size; } -size_t sx126x::write(const uint8_t *buffer, size_t size) -{ - if ((_payloadLength + size) > MAX_PKT_LENGTH) { - size = MAX_PKT_LENGTH - _payloadLength; - } - - // write data - writeBuffer(buffer, size); - _payloadLength = _payloadLength + size; - return size; +int ISR_VECT sx126x::available() { + uint8_t buf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); + return buf[0] - _packetIndex; } -int ISR_VECT sx126x::available() -{ - uint8_t buf[2] = {0}; - executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); - return buf[0] - _packetIndex; -} - -int ISR_VECT sx126x::read() -{ - if (!available()) { - return -1; - } - - // if received new packet +int ISR_VECT sx126x::read(){ + if (!available()) { return -1; } if (_packetIndex == 0) { - uint8_t rxbuf[2] = {0}; - executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); - int size = rxbuf[0]; - _fifo_rx_addr_ptr = rxbuf[1]; - - readBuffer(_packet, size); + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); + int size = rxbuf[0]; + _fifo_rx_addr_ptr = rxbuf[1]; + readBuffer(_packet, size); } uint8_t byte = _packet[_packetIndex]; @@ -617,19 +504,13 @@ int ISR_VECT sx126x::read() return byte; } -int sx126x::peek() -{ - if (!available()) { - return -1; - } - - // if received new packet +int sx126x::peek() { + if (!available()) { return -1; } if (_packetIndex == 0) { uint8_t rxbuf[2] = {0}; executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); int size = rxbuf[0]; _fifo_rx_addr_ptr = rxbuf[1]; - readBuffer(_packet, size); } @@ -637,91 +518,71 @@ int sx126x::peek() return b; } -void sx126x::flush() -{ -} +void sx126x::flush() { } -void sx126x::onReceive(void(*callback)(int)) -{ +void sx126x::onReceive(void(*callback)(int)){ _onReceive = callback; if (callback) { pinMode(_dio0, INPUT); - - // set preamble and header detection irqs, plus dio0 mask - uint8_t buf[8]; - - // set irq masks, enable all - buf[0] = 0xFF; + uint8_t buf[8]; // Set preamble and header detection irqs, plus dio0 mask + buf[0] = 0xFF; // Set irq masks, enable all buf[1] = 0xFF; - - // set dio0 masks - buf[2] = 0x00; + buf[2] = 0x00; // Set dio0 masks buf[3] = IRQ_RX_DONE_MASK_6X; - - // set dio1 masks - buf[4] = 0x00; + buf[4] = 0x00; // Set dio1 masks buf[5] = 0x00; - - // set dio2 masks - buf[6] = 0x00; + buf[6] = 0x00; // Set dio2 masks buf[7] = 0x00; - executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); -#ifdef SPI_HAS_NOTUSINGINTERRUPT - SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); -#endif + + #ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); + #endif attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING); + } else { detachInterrupt(digitalPinToInterrupt(_dio0)); -#ifdef SPI_HAS_NOTUSINGINTERRUPT - SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); -#endif + #ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); + #endif } } -void sx126x::receive(int size) -{ - if (size > 0) { - implicitHeaderMode(); +void sx126x::receive(int size) { + if (size > 0) { + implicitHeaderMode(); + _payloadLength = size; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + } else { explicitHeaderMode(); } - // tell radio payload length - _payloadLength = size; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - } else { - explicitHeaderMode(); - } - - if (_rxen != -1) { - rxAntEnable(); - } - - uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode - executeOpcode(OP_RX_6X, mode, 3); + if (_rxen != -1) { rxAntEnable(); } + uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // Continuous mode + executeOpcode(OP_RX_6X, mode, 3); } -void sx126x::standby() -{ - // STDBY_XOSC - uint8_t byte = MODE_STDBY_XOSC_6X; - // STDBY_RC - // uint8_t byte = MODE_STDBY_RC_6X; +void sx126x::standby() { + uint8_t byte = MODE_STDBY_XOSC_6X; // STDBY_XOSC executeOpcode(OP_STANDBY_6X, &byte, 1); } -void sx126x::sleep() -{ - uint8_t byte = 0x00; - executeOpcode(OP_SLEEP_6X, &byte, 1); -} +void sx126x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_6X, &byte, 1); } void sx126x::enableTCXO() { #if HAS_TCXO - #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 + #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3 uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF}; #elif BOARD_MODEL == BOARD_TBEAM uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; - #elif BOARD_MODEL == BOARD_RNODE_NG_22 + #elif BOARD_MODEL == BOARD_TDECK + uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; + #elif BOARD_MODEL == BOARD_TBEAM_S_V1 + uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; + #elif BOARD_MODEL == BOARD_T3S3 + uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; + #elif BOARD_MODEL == BOARD_HELTEC_T114 + uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; + #elif BOARD_MODEL == BOARD_TECHO uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; #endif executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4); @@ -732,185 +593,125 @@ void sx126x::enableTCXO() { void sx126x::disableTCXO() { } void sx126x::setTxPower(int level, int outputPin) { - // currently no low power mode for SX1262 implemented, assuming PA boost - - // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 - // RegTxClampConfig = @address 0x08D8 - writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); + // Currently no low power mode for SX1262 implemented, assuming PA boost + + // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 + // RegTxClampConfig = @address 0x08D8 + writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); - uint8_t pa_buf[4]; + uint8_t pa_buf[4]; + pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs + pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262 + pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261) + pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet) + executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power - pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs - pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262 - pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261) - pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet) + if (level > 22) { level = 22; } + else if (level < -9) { level = -9; } + writeRegister(REG_OCP_6X, OCP_TUNED); // Use board-specific tuned OCP - executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power + uint8_t tx_buf[2]; + tx_buf[0] = level; + tx_buf[1] = 0x02; // PA ramping time - 40 microseconds + executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2); - if (level > 22) { level = 22; } - else if (level < -9) { level = -9; } - - writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection - - uint8_t tx_buf[2]; - - tx_buf[0] = level; - tx_buf[1] = 0x02; // PA ramping time - 40 microseconds - - executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2); - - _txp = level; + _txp = level; } -uint8_t sx126x::getTxPower() { - return _txp; -} +uint8_t sx126x::getTxPower() { return _txp; } void sx126x::setFrequency(long frequency) { _frequency = frequency; - uint8_t buf[4]; - uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X); - buf[0] = ((freq >> 24) & 0xFF); buf[1] = ((freq >> 16) & 0xFF); buf[2] = ((freq >> 8) & 0xFF); buf[3] = (freq & 0xFF); - executeOpcode(OP_RF_FREQ_6X, buf, 4); } uint32_t sx126x::getFrequency() { - // we can't read the frequency on the sx1262 / 80 + // We can't read the frequency on the sx1262 / 80 uint32_t frequency = _frequency; - return frequency; } -void sx126x::setSpreadingFactor(int sf) -{ - if (sf < 5) { - sf = 5; - } else if (sf > 12) { - sf = 12; - } - +void sx126x::setSpreadingFactor(int sf) { + if (sf < 5) { sf = 5; } + else if (sf > 12) { sf = 12; } _sf = sf; handleLowDataRate(); setModulationParams(sf, _bw, _cr, _ldro); } -long sx126x::getSignalBandwidth() -{ - int bw = _bw; - switch (bw) { - case 0x00: return 7.8E3; - case 0x01: return 15.6E3; - case 0x02: return 31.25E3; - case 0x03: return 62.5E3; - case 0x04: return 125E3; - case 0x05: return 250E3; - case 0x06: return 500E3; - case 0x08: return 10.4E3; - case 0x09: return 20.8E3; - case 0x0A: return 41.7E3; - } +long sx126x::getSignalBandwidth() { + int bw = _bw; + switch (bw) { + case 0x00: return 7.8E3; + case 0x01: return 15.6E3; + case 0x02: return 31.25E3; + case 0x03: return 62.5E3; + case 0x04: return 125E3; + case 0x05: return 250E3; + case 0x06: return 500E3; + case 0x08: return 10.4E3; + case 0x09: return 20.8E3; + case 0x0A: return 41.7E3; + } return 0; } -void sx126x::handleLowDataRate(){ - if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) { - _ldro = 0x01; - } else { - _ldro = 0x00; - } +extern bool lora_low_datarate; +void sx126x::handleLowDataRate() { + if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) + { _ldro = 0x01; lora_low_datarate = true; } + else { _ldro = 0x00; lora_low_datarate = false; } } -void sx126x::optimizeModemSensitivity(){ - // todo: check if there's anything the sx1262 can do here -} +// TODO: Check if there's anything the sx1262 can do here +void sx126x::optimizeModemSensitivity(){ } -void sx126x::setSignalBandwidth(long sbw) -{ - if (sbw <= 7.8E3) { - _bw = 0x00; - } else if (sbw <= 10.4E3) { - _bw = 0x08; - } else if (sbw <= 15.6E3) { - _bw = 0x01; - } else if (sbw <= 20.8E3) { - _bw = 0x09; - } else if (sbw <= 31.25E3) { - _bw = 0x02; - } else if (sbw <= 41.7E3) { - _bw = 0x0A; - } else if (sbw <= 62.5E3) { - _bw = 0x03; - } else if (sbw <= 125E3) { - _bw = 0x04; - } else if (sbw <= 250E3) { - _bw = 0x05; - } else /*if (sbw <= 250E3)*/ { - _bw = 0x06; - } +void sx126x::setSignalBandwidth(long sbw) { + if (sbw <= 7.8E3) { _bw = 0x00; } + else if (sbw <= 10.4E3) { _bw = 0x08; } + else if (sbw <= 15.6E3) { _bw = 0x01; } + else if (sbw <= 20.8E3) { _bw = 0x09; } + else if (sbw <= 31.25E3) { _bw = 0x02; } + else if (sbw <= 41.7E3) { _bw = 0x0A; } + else if (sbw <= 62.5E3) { _bw = 0x03; } + else if (sbw <= 125E3) { _bw = 0x04; } + else if (sbw <= 250E3) { _bw = 0x05; } + else { _bw = 0x06; } handleLowDataRate(); setModulationParams(_sf, _bw, _cr, _ldro); - optimizeModemSensitivity(); } -void sx126x::setCodingRate4(int denominator) -{ - if (denominator < 5) { - denominator = 5; - } else if (denominator > 8) { - denominator = 8; - } - +void sx126x::setCodingRate4(int denominator) { + if (denominator < 5) { denominator = 5; } + else if (denominator > 8) { denominator = 8; } int cr = denominator - 4; - _cr = cr; - setModulationParams(_sf, _bw, cr, _ldro); } -void sx126x::setPreambleLength(long length) -{ - _preambleLength = length; - setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); +void sx126x::setPreambleLength(long preamble_symbols) { + _preambleLength = preamble_symbols; + setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode); } -void sx126x::setSyncWord(uint16_t sw) -{ - // TODO: Fix - // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8); - // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF); - writeRegister(REG_SYNC_WORD_MSB_6X, 0x14); - writeRegister(REG_SYNC_WORD_LSB_6X, 0x24); +void sx126x::setSyncWord(uint16_t sw) { + // TODO: Why was this hardcoded instead of using the config value? + // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8); + // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF); + writeRegister(REG_SYNC_WORD_MSB_6X, 0x14); + writeRegister(REG_SYNC_WORD_LSB_6X, 0x24); } -void sx126x::enableCrc() -{ - _crcMode = 1; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); -} - -void sx126x::disableCrc() -{ - _crcMode = 0; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); -} - -byte sx126x::random() -{ - return readRegister(REG_RANDOM_GEN_6X); -} - -void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) -{ +void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) { _ss = ss; _reset = reset; _dio0 = dio0; @@ -918,13 +719,7 @@ void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) _rxen = rxen; } -void sx126x::setSPIFrequency(uint32_t frequency) -{ - _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); -} - -void sx126x::dumpRegisters(Stream& out) -{ +void sx126x::dumpRegisters(Stream& out) { for (int i = 0; i < 128; i++) { out.print("0x"); out.print(i, HEX); @@ -933,54 +728,29 @@ void sx126x::dumpRegisters(Stream& out) } } -void sx126x::explicitHeaderMode() -{ - _implicitHeaderMode = 0; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +void ISR_VECT sx126x::handleDio0Rise() { + uint8_t buf[2]; + buf[0] = 0x00; + buf[1] = 0x00; + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); + + if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { + _packetIndex = 0; + uint8_t rxbuf[2] = {0}; // Read packet length + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); + int packetLength = rxbuf[0]; + if (_onReceive) { _onReceive(packetLength); } + } } -void sx126x::implicitHeaderMode() -{ - _implicitHeaderMode = 1; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); -} - - -void ISR_VECT sx126x::handleDio0Rise() -{ - uint8_t buf[2]; - - buf[0] = 0x00; - buf[1] = 0x00; - - executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - - executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); - - if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { - // received a packet - _packetIndex = 0; - - // read packet length - uint8_t rxbuf[2] = {0}; - executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); - int packetLength = rxbuf[0]; - - if (_onReceive) { - _onReceive(packetLength); - } - } - // else { - // Serial.println("CRCE"); - // Serial.println(buf[0]); - // Serial.println(buf[1]); - // } -} - -void ISR_VECT sx126x::onDio0Rise() -{ - sx126x_modem.handleDio0Rise(); -} +void ISR_VECT sx126x::onDio0Rise() { sx126x_modem.handleDio0Rise(); } +void sx126x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } +void sx126x::enableCrc() { _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx126x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx126x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx126x::implicitHeaderMode() { _implicitHeaderMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +byte sx126x::random() { return readRegister(REG_RANDOM_GEN_6X); } sx126x sx126x_modem; diff --git a/sx126x.h b/sx126x.h index 5e0ff4d..068a1bb 100644 --- a/sx126x.h +++ b/sx126x.h @@ -1,9 +1,6 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - #ifndef SX126X_H #define SX126X_H @@ -17,6 +14,7 @@ #define LORA_DEFAULT_RXEN_PIN -1 #define LORA_DEFAULT_TXEN_PIN -1 #define LORA_DEFAULT_BUSY_PIN -1 +#define LORA_MODEM_TIMEOUT_MS 20E3 #define PA_OUTPUT_RFO_PIN 0 #define PA_OUTPUT_PA_BOOST_PIN 1 @@ -35,6 +33,7 @@ public: int parsePacket(int size = 0); int packetRssi(); + int packetRssi(uint8_t pkt_snr_raw); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); @@ -57,6 +56,7 @@ public: void receive(int size = 0); void standby(); void sleep(); + void reset(void); bool preInit(); uint8_t getTxPower(); @@ -67,9 +67,9 @@ public: long getSignalBandwidth(); void setSignalBandwidth(long sbw); void setCodingRate4(int denominator); - void setPreambleLength(long length); + void setPreambleLength(long preamble_symbols); void setSyncWord(uint16_t sw); - uint8_t modemStatus(); + bool dcd(); void enableCrc(); void disableCrc(); void enableTCXO(); @@ -82,7 +82,7 @@ public: void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); void writeBuffer(const uint8_t* buffer, size_t size); void readBuffer(uint8_t* buffer, size_t size); - void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); + void setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); @@ -112,7 +112,6 @@ private: void handleLowDataRate(); void optimizeModemSensitivity(); - void reset(void); void calibrate(void); void calibrate_image(long frequency); diff --git a/sx127x.cpp b/sx127x.cpp index c5f6d40..ef9038d 100644 --- a/sx127x.cpp +++ b/sx127x.cpp @@ -1,9 +1,6 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - #include "Boards.h" #if MODEM == SX1276 @@ -11,7 +8,7 @@ #if MCU_VARIANT == MCU_ESP32 #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) - #include "soc/rtc_wdt.h" + #include "hal/wdt_hal.h" #endif #define ISR_VECT IRAM_ATTR #else @@ -81,10 +78,7 @@ extern SPIClass SPI; sx127x::sx127x() : _spiSettings(8E6, MSBFIRST, SPI_MODE0), _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), - _frequency(0), - _packetIndex(0), - _preinit_done(false), - _onReceive(NULL) { setTimeout(0); } + _frequency(0), _packetIndex(0), _preinit_done(false), _onReceive(NULL) { setTimeout(0); } void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } void sx127x::setPins(int ss, int reset, int dio0, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _busy = busy; } @@ -92,7 +86,6 @@ uint8_t ISR_VECT sx127x::readRegister(uint8_t address) { return singleTransfer(a void sx127x::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); } void sx127x::standby() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); } void sx127x::sleep() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); } -uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); } void sx127x::setSyncWord(uint8_t sw) { writeRegister(REG_SYNC_WORD_7X, sw); } void sx127x::enableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); } void sx127x::disableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); } @@ -106,7 +99,12 @@ void sx127x::flush() { } bool sx127x::preInit() { pinMode(_ss, OUTPUT); digitalWrite(_ss, HIGH); - SPI.begin(); + + #if BOARD_MODEL == BOARD_T3S3 + SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); + #else + SPI.begin(); + #endif // Check modem version uint8_t version; @@ -118,7 +116,6 @@ bool sx127x::preInit() { } if (version != 0x12) { return false; } - _preinit_done = true; return true; } @@ -139,8 +136,6 @@ uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) { int sx127x::begin(long frequency) { if (_reset != -1) { pinMode(_reset, OUTPUT); - - // Perform reset digitalWrite(_reset, LOW); delay(10); digitalWrite(_reset, HIGH); @@ -148,19 +143,16 @@ int sx127x::begin(long frequency) { } if (_busy != -1) { pinMode(_busy, INPUT); } - - if (!_preinit_done) { - if (!preInit()) { return false; } - } + if (!_preinit_done) { if (!preInit()) { return false; } } sleep(); setFrequency(frequency); - // set base addresses + // Set base addresses writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0); writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0); - // set LNA boost and auto AGC + // Set LNA boost and auto AGC writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03); writeRegister(REG_MODEM_CONFIG_3_7X, 0x04); @@ -173,20 +165,13 @@ int sx127x::begin(long frequency) { return 1; } -void sx127x::end() { - sleep(); - SPI.end(); - _preinit_done = false; -} +void sx127x::end() { sleep(); SPI.end(); _preinit_done = false; } int sx127x::beginPacket(int implicitHeader) { standby(); - if (implicitHeader) { - implicitHeaderMode(); - } else { - explicitHeaderMode(); - } + if (implicitHeader) { implicitHeaderMode(); } + else { explicitHeaderMode(); } // Reset FIFO address and payload length writeRegister(REG_FIFO_ADDR_PTR_7X, 0); @@ -209,6 +194,14 @@ int sx127x::endPacket() { return 1; } +bool sx127x::dcd() { + bool carrier_detected = false; + uint8_t status = readRegister(REG_MODEM_STAT_7X); + if ((status & SIG_DETECT) == SIG_DETECT) { carrier_detected = true; } + if ((status & SIG_SYNCED) == SIG_SYNCED) { carrier_detected = true; } + return carrier_detected; +} + uint8_t sx127x::currentRssiRaw() { uint8_t rssi = readRegister(REG_RSSI_VALUE_7X); return rssi; @@ -225,30 +218,42 @@ uint8_t sx127x::packetRssiRaw() { return pkt_rssi_value; } +int ISR_VECT sx127x::packetRssi(uint8_t pkt_snr_raw) { + int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET; + int pkt_snr = ((int8_t)pkt_snr_raw)*0.25; + + if (_frequency < 820E6) pkt_rssi -= 7; + + if (pkt_snr < 0) { + pkt_rssi += pkt_snr; + } else { + // Slope correction is (16/15)*pkt_rssi, + // this estimation looses one floating point + // operation, and should be precise enough. + pkt_rssi = (int)(1.066 * pkt_rssi); + } + return pkt_rssi; +} + int ISR_VECT sx127x::packetRssi() { - int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET; - int pkt_snr = packetSnr(); + int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET; + int pkt_snr = packetSnr(); - if (_frequency < 820E6) pkt_rssi -= 7; + 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; + 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); -} +uint8_t ISR_VECT sx127x::packetSnrRaw() { return readRegister(REG_PKT_SNR_VALUE_7X); } -float ISR_VECT sx127x::packetSnr() { - return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; -} +float ISR_VECT sx127x::packetSnr() { return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; } long sx127x::packetFrequencyError() { int32_t freqError = 0; @@ -271,17 +276,13 @@ long sx127x::packetFrequencyError() { size_t sx127x::write(uint8_t byte) { return write(&byte, sizeof(byte)); } size_t sx127x::write(const uint8_t *buffer, size_t size) { - int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X); - if ((currentLength + size) > MAX_PKT_LENGTH) { - size = MAX_PKT_LENGTH - currentLength; - } + int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X); + if ((currentLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - currentLength; } - for (size_t i = 0; i < size; i++) { - writeRegister(REG_FIFO_7X, buffer[i]); - } + for (size_t i = 0; i < size; i++) { writeRegister(REG_FIFO_7X, buffer[i]); } + writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size); - writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size); - return size; + return size; } int ISR_VECT sx127x::available() { return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); } @@ -446,19 +447,23 @@ void sx127x::setCodingRate4(int denominator) { writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1)); } -void sx127x::setPreambleLength(long length) { +void sx127x::setPreambleLength(long preamble_symbols) { + long length = preamble_symbols - 4; writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8)); writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0)); } +extern bool lora_low_datarate; void sx127x::handleLowDataRate() { int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4); if ( long( (1< 16) { // Set auto AGC and LowDataRateOptimize writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2)); + lora_low_datarate = true; } else { // Only set auto AGC writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2)); + lora_low_datarate = false; } } diff --git a/sx127x.h b/sx127x.h index f154b44..7639857 100644 --- a/sx127x.h +++ b/sx127x.h @@ -1,9 +1,6 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - #ifndef SX1276_H #define SX1276_H @@ -21,6 +18,11 @@ #define RSSI_OFFSET 157 +// Modem status flags +#define SIG_DETECT 0x01 +#define SIG_SYNCED 0x02 +#define RX_ONGOING 0x04 + class sx127x : public Stream { public: sx127x(); @@ -33,6 +35,7 @@ public: int parsePacket(int size = 0); int packetRssi(); + int packetRssi(uint8_t pkt_snr_raw); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); @@ -65,9 +68,9 @@ public: long getSignalBandwidth(); void setSignalBandwidth(long sbw); void setCodingRate4(int denominator); - void setPreambleLength(long length); + void setPreambleLength(long preamble_symbols); void setSyncWord(uint8_t sw); - uint8_t modemStatus(); + bool dcd(); void enableCrc(); void disableCrc(); void enableTCXO(); diff --git a/sx128x.cpp b/sx128x.cpp index e61ffb0..71ddac6 100644 --- a/sx128x.cpp +++ b/sx128x.cpp @@ -1,12 +1,11 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - -#include "sx128x.h" #include "Boards.h" +#if MODEM == SX1280 +#include "sx128x.h" + #define MCU_1284P 0x91 #define MCU_2560 0x92 #define MCU_ESP32 0x81 @@ -31,31 +30,29 @@ #if MCU_VARIANT == MCU_ESP32 #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) - #include "soc/rtc_wdt.h" + #include "hal/wdt_hal.h" #endif #define ISR_VECT IRAM_ATTR #else #define ISR_VECT #endif +// SX128x registers #define OP_RF_FREQ_8X 0x86 #define OP_SLEEP_8X 0x84 #define OP_STANDBY_8X 0x80 #define OP_TX_8X 0x83 #define OP_RX_8X 0x82 -#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as - // preamble detection, etc for - // knowing when it's safe to switch - // antenna modes +#define OP_SET_IRQ_FLAGS_8X 0x8D #define OP_CLEAR_IRQ_STATUS_8X 0x97 #define OP_GET_IRQ_STATUS_8X 0x15 #define OP_RX_BUFFER_STATUS_8X 0x17 -#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet +#define OP_PACKET_STATUS_8X 0x1D #define OP_CURRENT_RSSI_8X 0x1F -#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc. -#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc. +#define OP_MODULATION_PARAMS_8X 0x8B +#define OP_PACKET_PARAMS_8X 0x8C #define OP_STATUS_8X 0xC0 -#define OP_TX_PARAMS_8X 0x8E // set dbm, etc +#define OP_TX_PARAMS_8X 0x8E #define OP_PACKET_TYPE_8X 0x8A #define OP_BUFFER_BASE_ADDR_8X 0x8F #define OP_READ_REGISTER_8X 0x19 @@ -72,9 +69,9 @@ #define OP_FIFO_READ_8X 0x1B #define IRQ_PREAMBLE_DET_MASK_8X 0x80 -#define REG_PACKET_SIZE 0x901 -#define REG_FIRM_VER_MSB 0x154 -#define REG_FIRM_VER_LSB 0x153 +#define REG_PACKET_SIZE 0x901 +#define REG_FIRM_VER_MSB 0x154 +#define REG_FIRM_VER_LSB 0x153 #define XTAL_FREQ_8X (double)52000000 #define FREQ_DIV_8X (double)pow(2.0, 18.0) @@ -91,210 +88,160 @@ extern SPIClass SPI; sx128x::sx128x() : _spiSettings(8E6, MSBFIRST, SPI_MODE0), - _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN), - _frequency(0), - _txp(0), - _sf(0x50), - _bw(0x34), - _cr(0x01), - _packetIndex(0), - _preambleLength(18), - _implicitHeaderMode(0), - _payloadLength(255), - _crcMode(0), - _fifo_tx_addr_ptr(0), - _fifo_rx_addr_ptr(0), - _packet({0}), - _rxPacketLength(0), - _preinit_done(false), - _onReceive(NULL) -{ - // overide Stream timeout value - setTimeout(0); + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(pin_rxen), _busy(LORA_DEFAULT_BUSY_PIN), _txen(pin_txen), + _frequency(0), _txp(0), _sf(0x05), _bw(0x34), _cr(0x01), _packetIndex(0), _implicitHeaderMode(0), _payloadLength(255), _crcMode(0), _fifo_tx_addr_ptr(0), + _fifo_rx_addr_ptr(0), _rxPacketLength(0), _preinit_done(false), _tcxo(false) { setTimeout(0); } + +bool ISR_VECT sx128x::getPacketValidity() { + uint8_t buf[2]; + buf[0] = 0x00; + buf[1] = 0x00; + executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2); + if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { return true; } + else { return false; } +} + +void ISR_VECT sx128x::onDio0Rise() { + BaseType_t int_status = taskENTER_CRITICAL_FROM_ISR(); + // On the SX1280, there is a bug which can cause the busy line + // to remain high if a high amount of packets are received when + // in continuous RX mode. This is documented as Errata 16.1 in + // the SX1280 datasheet v3.2 (page 149) + // Therefore, the modem is set into receive mode each time a packet is received. + if (sx128x_modem.getPacketValidity()) { sx128x_modem.receive(); sx128x_modem.handleDio0Rise(); } + else { sx128x_modem.receive(); } + + taskEXIT_CRITICAL_FROM_ISR(int_status); +} + +void sx128x::handleDio0Rise() { + _packetIndex = 0; + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); + + // If implicit header mode is enabled, use pre-set packet length as payload length instead. + // See SX1280 datasheet v3.2, page 92 + if (_implicitHeaderMode == 0x80) { _rxPacketLength = _payloadLength; } + else { _rxPacketLength = rxbuf[0]; } + + if (_receive_callback) { _receive_callback(_rxPacketLength); } } bool sx128x::preInit() { - // setup pins pinMode(_ss, OUTPUT); - // set SS high digitalWrite(_ss, HIGH); - SPI.begin(); + // TODO: Check if this change causes issues on any platforms + #if MCU_VARIANT == MCU_ESP32 + #if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_TDECK + SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); + #else + SPI.begin(); + #endif + #else + SPI.begin(); + #endif - // check version (retry for up to 2 seconds) + // Detect modem (retry for up to 500ms) long start = millis(); - uint8_t version_msb; uint8_t version_lsb; - - while (((millis() - start) < 2000) && (millis() >= start)) { - + while (((millis() - start) < 500) && (millis() >= start)) { version_msb = readRegister(REG_FIRM_VER_MSB); version_lsb = readRegister(REG_FIRM_VER_LSB); - - if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { - break; - } + if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { break; } delay(100); } - if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { - return false; - } + if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { return false; } _preinit_done = true; return true; } -uint8_t ISR_VECT sx128x::readRegister(uint16_t address) -{ - return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); -} +uint8_t ISR_VECT sx128x::readRegister(uint16_t address) { return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); } +void sx128x::writeRegister(uint16_t address, uint8_t value) { singleTransfer(OP_WRITE_REGISTER_8X, address, value); } -void sx128x::writeRegister(uint16_t address, uint8_t value) -{ - singleTransfer(OP_WRITE_REGISTER_8X, address, value); -} - -uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) -{ +uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) { waitOnBusy(); - uint8_t response; - digitalWrite(_ss, LOW); SPI.beginTransaction(_spiSettings); SPI.transfer(opcode); SPI.transfer((address & 0xFF00) >> 8); SPI.transfer(address & 0x00FF); - if (opcode == OP_READ_REGISTER_8X) { - SPI.transfer(0x00); - } + if (opcode == OP_READ_REGISTER_8X) { SPI.transfer(0x00); } response = SPI.transfer(value); SPI.endTransaction(); - digitalWrite(_ss, HIGH); return response; } -void sx128x::rxAntEnable() -{ - if (_txen != -1) { - digitalWrite(_txen, LOW); - } - if (_rxen != -1) { - digitalWrite(_rxen, HIGH); - } +void sx128x::rxAntEnable() { + if (_txen != -1) { digitalWrite(_txen, LOW); } + if (_rxen != -1) { digitalWrite(_rxen, HIGH); } } -void sx128x::txAntEnable() -{ - if (_txen != -1) { - digitalWrite(_txen, HIGH); - } - if (_rxen != -1) { - digitalWrite(_rxen, LOW); - } +void sx128x::txAntEnable() { + if (_txen != -1) { digitalWrite(_txen, HIGH); } + if (_rxen != -1) { digitalWrite(_rxen, LOW); } } void sx128x::loraMode() { - // enable lora mode on the SX1262 chip uint8_t mode = MODE_LONG_RANGE_MODE_8X; executeOpcode(OP_PACKET_TYPE_8X, &mode, 1); } void sx128x::waitOnBusy() { - unsigned long time = millis(); - if (_busy != -1) { - while (digitalRead(_busy) == HIGH) - { - if (millis() >= (time + 100)) { - break; - } - // do nothing - } - } + unsigned long time = millis(); + while (digitalRead(_busy) == HIGH) { + if (millis() >= (time + 100)) { break; } + } } -void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ +void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) { waitOnBusy(); - digitalWrite(_ss, LOW); - SPI.beginTransaction(_spiSettings); SPI.transfer(opcode); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - } - + for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); } SPI.endTransaction(); - digitalWrite(_ss, HIGH); } -void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ +void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) { waitOnBusy(); - digitalWrite(_ss, LOW); - SPI.beginTransaction(_spiSettings); SPI.transfer(opcode); SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - + for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } SPI.endTransaction(); - digitalWrite(_ss, HIGH); } -void sx128x::writeBuffer(const uint8_t* buffer, size_t size) -{ +void sx128x::writeBuffer(const uint8_t* buffer, size_t size) { waitOnBusy(); - digitalWrite(_ss, LOW); - SPI.beginTransaction(_spiSettings); SPI.transfer(OP_FIFO_WRITE_8X); SPI.transfer(_fifo_tx_addr_ptr); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - _fifo_tx_addr_ptr++; - } - + for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; } SPI.endTransaction(); - digitalWrite(_ss, HIGH); } -void sx128x::readBuffer(uint8_t* buffer, size_t size) -{ +void sx128x::readBuffer(uint8_t* buffer, size_t size) { waitOnBusy(); - digitalWrite(_ss, LOW); - SPI.beginTransaction(_spiSettings); SPI.transfer(OP_FIFO_READ_8X); SPI.transfer(_fifo_rx_addr_ptr); SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - + for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); } SPI.endTransaction(); - digitalWrite(_ss, HIGH); } @@ -302,76 +249,75 @@ void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) { // because there is no access to these registers on the sx1280, we have // to set all these parameters at once or not at all. uint8_t buf[3]; - - buf[0] = sf; + buf[0] = sf << 4; buf[1] = bw; - buf[2] = cr; + buf[2] = cr; executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3); - if (sf <= 6) { - writeRegister(0x925, 0x1E); - } else if (sf <= 8) { - writeRegister(0x925, 0x37); - } else if (sf >= 9) { - writeRegister(0x925, 0x32); - } + if (sf <= 6) { writeRegister(0x925, 0x1E); } + else if (sf <= 8) { writeRegister(0x925, 0x37); } + else if (sf >= 9) { writeRegister(0x925, 0x32); } writeRegister(0x093C, 0x1); } -void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) { - // because there is no access to these registers on the sx1280, we have - // to set all these parameters at once or not at all. - uint8_t buf[7]; - - // calculate exponent and mantissa values for modem - uint8_t e = 1; - uint8_t m = 1; - uint32_t preamblelen; - - for (e <= 15; e++;) { - for (m <= 15; m++;) { - preamblelen = m * (uint32_t(1) << e); - if (preamblelen >= preamble) break; +uint8_t preamble_e = 0; +uint8_t preamble_m = 0; +uint32_t last_me_result_target = 0; +extern long lora_preamble_symbols; +void sx128x::setPacketParams(uint32_t target_preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) { + if (last_me_result_target != target_preamble_symbols) { + // Calculate exponent and mantissa values for modem + if (target_preamble_symbols >= 0xF000) target_preamble_symbols = 0xF000; + uint32_t calculated_preamble_symbols; + uint8_t e = 1; + uint8_t m = 1; + while (e <= 15) { + while (m <= 15) { + calculated_preamble_symbols = m * (pow(2,e)); + if (calculated_preamble_symbols >= target_preamble_symbols-4) break; + m++; } - if (preamblelen >= preamble) break; + + if (calculated_preamble_symbols >= target_preamble_symbols-4) break; + m = 1; e++; + } + + last_me_result_target = target_preamble_symbols; + lora_preamble_symbols = calculated_preamble_symbols+4; + _preambleLength = lora_preamble_symbols; + + preamble_e = e; + preamble_m = m; } - buf[0] = (e << 4) | m; + uint8_t buf[7]; + buf[0] = (preamble_e << 4) | preamble_m; buf[1] = headermode; - buf[2] = length; + buf[2] = payload_length; buf[3] = crc; - // standard IQ setting (no inversion) - buf[4] = 0x40; - // unused params - buf[5] = 0x00; + buf[4] = 0x40; // Standard IQ setting (no inversion) + buf[5] = 0x00; // Unused params buf[6] = 0x00; executeOpcode(OP_PACKET_PARAMS_8X, buf, 7); } -int sx128x::begin(unsigned long frequency) -{ +void sx128x::reset() { if (_reset != -1) { pinMode(_reset, OUTPUT); - - // perform reset digitalWrite(_reset, LOW); delay(10); digitalWrite(_reset, HIGH); delay(10); } +} - if (_rxen != -1) { - pinMode(_rxen, OUTPUT); - } +int sx128x::begin(unsigned long frequency) { + reset(); - if (_txen != -1) { - pinMode(_txen, OUTPUT); - } - - if (_busy != -1) { - pinMode(_busy, INPUT); - } + if (_rxen != -1) { pinMode(_rxen, OUTPUT); } + if (_txen != -1) { pinMode(_txen, OUTPUT); } + if (_busy != -1) { pinMode(_busy, INPUT); } if (!_preinit_done) { if (!preInit()) { @@ -379,50 +325,39 @@ int sx128x::begin(unsigned long frequency) } } - idle(); + standby(); loraMode(); rxAntEnable(); - setFrequency(frequency); - // set LNA boost - // todo: implement this + // TODO: Implement LNA boost //writeRegister(REG_LNA, 0x96); setModulationParams(_sf, _bw, _cr); setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + setTxPower(_txp); - // set output power to 2 dBm - setTxPower(2); - - // set base addresses + // Set base addresses uint8_t basebuf[2] = {0}; executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2); + _radio_online = true; return 1; } -void sx128x::end() -{ - // put in sleep mode +void sx128x::end() { sleep(); - - // stop SPI SPI.end(); - + _bitrate = 0; + _radio_online = false; _preinit_done = false; } -int sx128x::beginPacket(int implicitHeader) -{ - // put in standby mode - idle(); +int sx128x::beginPacket(int implicitHeader) { + standby(); - if (implicitHeader) { - implicitHeaderMode(); - } else { - explicitHeaderMode(); - } + if (implicitHeader) { implicitHeaderMode(); } + else { explicitHeaderMode(); } _payloadLength = 0; _fifo_tx_addr_ptr = 0; @@ -431,65 +366,70 @@ int sx128x::beginPacket(int implicitHeader) return 1; } -int sx128x::endPacket() -{ +int sx128x::endPacket() { setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - txAntEnable(); - // put in single TX mode + // Put in single TX mode uint8_t timeout[3] = {0}; executeOpcode(OP_TX_8X, timeout, 3); uint8_t buf[2]; - buf[0] = 0x00; buf[1] = 0x00; - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); - // wait for TX done - while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) { + // Wait for TX done + bool timed_out = false; + uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS; + while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0)) { buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); yield(); } - // clear IRQ's + if (!(millis() < w_timeout)) { timed_out = true; } + // clear IRQ's uint8_t mask[2]; mask[0] = 0x00; mask[1] = IRQ_TX_DONE_MASK_8X; executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2); - return 1; + + if (timed_out) { return 0; } + else { return 1; } } -uint8_t sx128x::modemStatus() { - // imitate the register status from the sx1276 / 78 - uint8_t buf[2] = {0}; +unsigned long preamble_detected_at = 0; +extern long lora_preamble_time_ms; +extern long lora_header_time_ms; +bool false_preamble_detected = false; +bool sx128x::dcd() { + uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + uint32_t now = millis(); - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + bool header_detected = false; + bool carrier_detected = false; - uint8_t clearbuf[2] = {0}; + if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { header_detected = true; carrier_detected = true; } + else { header_detected = false; } - uint8_t byte = 0x00; - - if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) { - byte = byte | 0x01 | 0x04; - // clear register after reading - clearbuf[0] = 0xFF; + if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) { + carrier_detected = true; + if (preamble_detected_at == 0) { preamble_detected_at = now; } + if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) { + preamble_detected_at = 0; + if (!header_detected) { false_preamble_detected = true; } + uint8_t clearbuf[2] = {0}; clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X; + executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); } + } - if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { - byte = byte | 0x02 | 0x04; - // clear register after reading - clearbuf[1] = 0xFF; - } - - executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); - - return byte; + // TODO: Maybe there's a way of unlatching the RSSI + // status without re-activating receive mode? + if (false_preamble_detected) { sx128x_modem.receive(); false_preamble_detected = false; } + return carrier_detected; } @@ -512,8 +452,8 @@ uint8_t sx128x::packetRssiRaw() { return buf[0]; } -int ISR_VECT sx128x::packetRssi() { - // may need more calculations here +int ISR_VECT sx128x::packetRssi(uint8_t pkt_snr_raw) { + // TODO: May need more calculations here uint8_t buf[5] = {0}; executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); int pkt_rssi = -buf[0] / 2; @@ -528,44 +468,48 @@ uint8_t ISR_VECT sx128x::packetSnrRaw() { float ISR_VECT sx128x::packetSnr() { uint8_t buf[5] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3); + executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); return float(buf[1]) * 0.25; } -long sx128x::packetFrequencyError() -{ +long sx128x::packetFrequencyError() { + // TODO: Implement this, page 120 of sx1280 datasheet int32_t freqError = 0; - // todo: implement this, page 120 of sx1280 datasheet const float fError = 0.0; return static_cast(fError); } -size_t sx128x::write(uint8_t byte) -{ - return write(&byte, sizeof(byte)); -} - -size_t sx128x::write(const uint8_t *buffer, size_t size) -{ - if ((_payloadLength + size) > MAX_PKT_LENGTH) { - size = MAX_PKT_LENGTH - _payloadLength; - } - - // write data +void sx128x::flush() { } +int ISR_VECT sx128x::available() { return _rxPacketLength - _packetIndex; } +size_t sx128x::write(uint8_t byte) { return write(&byte, sizeof(byte)); } +size_t sx128x::write(const uint8_t *buffer, size_t size) { + if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; } writeBuffer(buffer, size); _payloadLength = _payloadLength + size; return size; } -int ISR_VECT sx128x::available() -{ - return _rxPacketLength - _packetIndex; -} +int ISR_VECT sx128x::read() { + if (!available()) { return -1; } -int ISR_VECT sx128x::read() -{ - if (!available()) { - return -1; + // If received new packet + if (_packetIndex == 0) { + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); + int size; + + // If implicit header mode is enabled, read packet length as payload length instead. + // See SX1280 datasheet v3.2, page 92 + if (_implicitHeaderMode == 0x80) { + size = _payloadLength; + } else { + size = rxbuf[0]; + } + + _fifo_rx_addr_ptr = rxbuf[1]; + if (size > 255) { size = 255; } + + readBuffer(_packet, size); } uint8_t byte = _packet[_packetIndex]; @@ -573,66 +517,66 @@ int ISR_VECT sx128x::read() return byte; } -int sx128x::peek() -{ - if (!available()) { - return -1; - } - +int sx128x::peek() { + if (!available()) { return -1; } uint8_t b = _packet[_packetIndex]; return b; } -void sx128x::flush() -{ -} -void sx128x::onReceive(void(*callback)(int)) -{ - _onReceive = callback; +void sx128x::onReceive(void(*callback)(int)) { + _receive_callback = callback; if (callback) { pinMode(_dio0, INPUT); - // set preamble and header detection irqs, plus dio0 mask - uint8_t buf[8]; + // Set preamble and header detection irqs, plus dio0 mask + uint8_t buf[8]; - // set irq masks, enable all - buf[0] = 0xFF; - buf[1] = 0xFF; + // Set irq masks, enable all + buf[0] = 0xFF; + buf[1] = 0xFF; - // set dio0 masks - buf[2] = 0x00; - buf[3] = IRQ_RX_DONE_MASK_8X; + // On the SX1280, no RxDone IRQ is generated if a packet is received with + // an invalid header, but the modem will be taken out of single RX mode. + // This can cause the modem to not receive packets until it is reset + // again. This is documented as Errata 16.2 in the SX1280 datasheet v3.2 + // (page 150) Below, the header error IRQ is mapped to dio0 so that the + // modem can be set into RX mode again on reception of a corrupted + // header. + // set dio0 masks + buf[2] = 0x00; + buf[3] = IRQ_RX_DONE_MASK_8X | IRQ_HEADER_ERROR_MASK_8X; - // set dio1 masks - buf[4] = 0x00; - buf[5] = 0x00; + // Set dio1 masks + buf[4] = 0x00; + buf[5] = 0x00; - // set dio2 masks - buf[6] = 0x00; - buf[7] = 0x00; + // Set dio2 masks + buf[6] = 0x00; + buf[7] = 0x00; + + executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); + + #ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); + #endif + + attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING); - executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); -//#ifdef SPI_HAS_NOTUSINGINTERRUPT -// SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); -//#endif - attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING); } else { detachInterrupt(digitalPinToInterrupt(_dio0)); -//#ifdef SPI_HAS_NOTUSINGINTERRUPT -// SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); -//#endif + #ifdef SPI_HAS_NOTUSINGINTERRUPT + _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0)); + #endif } } -void sx128x::receive(int size) -{ +void sx128x::receive(int size) { if (size > 0) { implicitHeaderMode(); - - // tell radio payload length - _rxPacketLength = size; + // Tell radio payload length + //_rxPacketLength = size; //_payloadLength = size; //setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } else { @@ -641,181 +585,23 @@ void sx128x::receive(int size) rxAntEnable(); - uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode - executeOpcode(OP_RX_8X, mode, 3); + // On the SX1280, there is a bug which can cause the busy line + // to remain high if a high amount of packets are received when + // in continuous RX mode. This is documented as Errata 16.1 in + // the SX1280 datasheet v3.2 (page 149) + // Therefore, the modem is set to single RX mode below instead. + + // uint8_t mode[3] = {0x03, 0xFF, 0xFF}; // Countinuous RX mode + uint8_t mode[3] = {0}; // single RX mode + executeOpcode(OP_RX_8X, mode, 3); } -void sx128x::idle() -{ - #if HAS_TCXO - // STDBY_XOSC - uint8_t byte = 0x01; - #else - // STDBY_RC - uint8_t byte = 0x00; - #endif - executeOpcode(OP_STANDBY_8X, &byte, 1); +void sx128x::standby() { + uint8_t byte = 0x01; // Always use STDBY_XOSC + executeOpcode(OP_STANDBY_8X, &byte, 1); } -void sx128x::sleep() -{ - uint8_t byte = 0x00; - executeOpcode(OP_SLEEP_8X, &byte, 1); -} - -void sx128x::enableTCXO() { - // todo: need to check how to implement on sx1280 -} - -void sx128x::disableTCXO() { - // todo: need to check how to implement on sx1280 -} - -void sx128x::setTxPower(int level, int outputPin) { - if (level > 13) { - level = 13; - } else if (level < -18) { - level = -18; - } - - _txp = level; - - level = level + 18; - - uint8_t tx_buf[2]; - - tx_buf[0] = level; - tx_buf[1] = 0xE0; // ramping time - 20 microseconds - - executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); -} - -uint8_t sx128x::getTxPower() { - return _txp; -} - -void sx128x::setFrequency(unsigned long frequency) { - _frequency = frequency; - - uint8_t buf[3]; - - uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X); - - buf[0] = ((freq >> 16) & 0xFF); - buf[1] = ((freq >> 8) & 0xFF); - buf[2] = (freq & 0xFF); - - executeOpcode(OP_RF_FREQ_8X, buf, 3); -} - -uint32_t sx128x::getFrequency() { - // we can't read the frequency on the sx1280 - uint32_t frequency = _frequency; - - return frequency; -} - -void sx128x::setSpreadingFactor(int sf) -{ - if (sf < 5) { - sf = 5; - } else if (sf > 12) { - sf = 12; - } - - _sf = sf << 4; - - setModulationParams(sf << 4, _bw, _cr); - handleLowDataRate(); -} - -long sx128x::getSignalBandwidth() -{ - int bw = _bw; - switch (bw) { - case 0x34: return 203.125E3; - case 0x26: return 406.25E3; - case 0x18: return 812.5E3; - case 0x0A: return 1625E3; - } - - return 0; -} - -void sx128x::handleLowDataRate(){ - // todo: do i need this?? -} - -void sx128x::optimizeModemSensitivity(){ - // todo: check if there's anything the sx1280 can do here -} - -void sx128x::setSignalBandwidth(long sbw) -{ - if (sbw <= 203.125E3) { - _bw = 0x34; - } else if (sbw <= 406.25E3) { - _bw = 0x26; - } else if (sbw <= 812.5E3) { - _bw = 0x18; - } else { - _bw = 0x0A; - } - - setModulationParams(_sf, _bw, _cr); - - handleLowDataRate(); - optimizeModemSensitivity(); -} - -void sx128x::setCodingRate4(int denominator) -{ - if (denominator < 5) { - denominator = 5; - } else if (denominator > 8) { - denominator = 8; - } - - _cr = denominator - 4; - - // todo: add support for new interleaving scheme, see page 117 of sx1280 - // datasheet - - // update cr values for sx1280's use - - setModulationParams(_sf, _bw, _cr); -} - -void sx128x::setPreambleLength(long length) -{ - _preambleLength = length; - setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); -} - -void sx128x::setSyncWord(int sw) -{ - // not implemented -} - -void sx128x::enableCrc() -{ - _crcMode = 0x20; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); -} - -void sx128x::disableCrc() -{ - _crcMode = 0; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); -} - -byte sx128x::random() -{ - // todo: implement -} - -void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) -{ +void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) { _ss = ss; _reset = reset; _dio0 = dio0; @@ -824,66 +610,278 @@ void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) _txen = txen; } -void sx128x::setSPIFrequency(uint32_t frequency) -{ - _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +void sx128x::setTxPower(int level, int outputPin) { + uint8_t tx_buf[2]; + + // RAK4631 with WisBlock SX1280 module (LIBSYS002) + #if BOARD_VARIANT == MODEL_13 || BOARD_VARIANT == MODEL_21 + if (level > 27) { level = 27; } + else if (level < 0) { level = 0; } + + _txp = level; + int reg_value; + switch (level) { + case 0: + reg_value = -18; + break; + case 1: + reg_value = -16; + break; + case 2: + reg_value = -15; + break; + case 3: + reg_value = -14; + break; + case 4: + reg_value = -13; + break; + case 5: + reg_value = -12; + break; + case 6: + reg_value = -11; + break; + case 7: + reg_value = -9; + break; + case 8: + reg_value = -8; + break; + case 9: + reg_value = -7; + break; + case 10: + reg_value = -6; + break; + case 11: + reg_value = -5; + break; + case 12: + reg_value = -4; + break; + case 13: + reg_value = -3; + break; + case 14: + reg_value = -2; + break; + case 15: + reg_value = -1; + break; + case 16: + reg_value = 0; + break; + case 17: + reg_value = 1; + break; + case 18: + reg_value = 2; + break; + case 19: + reg_value = 3; + break; + case 20: + reg_value = 4; + break; + case 21: + reg_value = 5; + break; + case 22: + reg_value = 6; + break; + case 23: + reg_value = 7; + break; + case 24: + reg_value = 8; + break; + case 25: + reg_value = 9; + break; + case 26: + reg_value = 12; + break; + case 27: + reg_value = 13; + break; + default: + reg_value = 0; + break; + } + + tx_buf[0] = reg_value + 18; + tx_buf[1] = 0xE0; // Ramping time, 20 microseconds + executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); + + // T3S3 SX1280 PA + #elif BOARD_VARIANT == MODEL_AC + if (level > 20) { level = 20; } + else if (level < 0) { level = 0; } + + _txp = level; + int reg_value; + switch (level) { + case 0: + reg_value = -18; + break; + case 1: + reg_value = -17; + break; + case 2: + reg_value = -16; + break; + case 3: + reg_value = -15; + break; + case 4: + reg_value = -14; + break; + case 5: + reg_value = -13; + break; + case 6: + reg_value = -12; + break; + case 7: + reg_value = -10; + break; + case 8: + reg_value = -9; + break; + case 9: + reg_value = -8; + break; + case 10: + reg_value = -7; + break; + case 11: + reg_value = -6; + break; + case 12: + reg_value = -5; + break; + case 13: + reg_value = -4; + break; + case 14: + reg_value = -3; + break; + case 15: + reg_value = -2; + break; + case 16: + reg_value = -1; + break; + case 17: + reg_value = 0; + break; + case 18: + reg_value = 1; + break; + case 19: + reg_value = 2; + break; + case 20: + reg_value = 3; + break; + default: + reg_value = 0; + break; + } + tx_buf[0] = reg_value; + tx_buf[1] = 0xE0; // Ramping time, 20 microseconds + + // For SX1280 boards with no specific PA requirements + #else + if (level > 13) { level = 13; } + else if (level < -18) { level = -18; } + _txp = level; + tx_buf[0] = level + 18; + tx_buf[1] = 0xE0; // Ramping time, 20 microseconds + #endif + + executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); } -void sx128x::dumpRegisters(Stream& out) -{ - for (int i = 0; i < 128; i++) { - out.print("0x"); - out.print(i, HEX); - out.print(": 0x"); - out.println(readRegister(i), HEX); +void sx128x::setFrequency(uint32_t frequency) { + _frequency = frequency; + uint8_t buf[3]; + uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X); + buf[0] = ((freq >> 16) & 0xFF); + buf[1] = ((freq >> 8) & 0xFF); + buf[2] = (freq & 0xFF); + + executeOpcode(OP_RF_FREQ_8X, buf, 3); +} + +uint32_t sx128x::getFrequency() { + // We can't read the frequency on the sx1280 + uint32_t frequency = _frequency; + return frequency; +} + +void sx128x::setSpreadingFactor(int sf) { + if (sf < 5) { sf = 5; } + else if (sf > 12) { sf = 12; } + _sf = sf; + + setModulationParams(sf, _bw, _cr); + handleLowDataRate(); +} + +uint32_t sx128x::getSignalBandwidth() { + int bw = _bw; + switch (bw) { + case 0x34: return 203.125E3; + case 0x26: return 406.25E3; + case 0x18: return 812.5E3; + case 0x0A: return 1625E3; } + + return 0; } -void sx128x::explicitHeaderMode() -{ - _implicitHeaderMode = 0; +void sx128x::setSignalBandwidth(uint32_t sbw) { + if (sbw <= 203.125E3) { _bw = 0x34; } + else if (sbw <= 406.25E3) { _bw = 0x26; } + else if (sbw <= 812.5E3) { _bw = 0x18; } + else { _bw = 0x0A; } - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + setModulationParams(_sf, _bw, _cr); + handleLowDataRate(); + optimizeModemSensitivity(); } -void sx128x::implicitHeaderMode() -{ - _implicitHeaderMode = 0x80; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +// TODO: add support for new interleaving scheme, see page 117 of sx1280 datasheet +void sx128x::setCodingRate4(int denominator) { + if (denominator < 5) { denominator = 5; } + else if (denominator > 8) { denominator = 8; } + _cr = denominator - 4; + setModulationParams(_sf, _bw, _cr); } - -void ISR_VECT sx128x::handleDio0Rise() -{ - uint8_t buf[2]; - - buf[0] = 0x00; - buf[1] = 0x00; - - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); - - executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2); - - if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { - // received a packet - _packetIndex = 0; - - uint8_t rxbuf[2] = {0}; - executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); - _rxPacketLength = rxbuf[0]; - _fifo_rx_addr_ptr = rxbuf[1]; - readBuffer(_packet, _rxPacketLength); - - if (_onReceive) { - _onReceive(_rxPacketLength); - } - - } +extern bool lora_low_datarate; +void sx128x::handleLowDataRate() { + if (_sf > 10) { lora_low_datarate = true; } + else { lora_low_datarate = false; } } -void ISR_VECT sx128x::onDio0Rise() -{ - sx128x_modem.handleDio0Rise(); -} +void sx128x::optimizeModemSensitivity() { } // TODO: Check if there's anything the sx1280 can do here +uint8_t sx128x::getCodingRate4() { return _cr + 4; } +void sx128x::setPreambleLength(long preamble_symbols) { setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx128x::setSyncWord(int sw) { } // TODO: Implement +void sx128x::enableTCXO() { } // TODO: Need to check how to implement on sx1280 +void sx128x::disableTCXO() { } // TODO: Need to check how to implement on sx1280 +void sx128x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_8X, &byte, 1); } +uint8_t sx128x::getTxPower() { return _txp; } +uint8_t sx128x::getSpreadingFactor() { return _sf; } +void sx128x::enableCrc() { _crcMode = 0x20; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx128x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx128x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } +void sx128x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx128x::implicitHeaderMode() { _implicitHeaderMode = 0x80; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); } +void sx128x::dumpRegisters(Stream& out) { for (int i = 0; i < 128; i++) { out.print("0x"); out.print(i, HEX); out.print(": 0x"); out.println(readRegister(i), HEX); } } sx128x sx128x_modem; +#endif \ No newline at end of file diff --git a/sx128x.h b/sx128x.h index 213fc48..ccaeb04 100644 --- a/sx128x.h +++ b/sx128x.h @@ -1,9 +1,6 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. +// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva. // Licensed under the MIT license. -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - #ifndef SX128X_H #define SX128X_H @@ -11,17 +8,16 @@ #include #include "Modem.h" -#define LORA_DEFAULT_SS_PIN 10 -#define LORA_DEFAULT_RESET_PIN 9 -#define LORA_DEFAULT_DIO0_PIN 2 +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 #define LORA_DEFAULT_RXEN_PIN -1 #define LORA_DEFAULT_TXEN_PIN -1 #define LORA_DEFAULT_BUSY_PIN -1 - -#define PA_OUTPUT_RFO_PIN 0 -#define PA_OUTPUT_PA_BOOST_PIN 1 - -#define RSSI_OFFSET 157 +#define LORA_MODEM_TIMEOUT_MS 15E3 +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 +#define RSSI_OFFSET 157 class sx128x : public Stream { public: @@ -29,12 +25,14 @@ public: int begin(unsigned long frequency); void end(); + void reset(); int beginPacket(int implicitHeader = false); int endPacket(); int parsePacket(int size = 0); int packetRssi(); + int packetRssi(uint8_t pkt_snr_raw); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); @@ -55,21 +53,24 @@ public: void onReceive(void(*callback)(int)); void receive(int size = 0); - void idle(); + void standby(); void sleep(); bool preInit(); uint8_t getTxPower(); void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); uint32_t getFrequency(); - void setFrequency(unsigned long frequency); + void setFrequency(uint32_t frequency); void setSpreadingFactor(int sf); - long getSignalBandwidth(); - void setSignalBandwidth(long sbw); + uint8_t getSpreadingFactor(); + uint32_t getSignalBandwidth(); + void setSignalBandwidth(uint32_t sbw); void setCodingRate4(int denominator); - void setPreambleLength(long length); + uint8_t getCodingRate4(); + void setPreambleLength(long preamble_symbols); void setSyncWord(int sw); - uint8_t modemStatus(); + bool dcd(); + void clearIRQStatus(); void enableCrc(); void disableCrc(); void enableTCXO(); @@ -83,15 +84,12 @@ public: void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); void writeBuffer(const uint8_t* buffer, size_t size); void readBuffer(uint8_t* buffer, size_t size); - void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); + void setPacketParams(uint32_t target_preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); - // deprecated void crc() { enableCrc(); } void noCrc() { disableCrc(); } - byte random(); - void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN); void setSPIFrequency(uint32_t frequency); @@ -101,6 +99,7 @@ private: void explicitHeaderMode(); void implicitHeaderMode(); + bool getPacketValidity(); void handleDio0Rise(); uint8_t readRegister(uint16_t address); @@ -135,8 +134,11 @@ private: int _fifo_rx_addr_ptr; uint8_t _packet[256]; bool _preinit_done; + bool _tcxo; + bool _radio_online; int _rxPacketLength; - void (*_onReceive)(int); + uint32_t _bitrate; + void (*_receive_callback)(int); }; extern sx128x sx128x_modem;