diff --git a/docs/faq.md b/docs/faq.md index c61c8d2b..1c67c9c7 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -64,9 +64,13 @@ author: https://github.com/LitBomb - [6.3. Q: How to connect to a repeater via BLE (Bluetooth)?](#63-q-how-to-connect-to-a-repeater-via-ble-bluetooth) - [6.4. Q: I can't connect via Bluetooth, what is the Bluetooth pairing code?](#64-q-i-cant-connect-via-bluetooth-what-is-the-bluetooth-pairing-code) - [6.5. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.](#65-q-my-heltec-v3-keeps-disconnecting-from-my-smartphone--it-cant-hold-a-solid-bluetooth-connection) + - [6.6. Q: My RAK/T1000-E/xiao\_nRF52 device seems to be corrupted, how do I wipe it clean to start fresh?](#66-q-my-rakt1000-exiao_nrf52-device-seems-to-be-corrupted-how-do-i-wipe-it-clean-to-start-fresh) + - [6.7. Q: WebFlasher fails on Linux with failed to open](#67-q-webflasher-fails-on-Linux-with-failed-to-open) + - [7. Other Questions:](#7-other-questions) - - [7.2 Q: How to update ESP32-based devices over the air?](#72-q-how-to-update-esp32-based-devices-over-the-air) - [7.1 Q: How to update nRF (RAK, T114, Seed XIAO) repeater and room server firmware over the air using the new simpler DFU app?](#71-q-how-to-update-nrf-rak-t114-seed-xiao-repeater-and-room-server-firmware-over-the-air-using-the-new-simpler-dfu-app) + - [7.2 Q: How to update ESP32-based devices over the air?](#72-q-how-to-update-esp32-based-devices-over-the-air) + - [7.3 Q: Is there a way to lower the chance of a failed OTA device firmware update (DFU)?](#73-q-is-there-a-way-to-lower-the-chance-of-a-failed-ota-device-firmware-update-dfu) ## 1. Introduction @@ -533,9 +537,57 @@ You can get the epoch time on and use it to se **A:** Heltec V3 has a very small coil antenna on its PCB for Wi-Fi and Bluetooth connectivity. It has a very short range, only a few feet. It is possible to remove the coil antenna and replace it with a 31mm wire. The BT range is much improved with the modification. +### 6.6. Q: My RAK/T1000-E/xiao_nRF52 device seems to be corrupted, how do I wipe it clean to start fresh? + +**A:** +1. Connect USB-C cable to your device, per your device's instruction, get it to flash mode: + - For RAK, double click its reset button + - For T1000-e, quickly disconnect and reconnect the magnetic side of the cable from the device TWICE + - For Heltec T114, click the reset button once (the bottom button) + - For Xiao nRF52, click the reset button once. If that doesn't work, quickly double click the reset button twice. If that doesn't work, disconnection the board from your PC and reconnect again ([seeed studio wiki](https://wiki.seeedstudio.com/XIAO_BLE/#access-the-swd-pins-for-debugging-and-reflashing-bootloader)) +5. A new folder will appear on your computer's desktop +6. Download the `flash_erase*.uf2` file for your device on flasher.meshcore.co.uk + - RAK WisBlock and Heltec T114: `Flash_erase-nRF32_softdevice_v6.uf2` + - Seeed Studio Xiao nRF52 WIO: `Flash_erase-nRF52_softdevice_v7.uf2` +8. drag and drop the uf2 file for your device to the root of the new folder +9. Wait for the copy to complete. You might get an error dialog, you can ignore it +10. Go to https://flasher.meshcore.co.uk/, click `Console` and select the serial port for your connected device +11. In the console, press enter. Your flash should now be erased +12. You may now flash the latest MeshCore firmware onto your device + +Separately, starting in firmware version 1.7.0, there is a CLI Rescue mode. If your device has a user button (e.g. some RAK, T114), you can activate the rescue mode by hold down the user button of the device within 8 seconds of boot. Then you can use the 'Console' on flasher.meshcore.co.uk + + +### 6.7. Q: WebFlasher fails on Linux with failed to open + +**A:** If the usb port doesn't have the right ownership for this task, the process fails with the following error: +`NetworkError: Failed to execute 'open' on 'SerialPort': Failed to open serial port.` + +Allow the browser user on it: +`# setfacl -m u:YOUR_USER_HERE:rw /dev/ttyUSB0` + --- ## 7. Other Questions: +### 7.1 Q: How to update nRF (RAK, T114, Seed XIAO) repeater and room server firmware over the air using the new simpler DFU app? + +**A:** The steps below work on both Android and iOS as nRF has made both apps' user interface the same on both platforms: + +1. Download nRF's DFU app from iOS App Store or Android's Play Store, you can find the app by searching for `nrf dfu`, the app's full name is `nRF Device Firmware Update` +2. On flasher.meshcore.co.uk, download the **ZIP** version of the firmware for your nRF device (e.g. RAK or Heltec T114 or Seeed Studio's Xiao) +3. From the MeshCore app, login remotely to the repeater you want to update with admin priviledge +4. Go to the Command Line tab, type `start ota` and hit enter. +5. you should see `OK` to confirm the repeater device is now in OTA mode +6. Run the DFU app,tab `Settings` on the top right corner +7. Enable `Packets receipt notifications`, and change `Number of Packets` to 10 for RAK, 8 for T114. 8 also works for RAK. +9. Select the firmware zip file you downloaded +10. Select the device you want to update. If the device you want to updat is not on the list, try enabling`OTA` on the device again +11. If the device is not found, enable `Force Scanning` in the DFU app +12. Tab the `Upload` to begin OTA update +13. If it fails, try turning off and on Bluetooth on your phone. If that doesn't work, try rebooting your phone. +14. Wait for the update to complete. It can take a few minutes. + + ### 7.2 Q: How to update ESP32-based devices over the air? **A:** For ESP32-based devices (e.g. Heltec V3): @@ -548,22 +600,19 @@ You can get the epoch time on and use it to se 8. From a browser, go to http://192.168.4.1/update and upload the non-merged bin from the flasher -### 7.1 Q: How to update nRF (RAK, T114, Seed XIAO) repeater and room server firmware over the air using the new simpler DFU app? +### 7.3 Q: Is there a way to lower the chance of a failed OTA device firmware update (DFU)? + +**A:** Yes, developer `che aporeps` has an enhanced OTA DFU bootloader for nRF52 based devices. With this bootloader, if it detects that the application firmware is invalid, it falls back to OTA DFU mode so you can attempt to flash again to recover. This bootloader has other changes to make the OTA DFU process more fault tolerant. + +Refer to https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX for the latest information. + +Currently, the following boards are supported: +- Nologo ProMicro +- Seeed Studio XIAO nRF52840 BLE +- Seeed Studio XIAO nRF52840 BLE SENSE +- RAK 4631 -**A:** The steps below work on both Android and iOS as nRF has made both apps' user interface the same on both platforms: -1. Download nRF's DFU app from iOS App Store or Android's Play Store, you can find the app by searching for `nrf dfu`, the app's full name is `nRF Device Firmware Update` -2. On flasher.meshcore.co.uk, download the **ZIP** version of the firmware for your nRF device (e.g. RAK or Heltec T114 or Seeed Studio's Xiao) -3. From the MeshCore app, login remotely to the repeater you want to update with admin priviledge -4. Go to the Command Line tab, type `start ota` and hit enter. -5. you should see `OK` to confirm the repeater device is now in OTA mode -6. Run the DFU app,tab `Settings` on the top right corner -7. Enable `Packets receipt notifications` and change `Number of Packets` to 10 for RAK, 8 for T114. 8 also works for RAK. -8. Select the firmware zip file you downloaded -9. Select the device you want to update. If the device you want to updat is not on the list, try enabling`OTA` on the device again -10. Tab the `Upload` to begin OTA update -11. If it fails, try turning off and on Bluetooth on your phone. If that doesn't work, try rebooting your phone. -12. Wait for the update to complete. It can take a few minutes. --- diff --git a/docs/packet_structure.md b/docs/packet_structure.md new file mode 100644 index 00000000..4a28526f --- /dev/null +++ b/docs/packet_structure.md @@ -0,0 +1,54 @@ +# Packet Structure + +| Field | Size (bytes) | Description | +|----------|----------------------------------|-----------------------------------------------------------| +| header | 1 | Contains routing type, payload type, and payload version. | +| path_len | 1 | Length of the path field in bytes. | +| path | up to 64 (`MAX_PATH_SIZE`) | Stores the routing path if applicable. | +| payload | up to 184 (`MAX_PACKET_PAYLOAD`) | The actual data being transmitted. | + +Note: see the [payloads doc](./payloads.md) for more information about the content of payload. + +## Header Breakdown + +bit 0 means the lowest bit (1s place) + +| Bits | Mask | Field | Description | +|-------|--------|-----------------|-----------------------------------------------| +| 0-1 | `0x03` | Route Type | Flood, Direct, Reserved - see below. | +| 2-5 | `0x3C` | Payload Type | Request, Response, ACK, etc. - see below. | +| 6-7 | `0xC0` | Payload Version | Versioning of the payload format - see below. | + +## Route Type Values + +| Value | Name | Description | +|--------|-------------------------------|--------------------------------------| +| `0x00` | `ROUTE_TYPE_TRANSPORT_FLOOD` | Flood routing mode + transport codes | +| `0x01` | `ROUTE_TYPE_FLOOD` | Flood routing mode (builds up path). | +| `0x02` | `ROUTE_TYPE_DIRECT` | Direct route (path is supplied). | +| `0x03` | `ROUTE_TYPE_TRANSPORT_DIRECT` | direct route + transport codes | + +## Payload Type Values + +| Value | Name | Description | +|--------|---------------------------|-----------------------------------------------| +| `0x00` | `PAYLOAD_TYPE_REQ` | Request (destination/source hashes + MAC). | +| `0x01` | `PAYLOAD_TYPE_RESPONSE` | Response to REQ or ANON_REQ. | +| `0x02` | `PAYLOAD_TYPE_TXT_MSG` | Plain text message. | +| `0x03` | `PAYLOAD_TYPE_ACK` | Acknowledgment. | +| `0x04` | `PAYLOAD_TYPE_ADVERT` | Node advertisement. | +| `0x05` | `PAYLOAD_TYPE_GRP_TXT` | Group text message (unverified). | +| `0x06` | `PAYLOAD_TYPE_GRP_DATA` | Group datagram (unverified). | +| `0x07` | `PAYLOAD_TYPE_ANON_REQ` | Anonymous request. | +| `0x08` | `PAYLOAD_TYPE_PATH` | Returned path. | +| `0x09` | `PAYLOAD_TYPE_TRACE` | trace a path, collecting SNI for each hop. | +| `0x0F` | `PAYLOAD_TYPE_RAW_CUSTOM` | Custom packet (raw bytes, custom encryption). | + +## Payload Version Values + +| Value | Version | Description | +|--------|---------|---------------------------------------------------| +| `0x00` | 1 | 1-byte src/dest hashes, 2-byte MAC. | +| `0x01` | 2 | Future version (e.g., 2-byte hashes, 4-byte MAC). | +| `0x02` | 3 | Future version. | +| `0x03` | 4 | Future version. | diff --git a/docs/payloads.md b/docs/payloads.md index 2c402102..b094d9a9 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -1,25 +1,29 @@ # Meshcore payloads Inside of each [meshcore packet](./packet_structure.md) is a payload, identified by the payload type in the packet header. The types of payloads are: +* Node advertisement. +* Acknowledgment. +* Returned path. * Request (destination/source hashes + MAC). * Response to REQ or ANON_REQ. * Plain text message. -* Acknowledgment. -* Node advertisement. +* Anonymous request. * Group text message (unverified). * Group datagram (unverified). -* Anonymous request. -* Returned path. * Custom packet (raw bytes, custom encryption). This document defines the structure of each of these payload types +## Important concepts: + +* Node/channel hash: the first byte of the node or channel's public key + # Node advertisement This kind of payload notifies receivers that a node exists, and gives information about the node | Field | Size (bytes) | Description | |---------------|-----------------|----------------------------------------------------------| -| public key | 32 | Ed25519 public key | +| public key | 32 | Ed25519 public key of the node | | timestamp | 4 | unix timestamp of advertisement | | signature | 64 | Ed25519 signature of public key, timestamp, and app data | | appdata | rest of payload | optional, see below | @@ -37,20 +41,29 @@ Appdata Appdata Flags -| Value | Name | Description | -|--------|-----------|---------------------------------------| -| `0x10` | location | appdata contains lat/long information | -| `0x20` | feature 1 | Reserved for future use. | -| `0x40` | feature 2 | Reserved for future use. | -| `0x80` | name | appdata contains a node name | +| Value | Name | Description | +|--------|----------------|---------------------------------------| +| `0x01` | is chat node | advert is for a chat node | +| `0x02` | is repeater | advert is for a repeater | +| `0x03` | is room server | advert is for a room server | +| `0x10` | has location | appdata contains lat/long information | +| `0x20` | has feature 1 | Reserved for future use. | +| `0x40` | has feature 2 | Reserved for future use. | +| `0x80` | has name | appdata contains a node name | # Acknowledgement + +An acknowledgement that a message was received. Note that for returned path messages, an acknowledgement will be sent in the "extra" payload (see [Returned Path](#returned-path)) and not as a discrete ackowledgement. CLI commands do not require an acknowledgement, neither discrete nor extra. + | Field | Size (bytes) | Description | |----------|--------------|------------------------------------------------------------| | checksum | 4 | CRC checksum of message timestamp, text, and sender pubkey | # Returned path, request, response, and plain text message + +Returned path, request, response, and plain text messages are all formatted in the same way. See the subsection for more details about the ciphertext's associated plaintext representation. + | Field | Size (bytes) | Description | |------------------|-----------------|------------------------------------------------------| | destination hash | 1 | first byte of destination node public key | @@ -60,11 +73,13 @@ Appdata Flags ## Returned path +Returned path messages provide a description of the route a packet took from the original author. Receivers will send returned path messages to the author of the original message. + | Field | Size (bytes) | Description | |-------------|--------------|----------------------------------------------------------------------------------------------| | path length | 1 | length of next field | -| path | see above | a list of node hashes (one byte each) describing the route from us to the packet author | -| extra type | 1 | extra, bundled payload type, eg., acknowledgement or response. See packet structure spec | +| path | see above | a list of node hashes (one byte each) | +| extra type | 1 | extra, bundled payload type, eg., acknowledgement or response. Same values as in [packet structure](./packet_structure.md) | | extra | rest of data | extra, bundled payload content, follows same format as main content defined by this document | ## Request @@ -156,18 +171,14 @@ Plaintext message # Group text message / datagram -| Field | Size (bytes) | Description | -|--------------|-----------------|------------------------------------------| -| channel hash | 1 | TODO | -| cipher MAC | 2 | MAC for encrypted data in next field | -| ciphertext | rest of payload | encrypted message, see below for details | +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| channel hash | 1 | the first byte of the channel's public key | +| cipher MAC | 2 | MAC for encrypted data in next field | +| ciphertext | rest of payload | encrypted message, see below for details | -Plaintext for text message +The plaintext contained in the ciphertext matches the format described in [plain text message](#plain-text-message). Specifically, it consists of a four byte timestamp, a flags byte, and the message. The flags byte will generally be `0x00` because it is a "plain text message". The message will be of the form `: ` (eg., `user123: I'm on my way`). -| Field | Size (bytes) | Description | -|-----------|-----------------|----------------------------------| -| timestamp | 4 | send time (unix timestamp) | -| content | rest of message | plain group text message content | TODO: describe what datagram looks like diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 23223622..879200b7 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -613,10 +613,10 @@ void MyMesh::begin(bool has_display) { _prefs.cr = constrain(_prefs.cr, 5, 8); _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); -#ifdef BLE_PIN_CODE +#ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { #ifdef DISPLAY_CLASS - if (has_display) { + if (has_display && BLE_PIN_CODE == 123456) { StdRNG rng; _active_ble_pin = rng.nextInt(100000, 999999); // random pin each session } else { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index eeeff75f..247a9b3d 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -10,11 +10,11 @@ #define FIRMWARE_VER_CODE 6 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "7 Jun 2025" +#define FIRMWARE_BUILD_DATE "2 Jul 2025" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.7.0" +#define FIRMWARE_VERSION "v1.7.2" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 4edca23d..131dc496 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "7 Jun 2025" + #define FIRMWARE_BUILD_DATE "2 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.0" + #define FIRMWARE_VERSION "v1.7.2" #endif #ifndef LORA_FREQ diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index f5c1e9dc..c394f3e5 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -22,11 +22,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "7 Jun 2025" + #define FIRMWARE_BUILD_DATE "2 Jul 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.7.0" + #define FIRMWARE_VERSION "v1.7.2" #endif #ifndef LORA_FREQ diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a6b06c07..87f99878 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -135,7 +135,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { int k = 0; uint8_t path_len = data[k++]; uint8_t* path = &data[k]; k += path_len; - uint8_t extra_type = data[k++]; + uint8_t extra_type = data[k++] & 0x0F; // upper 4 bits reserved for future use uint8_t* extra = &data[k]; uint8_t extra_len = len - k; // remainder of packet (may be padded with zeroes!) if (onPeerPathRecv(pkt, j, secret, path, path_len, extra_type, extra, extra_len)) { diff --git a/src/helpers/CustomLLCC68.h b/src/helpers/CustomLLCC68.h index 52b37a0e..c239cc41 100644 --- a/src/helpers/CustomLLCC68.h +++ b/src/helpers/CustomLLCC68.h @@ -42,12 +42,12 @@ class CustomLLCC68 : public LLCC68 { if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif #endif - int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); tcxo = SX126X_DIO3_TCXO_VOLTAGE; - status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); } if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); diff --git a/src/helpers/CustomLR1110.h b/src/helpers/CustomLR1110.h index 3451aac1..d431dac1 100644 --- a/src/helpers/CustomLR1110.h +++ b/src/helpers/CustomLR1110.h @@ -9,6 +9,35 @@ class CustomLR1110 : public LR1110 { public: CustomLR1110(Module *mod) : LR1110(mod) { } + RadioLibTime_t getTimeOnAir(size_t len) override { + uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << this->spreadingFactor) / (this->bandwidthKhz * 10) ; + uint8_t sfCoeff1_x4 = 17; // (4.25 * 4) + uint8_t sfCoeff2 = 8; + if(this->spreadingFactor == 5 || this->spreadingFactor == 6) { + sfCoeff1_x4 = 25; // 6.25 * 4 + sfCoeff2 = 0; + } + uint8_t sfDivisor = 4*this->spreadingFactor; + if(symbolLength_us >= 16000) { + sfDivisor = 4*(this->spreadingFactor - 2); + } + const int8_t bitsPerCrc = 16; + const int8_t N_symbol_header = this->headerType == RADIOLIB_SX126X_LORA_HEADER_EXPLICIT ? 20 : 0; + + // numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8) + int16_t bitCount = (int16_t) 8 * len + this->crcTypeLoRa * bitsPerCrc - 4 * this->spreadingFactor + sfCoeff2 + N_symbol_header; + if(bitCount < 0) { + bitCount = 0; + } + // add (sfDivisor) - 1 to the numerator to give integer CEIL(...) + uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor); + + // preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit + uint32_t nSymbol_x4 = (this->preambleLengthLoRa + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * (this->codingRate + 4) * 4; + + return((symbolLength_us * nSymbol_x4) / 4); + } + bool isReceiving() { uint16_t irq = getIrqStatus(); bool detected = ((irq & LR1110_IRQ_HEADER_VALID) || (irq & LR1110_IRQ_HAS_PREAMBLE)); diff --git a/src/helpers/CustomLR1110Wrapper.h b/src/helpers/CustomLR1110Wrapper.h index 7e2ffa2d..947bb51d 100644 --- a/src/helpers/CustomLR1110Wrapper.h +++ b/src/helpers/CustomLR1110Wrapper.h @@ -17,7 +17,7 @@ public: void onSendFinished() override { RadioLibWrapper::onSendFinished(); - _radio->setPreambleLength(8); // overcomes weird issues with small and big pkts + _radio->setPreambleLength(16); // overcomes weird issues with small and big pkts } float getLastRSSI() const override { return ((CustomLR1110 *)_radio)->getRSSI(); } diff --git a/src/helpers/CustomSX1262.h b/src/helpers/CustomSX1262.h index 17673d3a..bfaea7c7 100644 --- a/src/helpers/CustomSX1262.h +++ b/src/helpers/CustomSX1262.h @@ -42,12 +42,12 @@ class CustomSX1262 : public SX1262 { if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif #endif - int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); tcxo = SX126X_DIO3_TCXO_VOLTAGE; - status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); } if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); diff --git a/src/helpers/CustomSX1268.h b/src/helpers/CustomSX1268.h index eb2d4f12..1e2e2620 100644 --- a/src/helpers/CustomSX1268.h +++ b/src/helpers/CustomSX1268.h @@ -42,12 +42,12 @@ class CustomSX1268 : public SX1268 { if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif #endif - int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); tcxo = SX126X_DIO3_TCXO_VOLTAGE; - status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); } if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); diff --git a/src/helpers/CustomSX1276.h b/src/helpers/CustomSX1276.h index 35d879fc..bee25274 100644 --- a/src/helpers/CustomSX1276.h +++ b/src/helpers/CustomSX1276.h @@ -12,10 +12,63 @@ class CustomSX1276 : public SX1276 { public: CustomSX1276(Module *mod) : SX1276(mod) { } + #ifdef RP2040_PLATFORM + bool std_init(SPIClassRP2040* spi = NULL) + #else + bool std_init(SPIClass* spi = NULL) + #endif + { + #ifdef LORA_CR + uint8_t cr = LORA_CR; + #else + uint8_t cr = 5; + #endif + + #if defined(P_LORA_SCLK) + #ifdef NRF52_PLATFORM + if (spi) { spi->setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); spi->begin(); } + #elif defined(RP2040_PLATFORM) + if (spi) { + spi->setMISO(P_LORA_MISO); + //spi->setCS(P_LORA_NSS); // Setting CS results in freeze + spi->setSCK(P_LORA_SCLK); + spi->setMOSI(P_LORA_MOSI); + spi->begin(); + } + #else + if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + #endif + #endif + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16); + // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + #ifdef SX127X_CURRENT_LIMIT + setCurrentLimit(SX127X_CURRENT_LIMIT); + #endif + + #if defined(SX176X_RXEN) || defined(SX176X_TXEN) + #ifndef SX176X_RXEN + #define SX176X_RXEN RADIOLIB_NC + #endif + #ifndef SX176X_TXEN + #define SX176X_TXEN RADIOLIB_NC + #endif + setRfSwitchPins(SX176X_RXEN, SX176X_TXEN); + #endif + + setCRC(1); + + return true; // success + } + bool isReceiving() { return (getModemStatus() & - (RH_RF95_MODEM_STATUS_SIGNAL_DETECTED - | RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED + (RH_RF95_MODEM_STATUS_SIGNAL_DETECTED + | RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED | RH_RF95_MODEM_STATUS_HEADER_INFO_VALID)) != 0; } @@ -23,7 +76,7 @@ class CustomSX1276 : public SX1276 { // start CAD int16_t state = startChannelScan(); RADIOLIB_ASSERT(state); - + // wait for channel activity detected or timeout unsigned long timeout = millis() + 16; while(!this->mod->hal->digitalRead(this->mod->getIrq()) && millis() < timeout) { diff --git a/src/helpers/HeltecV3Board.h b/src/helpers/HeltecV3Board.h index 57015a04..b71514cc 100644 --- a/src/helpers/HeltecV3Board.h +++ b/src/helpers/HeltecV3Board.h @@ -4,7 +4,7 @@ #include // LoRa radio module pins for Heltec V3 -// Also for Heltec Wireless Tracker +// Also for Heltec Wireless Tracker/Paper #define P_LORA_DIO_1 14 #define P_LORA_NSS 8 #define P_LORA_RESET RADIOLIB_NC @@ -14,7 +14,9 @@ #define P_LORA_MOSI 10 // built-ins -#define PIN_VBAT_READ 1 +#ifndef PIN_VBAT_READ // set in platformio.ini for boards like Heltec Wireless Paper (20) + #define PIN_VBAT_READ 1 +#endif #ifndef PIN_ADC_CTRL // set in platformio.ini for Heltec Wireless Tracker (2) #define PIN_ADC_CTRL 37 #endif diff --git a/src/helpers/MeshadventurerBoard.h b/src/helpers/MeshadventurerBoard.h new file mode 100644 index 00000000..65e11102 --- /dev/null +++ b/src/helpers/MeshadventurerBoard.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +// LoRa radio module pins for Meshadventurer +#define P_LORA_DIO_1 33 +#define P_LORA_NSS 18 +#define P_LORA_RESET 23 +#define P_LORA_BUSY 32 +#define P_LORA_SCLK 5 +#define P_LORA_MISO 19 +#define P_LORA_MOSI 27 + +#define PIN_VBAT_READ 35 + +#include "ESP32Board.h" + +#include + +class MeshadventurerBoard : public ESP32Board { + +public: + void begin() { + ESP32Board::begin(); + + esp_reset_reason_t reason = esp_reset_reason(); + if (reason == ESP_RST_DEEPSLEEP) { + long wakeup_source = esp_sleep_get_ext1_wakeup_status(); + if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) + startup_reason = BD_STARTUP_RX_PACKET; + } + + rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); + rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); + } + } + + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are held on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void powerOff() override { + // TODO: re-enable this when there is a definite wake-up source pin: + // enterDeepSleep(0); + } + + uint16_t getBattMilliVolts() override { + analogReadResolution(12); + + uint32_t raw = 0; + for (int i = 0; i < 4; i++) { + raw += analogReadMilliVolts(PIN_VBAT_READ); + } + raw = raw / 4; + + return (2 * raw); + } + + const char* getManufacturerName() const override { + return "Meshadventurer"; + } +}; diff --git a/src/helpers/rp2040/XiaoRP2040Board.cpp b/src/helpers/rp2040/XiaoRP2040Board.cpp new file mode 100644 index 00000000..bb439706 --- /dev/null +++ b/src/helpers/rp2040/XiaoRP2040Board.cpp @@ -0,0 +1,30 @@ +#include "XiaoRP2040Board.h" + +#include +#include + +void XiaoRP2040Board::begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + +#ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); +#endif + +#ifdef PIN_VBAT_READ + pinMode(PIN_VBAT_READ, INPUT); +#endif + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setSDA(PIN_BOARD_SDA); + Wire.setSCL(PIN_BOARD_SCL); +#endif + + Wire.begin(); + + delay(10); // give sx1262 some time to power up +} + +bool XiaoRP2040Board::startOTAUpdate(const char *id, char reply[]) { + return false; +} diff --git a/src/helpers/rp2040/XiaoRP2040Board.h b/src/helpers/rp2040/XiaoRP2040Board.h new file mode 100644 index 00000000..c9353906 --- /dev/null +++ b/src/helpers/rp2040/XiaoRP2040Board.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +// LoRa radio module pins for the Xiao RP2040 +// https://wiki.seeedstudio.com/XIAO-RP2040/ + +#define P_LORA_DIO_1 27 // D1 +#define P_LORA_NSS 6 // D4 +#define P_LORA_RESET 28 // D2 +#define P_LORA_BUSY 29 // D3 +#define P_LORA_TX_LED 17 + +#define SX126X_RXEN 7 // D5 +#define SX126X_TXEN -1 + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* + * This board has no built-in way to read battery voltage. + * Nevertheless it's very easy to make it work, you only require two 1% resistors. + * If your using the WIO SX1262 Addon for xaio, make sure you dont connect D0! + * + * BAT+ -----+ + * | + * VSYS --+ -/\/\/\/\- --+ + * 200k | + * +-- D0 + * | + * GND --+ -/\/\/\/\- --+ + * | 100k + * BAT- -----+ + */ +#define PIN_VBAT_READ 26 // D0 +#define BATTERY_SAMPLES 8 +#define ADC_MULTIPLIER (3.0f * 3.3f * 1000) + +class XiaoRP2040Board : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin(); + uint8_t getStartupReason() const override { return startup_reason; } + +#ifdef P_LORA_TX_LED + void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); } + void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); } +#endif + + + uint16_t getBattMilliVolts() override { +#if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) + analogReadResolution(12); + + uint32_t raw = 0; + for (int i = 0; i < BATTERY_SAMPLES; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / BATTERY_SAMPLES; + + return (ADC_MULTIPLIER * raw) / 4096; +#else + return 0; +#endif + } + + const char *getManufacturerName() const override { return "Xiao RP2040"; } + + void reboot() override { rp2040.reboot(); } + + bool startOTAUpdate(const char *id, char reply[]) override; +}; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index b3de0568..a424c46b 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -15,6 +15,24 @@ static Adafruit_AHTX0 AHTX0; static Adafruit_BME280 BME280; #endif +#if ENV_INCLUDE_BMP280 +#ifndef TELEM_BMP280_ADDRESS +#define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address +#endif +#define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level +#include +static Adafruit_BMP280 BMP280; +#endif + +#if ENV_INCLUDE_SHTC3 +#include +static Adafruit_SHTC3 SHTC3; +#endif + +#if ENV_INCLUDE_LPS22HB +#include +#endif + #if ENV_INCLUDE_INA3221 #define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address #define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts @@ -55,28 +73,59 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_BMP280 + if (BMP280.begin(TELEM_BMP280_ADDRESS)) { + MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESS); + MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); + BMP280_initialized = true; + } else { + BMP280_initialized = false; + MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESS); + } + #endif + + #if ENV_INCLUDE_SHTC3 + if (SHTC3.begin()) { + MESH_DEBUG_PRINTLN("Found sensor: SHTC3"); + SHTC3_initialized = true; + } else { + SHTC3_initialized = false; + MESH_DEBUG_PRINTLN("SHTC3 was not found at I2C address %02X", 0x70); + } + #endif + + #if ENV_INCLUDE_LPS22HB + if (BARO.begin()) { + MESH_DEBUG_PRINTLN("Found sensor: LPS22HB"); + LPS22HB_initialized = true; + } else { + LPS22HB_initialized = false; + MESH_DEBUG_PRINTLN("LPS22HB was not found at I2C address %02X", 0x5C); + } + #endif + #if ENV_INCLUDE_INA3221 if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) { - MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); - MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); + MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); + MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); - for(int i = 0; i < 3; i++) { - INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE); - } - INA3221_initialized = true; + for(int i = 0; i < 3; i++) { + INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE); + } + INA3221_initialized = true; } else { - INA3221_initialized = false; - MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS); + INA3221_initialized = false; + MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS); } #endif #if ENV_INCLUDE_INA219 if (INA219.begin(&Wire)) { - MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); - INA219_initialized = true; + MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); + INA219_initialized = true; } else { - INA219_initialized = false; - MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS); + INA219_initialized = false; + MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS); } #endif @@ -97,7 +146,7 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen sensors_event_t humidity, temp; AHTX0.getEvent(&humidity, &temp); telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); } #endif @@ -110,6 +159,31 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_BMP280 + if (BMP280_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP280.readTemperature()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()); + telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); + } + #endif + + #if ENV_INCLUDE_SHTC3 + if (SHTC3_initialized) { + sensors_event_t humidity, temp; + SHTC3.getEvent(&humidity, &temp); + + telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); + } + #endif + + #if ENV_INCLUDE_LPS22HB + if (LPS22HB_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BARO.readTemperature()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BARO.readPressure()); + } + #endif + #if ENV_INCLUDE_INA3221 if (INA3221_initialized) { for(int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) { @@ -128,10 +202,10 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen #if ENV_INCLUDE_INA219 if (INA219_initialized) { - telemetry.addVoltage(next_available_channel, INA219.getBusVoltage_V()); - telemetry.addCurrent(next_available_channel, INA219.getCurrent_mA() / 1000); - telemetry.addPower(next_available_channel, INA219.getPower_mW() / 1000); - next_available_channel++; + telemetry.addVoltage(next_available_channel, INA219.getBusVoltage_V()); + telemetry.addCurrent(next_available_channel, INA219.getCurrent_mA() / 1000); + telemetry.addPower(next_available_channel, INA219.getPower_mW() / 1000); + next_available_channel++; } #endif @@ -152,7 +226,7 @@ int EnvironmentSensorManager::getNumSettings() const { const char* EnvironmentSensorManager::getSettingName(int i) const { #if ENV_INCLUDE_GPS return (gps_detected && i == 0) ? "gps" : NULL; - #else + #else return NULL; #endif } @@ -184,7 +258,7 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val void EnvironmentSensorManager::initBasicGPS() { Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); - + #ifdef GPS_BAUD_RATE Serial1.begin(GPS_BAUD_RATE); #else @@ -200,7 +274,7 @@ void EnvironmentSensorManager::initBasicGPS() { #ifndef PIN_GPS_EN MESH_DEBUG_PRINTLN("No GPS wake/reset pin found for this board. Continuing on..."); #endif - + // Give GPS a moment to power up and send data delay(1000); @@ -226,7 +300,7 @@ void EnvironmentSensorManager::start_gps() { gps_active = true; #ifdef PIN_GPS_EN pinMode(PIN_GPS_EN, OUTPUT); - digitalWrite(PIN_GPS_EN, HIGH); + digitalWrite(PIN_GPS_EN, HIGH); return; #endif @@ -241,7 +315,7 @@ void EnvironmentSensorManager::stop_gps() { return; #endif - MESH_DEBUG_PRINTLN("Stop GPS is N/A on this board. Actual GPS state unchanged"); + MESH_DEBUG_PRINTLN("Stop GPS is N/A on this board. Actual GPS state unchanged"); } void EnvironmentSensorManager::loop() { diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index c9ec6870..f7804431 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -10,9 +10,12 @@ protected: bool AHTX0_initialized = false; bool BME280_initialized = false; + bool BMP280_initialized = false; bool INA3221_initialized = false; bool INA219_initialized = false; - + bool SHTC3_initialized = false; + bool LPS22HB_initialized = false; + bool gps_detected = false; bool gps_active = false; @@ -31,7 +34,7 @@ public: EnvironmentSensorManager(){}; #endif bool begin() override; - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS void loop() override; #endif diff --git a/src/helpers/ui/E213Display.cpp b/src/helpers/ui/E213Display.cpp new file mode 100644 index 00000000..92bf37fb --- /dev/null +++ b/src/helpers/ui/E213Display.cpp @@ -0,0 +1,116 @@ +#include "E213Display.h" + +#include "../../MeshCore.h" + +bool E213Display::begin() { + if (_init) return true; + + powerOn(); + display.begin(); + + // Set to landscape mode rotated 180 degrees + display.setRotation(3); + + _init = true; + _isOn = true; + + clear(); + display.fastmodeOn(); // Enable fast mode for quicker (partial) updates + + return true; +} + +void E213Display::powerOn() { +#ifdef PIN_VEXT_EN + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, LOW); // Active low + delay(50); // Allow power to stabilize +#endif +} + +void E213Display::powerOff() { +#ifdef PIN_VEXT_EN + digitalWrite(PIN_VEXT_EN, HIGH); // Turn off power +#endif +} + +void E213Display::turnOn() { + if (!_init) begin(); + powerOn(); + _isOn = true; +} + +void E213Display::turnOff() { + powerOff(); + _isOn = false; +} + +void E213Display::clear() { + display.clear(); +} + +void E213Display::startFrame(Color bkg) { + // Fill screen with white first to ensure clean background + display.fillRect(0, 0, width(), height(), WHITE); + if (bkg == LIGHT) { + // Fill with black if light background requested (inverted for e-ink) + display.fillRect(0, 0, width(), height(), BLACK); + } +} + +void E213Display::setTextSize(int sz) { + // The library handles text size internally + display.setTextSize(sz); +} + +void E213Display::setColor(Color c) { + // implemented in individual display methods +} + +void E213Display::setCursor(int x, int y) { + display.setCursor(x, y); +} + +void E213Display::print(const char *str) { + display.print(str); +} + +void E213Display::fillRect(int x, int y, int w, int h) { + display.fillRect(x, y, w, h, BLACK); +} + +void E213Display::drawRect(int x, int y, int w, int h) { + display.drawRect(x, y, w, h, BLACK); +} + +void E213Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) { + // Width in bytes for bitmap processing + uint16_t widthInBytes = (w + 7) / 8; + + // Process the bitmap row by row + for (int by = 0; by < h; by++) { + // Scan across the row bit by bit + for (int bx = 0; bx < w; bx++) { + // Get the current bit using MSB ordering (like GxEPDDisplay) + uint16_t byteOffset = (by * widthInBytes) + (bx / 8); + uint8_t bitMask = 0x80 >> (bx & 7); + bool bitSet = bits[byteOffset] & bitMask; + + // If the bit is set, draw the pixel + if (bitSet) { + display.drawPixel(x + bx, y + by, BLACK); + } + } + } +} + +uint16_t E213Display::getTextWidth(const char *str) { + int16_t x1, y1; + uint16_t w, h; + display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); + return w; +} + +void E213Display::endFrame() { + display.update(); +} diff --git a/src/helpers/ui/E213Display.h b/src/helpers/ui/E213Display.h new file mode 100644 index 00000000..330a2b6d --- /dev/null +++ b/src/helpers/ui/E213Display.h @@ -0,0 +1,37 @@ +#pragma once + +#include "DisplayDriver.h" + +#include +#include +#include + +// Display driver for E213 e-ink display +class E213Display : public DisplayDriver { + EInkDisplay_VisionMasterE213 display; + bool _init = false; + bool _isOn = false; + +public: + E213Display() : DisplayDriver(250, 122) {} + + bool begin(); + bool isOn() override { return _isOn; } + void turnOn() override; + void turnOff() override; + void clear() override; + void startFrame(Color bkg = DARK) override; + void setTextSize(int sz) override; + void setColor(Color c) override; + void setCursor(int x, int y) override; + void print(const char *str) override; + void fillRect(int x, int y, int w, int h) override; + void drawRect(int x, int y, int w, int h) override; + void drawXbm(int x, int y, const uint8_t *bits, int w, int h) override; + uint16_t getTextWidth(const char *str) override; + void endFrame() override; + +private: + void powerOn(); + void powerOff(); +}; \ No newline at end of file diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp index 042c7c0d..f41702c5 100644 --- a/variants/heltec_tracker/target.cpp +++ b/variants/heltec_tracker/target.cpp @@ -23,43 +23,16 @@ HWTSensorManager sensors = HWTSensorManager(nmea); DISPLAY_CLASS display(&board.periph_power); // peripheral power pin is shared #endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); -#ifdef SX126X_DIO3_TCXO_VOLTAGE - float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -#else - float tcxo = 1.6f; -#endif - #if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); -#endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - - radio.setCRC(1); - -#ifdef SX126X_CURRENT_LIMIT - radio.setCurrentLimit(SX126X_CURRENT_LIMIT); -#endif -#ifdef SX126X_DIO2_AS_RF_SWITCH - radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); -#endif -#ifdef SX126X_RX_BOOSTED_GAIN - radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); + return radio.std_init(&spi); +#else + return radio.std_init(); #endif - return true; // success } uint32_t radio_get_rng_seed() { diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 562b309d..43bc43ad 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -96,7 +96,7 @@ build_flags = -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 - -D BLE_PIN_CODE=0 + -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/heltec_v2/target.cpp b/variants/heltec_v2/target.cpp index a7e9fa67..418f1f7f 100644 --- a/variants/heltec_v2/target.cpp +++ b/variants/heltec_v2/target.cpp @@ -20,31 +20,15 @@ SensorManager sensors; DISPLAY_CLASS display; #endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); #if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); #endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - -#ifdef SX127X_CURRENT_LIMIT - radio.setCurrentLimit(SX127X_CURRENT_LIMIT); -#endif - - radio.setCRC(1); - - return true; // success } uint32_t radio_get_rng_seed() { diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 173be80c..704958d1 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -19,15 +19,16 @@ build_flags = -D SX126X_RX_BOOSTED_GAIN=1 -D ENV_INCLUDE_AHTX0=1 -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_BMP280=1 -D ENV_INCLUDE_INA3221=1 - -D ENV_INCLUDE_INA219=1 + -D ENV_INCLUDE_INA219=1 -D ENV_INCLUDE_GPS=1 - -D PIN_GPS_RX=45 - -D PIN_GPS_TX=46 - -D PIN_GPS_EN=-1 + -D PIN_GPS_RX=47 + -D PIN_GPS_TX=48 + -D PIN_GPS_EN=26 build_src_filter = ${esp32_base.build_src_filter} +<../variants/heltec_v3> - + + + lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 @@ -35,6 +36,7 @@ lib_deps = adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BMP280 Library@^2.6.8 stevemarple/MicroNMEA @ ^2.0.6 [env:Heltec_v3_repeater] @@ -115,7 +117,7 @@ build_flags = -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display - -D BLE_PIN_CODE=0 ; dynamic, random PIN + -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini new file mode 100644 index 00000000..513ba4b9 --- /dev/null +++ b/variants/heltec_wireless_paper/platformio.ini @@ -0,0 +1,84 @@ +[Heltec_Wireless_Paper_base] +extends = esp32_base +board = esp32-s3-devkitc-1 +build_flags = + ${esp32_base.build_flags} + -I variants/heltec_wireless_paper + -D HELTEC_WIRELESS_PAPER + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D P_LORA_TX_LED=18 + ; -D PIN_BOARD_SDA=17 + ; -D PIN_BOARD_SCL=18 + -D PIN_USER_BTN=0 + -D PIN_VEXT_EN=45 + -D PIN_VBAT_READ=20 + -D PIN_ADC_CTRL=19 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D DISP_CS=4 + -D DISP_BUSY=7 + -D DISP_DC=5 + -D DISP_RST=6 + -D DISP_SCLK=3 + -D DISP_MOSI=2 + -D ARDUINO_heltec_wifi_lora_32_V3 + -D WIRELESS_PAPER +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/heltec_wireless_paper> +lib_deps = + ${esp32_base.lib_deps} + todd-herbert/heltec-eink-modules @ 4.5.0 + +[env:Heltec_Wireless_Paper_companion_radio_ble] +extends = Heltec_Wireless_Paper_base +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D DISPLAY_CLASS=E213Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + + + +<../examples/companion_radio> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_Wireless_Paper_repeater] +extends = Heltec_Wireless_Paper_base +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -D DISPLAY_CLASS=E213Display + -D ADVERT_NAME='"Heltec WP Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + ${esp32_ota.lib_deps} + +[env:Heltec_Wireless_Paper_room_server] +extends = Heltec_Wireless_Paper_base +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -D DISPLAY_CLASS=E213Display + -D ADVERT_NAME='"Heltec WP Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + +<../examples/simple_room_server> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file diff --git a/variants/heltec_wireless_paper/target.cpp b/variants/heltec_wireless_paper/target.cpp new file mode 100644 index 00000000..65eaab04 --- /dev/null +++ b/variants/heltec_wireless_paper/target.cpp @@ -0,0 +1,45 @@ +#include "target.h" + +#include + +HeltecV3Board board; + +static SPIClass spi; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +SensorManager sensors; + +#ifdef DISPLAY_CLASS +DISPLAY_CLASS display; +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + return radio.std_init(&spi); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} \ No newline at end of file diff --git a/variants/heltec_wireless_paper/target.h b/variants/heltec_wireless_paper/target.h new file mode 100644 index 00000000..7d901c01 --- /dev/null +++ b/variants/heltec_wireless_paper/target.h @@ -0,0 +1,27 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS +#include +#endif + +extern HeltecV3Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +#ifdef DISPLAY_CLASS +extern DISPLAY_CLASS display; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); \ No newline at end of file diff --git a/variants/lilygo_t3s3/target.cpp b/variants/lilygo_t3s3/target.cpp index 7fa45e54..b7c4542c 100644 --- a/variants/lilygo_t3s3/target.cpp +++ b/variants/lilygo_t3s3/target.cpp @@ -3,13 +3,8 @@ ESP32Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - +static SPIClass spi; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; @@ -28,35 +23,7 @@ bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); -#ifdef SX126X_DIO3_TCXO_VOLTAGE - float tcxo = SX126X_DIO3_TCXO_VOLTAGE; -#else - float tcxo = 1.6f; -#endif - -#if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); -#endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - - radio.setCRC(1); - -#ifdef SX126X_CURRENT_LIMIT - radio.setCurrentLimit(SX126X_CURRENT_LIMIT); -#endif -#ifdef SX126X_DIO2_AS_RF_SWITCH - radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); -#endif -#ifdef SX126X_RX_BOOSTED_GAIN - radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); -#endif - - return true; // success + return radio.std_init(&spi); } uint32_t radio_get_rng_seed() { diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index 1bcbbbf9..74eced9c 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -21,6 +21,8 @@ build_flags = -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper -D SX127X_CURRENT_LIMIT=120 + -D SX176X_RXEN=21 + -D SX176X_TXEN=10 -D LORA_TX_POWER=20 build_src_filter = ${esp32_base.build_src_filter} +<../variants/lilygo_t3s3_sx1276> diff --git a/variants/lilygo_t3s3_sx1276/target.cpp b/variants/lilygo_t3s3_sx1276/target.cpp index db11433a..b2ee4455 100644 --- a/variants/lilygo_t3s3_sx1276/target.cpp +++ b/variants/lilygo_t3s3_sx1276/target.cpp @@ -20,33 +20,16 @@ SensorManager sensors; DISPLAY_CLASS display; #endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); #if defined(P_LORA_SCLK) spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); #endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - -#ifdef SX127X_CURRENT_LIMIT - radio.setCurrentLimit(SX127X_CURRENT_LIMIT); -#endif - - radio.setCRC(1); - - radio.setRfSwitchPins(21, 10); - - return true; // success } uint32_t radio_get_rng_seed() { diff --git a/variants/lilygo_tbeam_SX1276/target.cpp b/variants/lilygo_tbeam_SX1276/target.cpp index d340dedb..7e2537bb 100644 --- a/variants/lilygo_tbeam_SX1276/target.cpp +++ b/variants/lilygo_tbeam_SX1276/target.cpp @@ -27,31 +27,15 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display; #endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); #if defined(P_LORA_SCLK) - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); #endif - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - -#ifdef SX127X_CURRENT_LIMIT - radio.setCurrentLimit(SX127X_CURRENT_LIMIT); -#endif - - radio.setCRC(1); - - return true; // success } uint32_t radio_get_rng_seed() { diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index bd351c5a..a2169f1f 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -17,6 +17,8 @@ build_flags = -D P_LORA_MISO=19 ; SPI MISO -D P_LORA_MOSI=27 ; SPI MOSI -D P_LORA_TX_LED=2 ; LED pin for TX indication + -D PIN_BOARD_SDA=21 + -D PIN_BOARD_SCL=22 -D PIN_VBAT_READ=35 ; Battery voltage reading (analog pin) -D PIN_USER_BTN=0 -D ARDUINO_LOOP_STACK_SIZE=16384 @@ -25,11 +27,22 @@ build_flags = -D WRAPPER_CLASS=CustomSX1276Wrapper -D SX127X_CURRENT_LIMIT=120 -D LORA_TX_POWER=20 + -D ENV_INCLUDE_AHTX0=1 + -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_BMP280=1 + -D ENV_INCLUDE_INA3221=1 + -D ENV_INCLUDE_INA219=1 build_src_filter = ${esp32_base.build_src_filter} +<../variants/lilygo_tlora_v2_1> + + lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BMP280 Library @ ^2.6.8 ; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments === [env:LilyGo_TLora_V2_1_1_6_Repeater] diff --git a/variants/lilygo_tlora_v2_1/target.cpp b/variants/lilygo_tlora_v2_1/target.cpp index 4d513ed9..5e8f15b2 100644 --- a/variants/lilygo_tlora_v2_1/target.cpp +++ b/variants/lilygo_tlora_v2_1/target.cpp @@ -10,35 +10,21 @@ WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; #endif -#ifndef LORA_CR - #define LORA_CR 5 -#endif - bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); - spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8); - if (status != RADIOLIB_ERR_NONE) { - Serial.print("ERROR: radio init failed: "); - Serial.println(status); - return false; // fail - } - -#ifdef SX127X_CURRENT_LIMIT - radio.setCurrentLimit(SX127X_CURRENT_LIMIT); +#if defined(P_LORA_SCLK) + return radio.std_init(&spi); +#else + return radio.std_init(); #endif - - radio.setCRC(1); - - return true; // success } uint32_t radio_get_rng_seed() { diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index edf28eb4..8e48c3e7 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef DISPLAY_CLASS #include #endif @@ -14,7 +15,7 @@ extern LilyGoTLoraBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini new file mode 100644 index 00000000..f8995e18 --- /dev/null +++ b/variants/meshadventurer/platformio.ini @@ -0,0 +1,234 @@ +[Meshadventurer] +extends = esp32_base +board = esp32doit-devkit-v1 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit +build_flags = + ${esp32_base.build_flags} + -I variants/meshadventurer + -D MESHADVENTURER + -D P_LORA_TX_LED=2 + -D PIN_VBAT_READ=35 + -D PIN_USER_BTN_ANA=39 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_BUSY=32 + -D P_LORA_SCLK=5 + -D P_LORA_MOSI=27 + -D P_LORA_MISO=19 + -D SX126X_TXEN=13 + -D SX126X_RXEN=14 + -D PIN_BOARD_SDA=21 + -D PIN_BOARD_SCL=22 + -D SX126X_DIO2_AS_RF_SWITCH=false + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_GPS_RX=12 + -D PIN_GPS_TX=15 + -D DISPLAY_CLASS=SSD1306Display +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/meshadventurer> +lib_deps = + ${esp32_base.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Meshadventurer_sx1262_repeater] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_repeater> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"Meshadventurer Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} + +[env:Meshadventurer_sx1268_repeater] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_repeater> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"Meshadventurer Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} + +[env:Meshadventurer_sx1262_companion_radio_usb] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/companion_radio> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1262_companion_radio_ble] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/companion_radio> + + + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1262_terminal_chat] +extends = Meshadventurer +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> + + +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1262_room_server] +extends = Meshadventurer +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"Meshadventurer Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_room_server> + + +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} + +[env:Meshadventurer_sx1268_companion_radio_usb] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/companion_radio> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1268_companion_radio_ble] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/companion_radio> + + + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1268_terminal_chat] +extends = Meshadventurer +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> + + +lib_deps = + ${Meshadventurer.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Meshadventurer_sx1268_room_server] +extends = Meshadventurer +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"Meshadventurer Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Meshadventurer.build_src_filter} + +<../examples/simple_room_server> + + +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file diff --git a/variants/meshadventurer/target.cpp b/variants/meshadventurer/target.cpp new file mode 100644 index 00000000..43e171c0 --- /dev/null +++ b/variants/meshadventurer/target.cpp @@ -0,0 +1,152 @@ +#include +#include "target.h" + +#include + +MeshadventurerBoard board; + +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +MASensorManager sensors = MASensorManager(nmea); + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#ifdef SX126X_DIO3_TCXO_VOLTAGE + float tcxo = SX126X_DIO3_TCXO_VOLTAGE; +#else + float tcxo = 1.6f; +#endif + +#if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); +#endif + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + + radio.setCRC(1); + +#if defined(SX126X_RXEN) && defined(SX126X_TXEN) + radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); +#endif + +#ifdef SX126X_CURRENT_LIMIT + radio.setCurrentLimit(SX126X_CURRENT_LIMIT); +#endif +#ifdef SX126X_DIO2_AS_RF_SWITCH + radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); +#endif +#ifdef SX126X_RX_BOOSTED_GAIN + radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); +#endif + + return true; // success +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} + +void MASensorManager::start_gps() { + if(!gps_active) { + MESH_DEBUG_PRINTLN("starting GPS"); + gps_active = true; + } +} + +void MASensorManager::stop_gps() { + if(gps_active) { + MESH_DEBUG_PRINTLN("stopping GPS"); + gps_active = false; + } +} + +bool MASensorManager::begin() { + Serial1.setPins(PIN_GPS_RX, PIN_GPS_TX); + Serial1.begin(9600); + delay(500); + return true; +} + +bool MASensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if(requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void MASensorManager::loop() { + static long next_gps_update = 0; + _location->loop(); + if(millis() > next_gps_update && gps_active) { + if(_location->isValid()) { + node_lat = ((double)_location->getLatitude()) / 1000000.; + node_lon = ((double)_location->getLongitude()) / 1000000.; + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + next_gps_update = millis() + 1000; + } +} + +int MASensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) + +const char* MASensorManager::getSettingName(int i) const { + return i == 0 ? "gps" : NULL; +} +const char* MASensorManager::getSettingValue(int i) const { + if(i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} +bool MASensorManager::setSettingValue(const char* name, const char* value) { + if(strcmp(name, "gps") == 0) { + if(strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + return false; // not supported +} diff --git a/variants/meshadventurer/target.h b/variants/meshadventurer/target.h new file mode 100644 index 00000000..0e0235ba --- /dev/null +++ b/variants/meshadventurer/target.h @@ -0,0 +1,46 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include +#endif + +class MASensorManager : public SensorManager { + bool gps_active = false; + LocationProvider * _location; + + void start_gps(); + void stop_gps(); +public: + MASensorManager(LocationProvider &location): _location(&location) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; +}; + +extern MeshadventurerBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern MASensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/meshadventurer/variant.h b/variants/meshadventurer/variant.h new file mode 100644 index 00000000..356a3e17 --- /dev/null +++ b/variants/meshadventurer/variant.h @@ -0,0 +1,44 @@ +// For OLED LCD +#define I2C_SDA 21 +#define I2C_SCL 22 + +// For GPS, 'undef's not needed +#define GPS_TX_PIN 15 +#define GPS_RX_PIN 12 +#define PIN_GPS_EN 4 +#define GPS_POWER_TOGGLE // Moved definition from platformio.ini to here + +#define BUTTON_PIN 39 // The middle button GPIO on the T-Beam +#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_GPIO35_CHANNEL +#define ADC_MULTIPLIER 2 +#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards +#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975). +#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets) + +// Radio +#define USE_SX1262 // E22-900M30S uses SX1262 +#define USE_SX1268 // E22-400M30S uses SX1268 +#define SX126X_MAX_POWER 22 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA) +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V + +#define SX126X_CS 18 // EBYTE module's NSS pin +#define SX126X_SCK 5 // EBYTE module's SCK pin +#define SX126X_MOSI 27 // EBYTE module's MOSI pin +#define SX126X_MISO 19 // EBYTE module's MISO pin +#define SX126X_RESET 23 // EBYTE module's NRST pin +#define SX126X_BUSY 32 // EBYTE module's BUSY pin +#define SX126X_DIO1 33 // EBYTE module's DIO1 pin + +// The E22's TXEN pin is connected to MCU pin, E22's RXEN pin is connected to MCU pin (allows for ramping up PA before transmission +// Don't define DIO2_AS_RF_SWITCH because we only use DIO2 or an MCU pin mutually exclusively to connect to E22's TXEN (to prevent +// a short if they are both connected at the same time and there's a slight non-neglibible delay and/or voltage difference between +// DIO2 and TXEN). +#define SX126X_TXEN 13 // Schematic connects EBYTE module's TXEN pin to MCU +#define SX126X_RXEN 14 // Schematic connects EBYTE module's RXEN pin to MCU + +#define LORA_CS SX126X_CS // Compatibility with variant file configuration structure +#define LORA_SCK SX126X_SCK // Compatibility with variant file configuration structure +#define LORA_MOSI SX126X_MOSI // Compatibility with variant file configuration structure +#define LORA_MISO SX126X_MISO // Compatibility with variant file configuration structure +#define LORA_DIO1 SX126X_DIO1 // Compatibility with variant file configuration structure diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 20928bdf..c2bb1f23 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -36,7 +36,7 @@ build_flags = -I src/helpers/ui -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 - -D BLE_PIN_CODE=0 + -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index e7099d1e..246019c2 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -1,7 +1,7 @@ [Faketec] -extends = nrf52840_base +extends = nrf52_base board = promicro_nrf52840 -build_flags = ${nrf52840_base.build_flags} +build_flags = ${nrf52_base.build_flags} -I variants/promicro -D FAKETEC -D RADIO_CLASS=CustomSX1262 @@ -19,20 +19,22 @@ build_flags = ${nrf52840_base.build_flags} -D ENV_INCLUDE_GPS=1 -D ENV_INCLUDE_AHTX0=1 -D ENV_INCLUDE_BME280=1 + -D ENV_INCLUDE_BMP280=1 -D ENV_INCLUDE_INA3221=1 -D ENV_INCLUDE_INA219=1 -build_src_filter = ${nrf52840_base.build_src_filter} +build_src_filter = ${nrf52_base.build_src_filter} + - + + + +<../variants/promicro> -lib_deps= ${nrf52840_base.lib_deps} +lib_deps= ${nrf52_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 - adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BMP280 Library@^2.6.8 stevemarple/MicroNMEA @ ^2.0.6 - + [env:Faketec_Repeater] extends = Faketec build_src_filter = ${Faketec.build_src_filter} @@ -116,9 +118,9 @@ lib_deps = ${Faketec.lib_deps} densaugeo/base64 @ ~1.4.0 [ProMicroLLCC68] -extends = nrf52840_base +extends = nrf52_base board = promicro_nrf52840 -build_flags = ${nrf52840_base.build_flags} +build_flags = ${nrf52_base.build_flags} -I variants/promicro -D PROMICROLLCC68 -D RADIO_CLASS=CustomLLCC68 @@ -127,15 +129,15 @@ build_flags = ${nrf52840_base.build_flags} -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = - ${nrf52840_base.build_src_filter} + ${nrf52_base.build_src_filter} + - + + + +<../variants/promicro> -lib_deps= ${nrf52840_base.lib_deps} +lib_deps= ${nrf52_base.lib_deps} adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 - adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BME280 Library @ ^2.3.0 [env:ProMicroLLCC68_Repeater] extends = ProMicroLLCC68 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 3014bc59..86879ed7 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -6,8 +6,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/rak4631 -D RAK_4631 - -D PIN_USER_BTN=9 - -D PIN_USER_BTN_ANA=31 -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 @@ -23,7 +21,6 @@ lib_deps = ${nrf52840_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 stevemarple/MicroNMEA @ ^2.0.6 - sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 @@ -41,6 +38,30 @@ build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_repeater> +[env:RAK_4631_GPS_Repeater] +extends = rak4631 +build_flags = + ${rak4631.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"RAK4631 GPS Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D FORCE_GPS_ALIVE=1 + -D ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_BME680=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${rak4631.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${rak4631.lib_deps} + sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 + https://github.com/boschsensortec/Bosch-BSEC2-Library + https://github.com/boschsensortec/Bosch-BME68x-Library + [env:RAK_4631_room_server] extends = rak4631 build_flags = @@ -61,6 +82,8 @@ build_src_filter = ${rak4631.build_src_filter} extends = rak4631 build_flags = ${rak4631.build_flags} + -D PIN_USER_BTN=9 + -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -77,6 +100,8 @@ lib_deps = extends = rak4631 build_flags = ${rak4631.build_flags} + -D PIN_USER_BTN=9 + -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -97,6 +122,8 @@ lib_deps = extends = rak4631 build_flags = ${rak4631.build_flags} + -D PIN_USER_BTN=9 + -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -104,6 +131,7 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D ENV_INCLUDE_GPS=1 + -D ENV_INCLUDE_BME680=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} @@ -112,12 +140,17 @@ build_src_filter = ${rak4631.build_src_filter} +<../examples/companion_radio> lib_deps = ${rak4631.lib_deps} + sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 + https://github.com/boschsensortec/Bosch-BSEC2-Library + https://github.com/boschsensortec/Bosch-BME68x-Library densaugeo/base64 @ ~1.4.0 [env:RAK_4631_terminal_chat] extends = rak4631 build_flags = ${rak4631.build_flags} + -D PIN_USER_BTN=9 + -D PIN_USER_BTN_ANA=31 -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index ede755e2..e9d80421 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -19,13 +19,29 @@ RAK4631SensorManager sensors = RAK4631SensorManager(nmea); RAK4631SensorManager sensors; #endif +#if ENV_INCLUDE_BME680 +#ifndef TELEM_BME680_ADDRESS +#define TELEM_BME680_ADDRESS 0x76 // BME680 environmental sensor I2C address +#endif +#include +static Bsec2 BME680; +static float rawPressure = 0; +static float rawTemperature = 0; +static float compTemperature = 0; +static float rawHumidity = 0; +static float compHumidity = 0; +static float readIAQ = 0; +static float readStaticIAQ = 0; +static float readCO2 = 0; +#endif + #ifdef DISPLAY_CLASS DISPLAY_CLASS display; #endif #ifdef MESH_DEBUG uint32_t deviceOnline = 0x00; -void scanDevices(TwoWire *w) +static void scanDevices(TwoWire *w) { uint8_t err, addr; int nDevices = 0; @@ -43,6 +59,10 @@ void scanDevices(TwoWire *w) Serial.println("\tFound RAK12500 GPS Sensor"); deviceOnline |= RAK12500_ONLINE; break; + case 0x76: + Serial.println("\tFound RAK1906 Environment Sensor"); + deviceOnline |= BME680_ONLINE; + break; default: Serial.print("\tI2C device found at address 0x"); if (addr < 16) { @@ -137,7 +157,7 @@ bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); disStandbyPin = ioPin; gps_active = true; - gps_present = true; + gps_detected = true; return true; } else @@ -147,73 +167,255 @@ bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ } #endif +#if ENV_INCLUDE_BME680 +static void checkBMEStatus(Bsec2 bsec) { + if (bsec.status < BSEC_OK) + { + MESH_DEBUG_PRINTLN("BSEC error code : %f", float(bsec.status)); + } + else if (bsec.status > BSEC_OK) + { + MESH_DEBUG_PRINTLN("BSEC warning code : %f", float(bsec.status)); + } + + if (bsec.sensor.status < BME68X_OK) + { + MESH_DEBUG_PRINTLN("BME68X error code : %f", bsec.sensor.status); + } + else if (bsec.sensor.status > BME68X_OK) + { + MESH_DEBUG_PRINTLN("BME68X warning code : %f", bsec.sensor.status); + } +} + +static void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec) { + if (!outputs.nOutputs) { + MESH_DEBUG_PRINTLN("No new data to report out"); + return; + } + + MESH_DEBUG_PRINTLN("BSEC outputs:\n\tTime stamp = %f", (int) (outputs.output[0].time_stamp / INT64_C(1000000))); + for (uint8_t i = 0; i < outputs.nOutputs; i++) { + const bsecData output = outputs.output[i]; + switch (output.sensor_id) + { + case BSEC_OUTPUT_IAQ: + readIAQ = output.signal; + MESH_DEBUG_PRINTLN("\tIAQ = %f", output.signal); + MESH_DEBUG_PRINTLN("\tIAQ accuracy = %f", output.accuracy); + break; + case BSEC_OUTPUT_RAW_TEMPERATURE: + rawTemperature = output.signal; + MESH_DEBUG_PRINTLN("\tTemperature = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_PRESSURE: + rawPressure = output.signal; + MESH_DEBUG_PRINTLN("\tPressure = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_HUMIDITY: + rawHumidity = output.signal; + MESH_DEBUG_PRINTLN("\tHumidity = %f", output.signal); + break; + case BSEC_OUTPUT_RAW_GAS: + MESH_DEBUG_PRINTLN("\tGas resistance = %f", output.signal); + break; + case BSEC_OUTPUT_STABILIZATION_STATUS: + MESH_DEBUG_PRINTLN("\tStabilization status = %f", output.signal); + break; + case BSEC_OUTPUT_RUN_IN_STATUS: + MESH_DEBUG_PRINTLN("\tRun in status = %f", output.signal); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: + compTemperature = output.signal; + MESH_DEBUG_PRINTLN("\tCompensated temperature = %f", output.signal); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: + compHumidity = output.signal; + MESH_DEBUG_PRINTLN("\tCompensated humidity = %f", output.signal); + break; + case BSEC_OUTPUT_STATIC_IAQ: + readStaticIAQ = output.signal; + MESH_DEBUG_PRINTLN("\tStatic IAQ = %f", output.signal); + break; + case BSEC_OUTPUT_CO2_EQUIVALENT: + readCO2 = output.signal; + MESH_DEBUG_PRINTLN("\tCO2 Equivalent = %f", output.signal); + break; + case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: + MESH_DEBUG_PRINTLN("\tbVOC equivalent = %f", output.signal); + break; + case BSEC_OUTPUT_GAS_PERCENTAGE: + MESH_DEBUG_PRINTLN("\tGas percentage = %f", output.signal); + break; + case BSEC_OUTPUT_COMPENSATED_GAS: + MESH_DEBUG_PRINTLN("\tCompensated gas = %f", output.signal); + break; + default: + break; + } + } +} +#endif + bool RAK4631SensorManager::begin() { #ifdef MESH_DEBUG scanDevices(&Wire); #endif -#if ENV_INCLUDE_GPS - //search for the correct IO standby pin depending on socket used - if(gpsIsAwake(P_GPS_STANDBY_A)){ - MESH_DEBUG_PRINTLN("GPS is on socket A"); - } - else if(gpsIsAwake(P_GPS_STANDBY_C)){ - MESH_DEBUG_PRINTLN("GPS is on socket C"); - } - else if(gpsIsAwake(P_GPS_STANDBY_F)){ - MESH_DEBUG_PRINTLN("GPS is on socket F"); - } - else{ - MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); - gps_active = false; - gps_present = false; - return false; - } + #if ENV_INCLUDE_GPS + //search for the correct IO standby pin depending on socket used + if(gpsIsAwake(P_GPS_STANDBY_A)){ + MESH_DEBUG_PRINTLN("GPS is on socket A"); + } + else if(gpsIsAwake(P_GPS_STANDBY_C)){ + MESH_DEBUG_PRINTLN("GPS is on socket C"); + } + else if(gpsIsAwake(P_GPS_STANDBY_F)){ + MESH_DEBUG_PRINTLN("GPS is on socket F"); + } + else{ + MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); + gps_active = false; + gps_detected = false; + return false; + } - //Now that GPS is found and set up, set to sleep for initial state - stop_gps(); -#endif + #ifndef FORCE_GPS_ALIVE + //Now that GPS is found and set up, set to sleep for initial state + stop_gps(); + #endif + #endif + + #if ENV_INCLUDE_BME680 + + bsecSensor sensorList[5] = { + BSEC_OUTPUT_IAQ, + // BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + // BSEC_OUTPUT_RAW_HUMIDITY, + // BSEC_OUTPUT_RAW_GAS, + // BSEC_OUTPUT_STABILIZATION_STATUS, + // BSEC_OUTPUT_RUN_IN_STATUS, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + BSEC_OUTPUT_STATIC_IAQ, + // BSEC_OUTPUT_CO2_EQUIVALENT, + // BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, + // BSEC_OUTPUT_GAS_PERCENTAGE, + // BSEC_OUTPUT_COMPENSATED_GAS + }; + + if(!BME680.begin(TELEM_BME680_ADDRESS, Wire)){ + checkBMEStatus(BME680); + bme680_present = false; + bme680_active = false; + return false; + } + + MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); + bme680_present = true; + bme680_active = true; + + if (SAMPLING_RATE == BSEC_SAMPLE_RATE_ULP) + { + BME680.setTemperatureOffset(BSEC_SAMPLE_RATE_ULP); + } + else if (SAMPLING_RATE == BSEC_SAMPLE_RATE_LP) + { + BME680.setTemperatureOffset(TEMP_OFFSET_LP); + } + + if (!BME680.updateSubscription(sensorList, ARRAY_LEN(sensorList), SAMPLING_RATE)) + { + checkBMEStatus(BME680); + } + + BME680.attachCallback(newDataCallback); + + #endif } -#if ENV_INCLUDE_GPS bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + #ifdef ENV_INCLUDE_GPS if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } + #endif + + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { + + #if ENV_INCLUDE_BME680 + if (bme680_active) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, compTemperature); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, compHumidity); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, rawPressure); + telemetry.addTemperature(TELEM_CHANNEL_SELF+1, readIAQ); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF+1, readStaticIAQ); + } + #endif + } return true; } void RAK4631SensorManager::loop() { static long next_update = 0; + #ifdef ENV_INCLUDE_GPS _nmea->loop(); + #endif - if (millis() > next_update && gps_active) { - node_lat = (double)ublox_GNSS.getLatitude()/10000000.; - node_lon = (double)ublox_GNSS.getLongitude()/10000000.; - node_altitude = (double)ublox_GNSS.getAltitude()/1000.; - MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); + if (millis() > next_update) { + #ifdef ENV_INCLUDE_GPS + if(gps_active){ + node_lat = (double)ublox_GNSS.getLatitude()/10000000.; + node_lon = (double)ublox_GNSS.getLongitude()/10000000.; + node_altitude = (double)ublox_GNSS.getAltitude()/1000.; + MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); + } + #endif + + #ifdef ENV_INCLUDE_BME680 + if(bme680_active){ + if (!BME680.run()){ + checkBMEStatus(BME680); + } + } + #endif next_update = millis() + 1000; } + } -int RAK4631SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) +int RAK4631SensorManager::getNumSettings() const { + #if ENV_INCLUDE_GPS + return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected + #else + return 0; + #endif +} const char* RAK4631SensorManager::getSettingName(int i) const { - return i == 0 ? "gps" : NULL; + #if ENV_INCLUDE_GPS + return (gps_detected && i == 0) ? "gps" : NULL; + #else + return NULL; + #endif } const char* RAK4631SensorManager::getSettingValue(int i) const { - if (i == 0) { + #if ENV_INCLUDE_GPS + if (gps_detected && i == 0) { return gps_active ? "1" : "0"; } + #endif return NULL; } bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) { - if (strcmp(name, "gps") == 0) { + #if ENV_INCLUDE_GPS + if (gps_detected && strcmp(name, "gps") == 0) { if (strcmp(value, "0") == 0) { stop_gps(); } else { @@ -221,9 +423,9 @@ bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) } return true; } + #endif return false; // not supported } -#endif mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index 3e6db0aa..3f26ab33 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -20,7 +20,7 @@ class RAK4631SensorManager: public SensorManager { #if ENV_INCLUDE_GPS bool gps_active = false; - bool gps_present = false; + bool gps_detected = false; LocationProvider * _nmea; SFE_UBLOX_GNSS ublox_GNSS; uint32_t disStandbyPin = 0; @@ -32,20 +32,26 @@ class RAK4631SensorManager: public SensorManager { bool gpsIsAwake(uint32_t ioPin); #endif + #if ENV_INCLUDE_BME680 + bool bme680_active = false; + bool bme680_present = false; + #define SAMPLING_RATE BSEC_SAMPLE_RATE_ULP + #endif + public: #if ENV_INCLUDE_GPS RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } - - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; - void loop() override; - int getNumSettings() const override; - const char* getSettingName(int i) const override; - const char* getSettingValue(int i) const override; - bool setSettingValue(const char* name, const char* value) override; #else RAK4631SensorManager() { } #endif - bool begin() override; + + void loop() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; + bool begin() override; }; extern RAK4631Board board; diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 49ae26eb..f6fb1f04 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -54,7 +54,7 @@ bool radio_init() { SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); - int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); Serial.println(status); diff --git a/src/helpers/nrf52/XiaoNrf52Board.cpp b/variants/xiao_nrf52/XiaoNrf52Board.cpp similarity index 100% rename from src/helpers/nrf52/XiaoNrf52Board.cpp rename to variants/xiao_nrf52/XiaoNrf52Board.cpp diff --git a/src/helpers/nrf52/XiaoNrf52Board.h b/variants/xiao_nrf52/XiaoNrf52Board.h similarity index 93% rename from src/helpers/nrf52/XiaoNrf52Board.h rename to variants/xiao_nrf52/XiaoNrf52Board.h index e6c8e7f7..60b9f5bb 100644 --- a/src/helpers/nrf52/XiaoNrf52Board.h +++ b/variants/xiao_nrf52/XiaoNrf52Board.h @@ -5,20 +5,19 @@ #ifdef XIAO_NRF52 -// LoRa radio module pins for Seeed Xiao-nrf52 +// redefine lora pins if using the S3 variant of SX1262 board #ifdef SX1262_XIAO_S3_VARIANT #undef P_LORA_DIO_1 #undef P_LORA_BUSY #undef P_LORA_RESET #undef P_LORA_NSS + #undef SX126X_RXEN #define P_LORA_DIO_1 D0 #define P_LORA_BUSY D1 #define P_LORA_RESET D2 #define P_LORA_NSS D3 + #define SX126X_RXEN D4 #endif -//#define SX126X_POWER_EN 37 - - class XiaoNrf52Board : public mesh::MainBoard { protected: diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index c4934e04..bba3e632 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -50,8 +50,7 @@ build_flags = ${nrf52840_xiao.build_flags} -D ENV_INCLUDE_INA219=1 build_src_filter = ${nrf52840_xiao.build_src_filter} + - + - + + + +<../variants/xiao_nrf52> debug_tool = jlink upload_protocol = nrfutil diff --git a/variants/xiao_nrf52/target.h b/variants/xiao_nrf52/target.h index eb299006..c8c6a42a 100644 --- a/variants/xiao_nrf52/target.h +++ b/variants/xiao_nrf52/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include diff --git a/variants/xiao_rp2040/platformio.ini b/variants/xiao_rp2040/platformio.ini new file mode 100644 index 00000000..960fdbba --- /dev/null +++ b/variants/xiao_rp2040/platformio.ini @@ -0,0 +1,104 @@ +[Xiao_rp2040] +extends = rp2040_base + +board = seeed_xiao_rp2040 +board_build.filesystem_size = 0.5m + +build_flags = ${rp2040_base.build_flags} + -I variants/xiao_rp2040 + -D SX126X_CURRENT_LIMIT=140 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 +; Debug options + ; -D DEBUG_RP2040_WIRE=1 + ; -D DEBUG_RP2040_SPI=1 + ; -D DEBUG_RP2040_CORE=1 + ; -D RADIOLIB_DEBUG_SPI=1 + ; -D DEBUG_RP2040_PORT=Serial + +build_src_filter = ${rp2040_base.build_src_filter} + + + +<../variants/xiao_rp2040> + +lib_deps = ${rp2040_base.lib_deps} + +[env:Xiao_rp2040_Repeater] +extends = Xiao_rp2040 +build_flags = ${Xiao_rp2040.build_flags} + -D ADVERT_NAME='"Xiao Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +build_src_filter = ${Xiao_rp2040.build_src_filter} + +<../examples/simple_repeater> + +[env:Xiao_rp2040_room_server] +extends = Xiao_rp2040 +build_flags = ${Xiao_rp2040.build_flags} + -D ADVERT_NAME='"Xiao Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_rp2040.build_src_filter} + +<../examples/simple_room_server> + +[env:Xiao_rp2040_companion_radio_usb] +extends = Xiao_rp2040 +build_flags = ${Xiao_rp2040.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${Xiao_rp2040.build_src_filter} + +<../examples/companion_radio> +lib_deps = ${Xiao_rp2040.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; [env:Xiao_rp2040_companion_radio_ble] +; extends = Xiao_rp2040 +; build_flags = ${Xiao_rp2040.build_flags} +; -D MAX_CONTACTS=100 +; -D MAX_GROUP_CHANNELS=8 +; -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Xiao_rp2040.build_src_filter} +; +<../examples/companion_radio> +; lib_deps = ${Xiao_rp2040.lib_deps} +; densaugeo/base64 @ ~1.4.0 + +; [env:Xiao_rp2040_companion_radio_wifi] +; extends = Xiao_rp2040 +; build_flags = ${Xiao_rp2040.build_flags} +; -D MAX_CONTACTS=100 +; -D MAX_GROUP_CHANNELS=8 +; -D WIFI_DEBUG_LOGGING=1 +; -D WIFI_SSID='"myssid"' +; -D WIFI_PWD='"mypwd"' +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Xiao_rp2040.build_src_filter} +; +<../examples/companion_radio> +; lib_deps = ${Xiao_rp2040.lib_deps} +; densaugeo/base64 @ ~1.4.0 + +[env:Xiao_rp2040_terminal_chat] +extends = Xiao_rp2040 +build_flags = ${Xiao_rp2040.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_rp2040.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> +lib_deps = ${Xiao_rp2040.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_rp2040/target.cpp b/variants/xiao_rp2040/target.cpp new file mode 100644 index 00000000..a801aae8 --- /dev/null +++ b/variants/xiao_rp2040/target.cpp @@ -0,0 +1,71 @@ +#include "target.h" + +#include +#include + +XiaoRP2040Board board; + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +#ifndef LORA_CR +#define LORA_CR 5 +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + +#ifdef SX126X_DIO3_TCXO_VOLTAGE + float tcxo = SX126X_DIO3_TCXO_VOLTAGE; +#else + float tcxo = 1.6f; +#endif + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; // fail + } + + radio.setCRC(1); + +#ifdef SX126X_CURRENT_LIMIT + radio.setCurrentLimit(SX126X_CURRENT_LIMIT); +#endif + +#ifdef SX126X_DIO2_AS_RF_SWITCH + radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH); +#endif + +#ifdef SX126X_RX_BOOSTED_GAIN + radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN); +#endif + + return true; // success +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/xiao_rp2040/target.h b/variants/xiao_rp2040/target.h new file mode 100644 index 00000000..6a3c192b --- /dev/null +++ b/variants/xiao_rp2040/target.h @@ -0,0 +1,21 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 + +#include +#include +#include +#include +#include +#include + +extern XiaoRP2040Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity();