diff --git a/README.md b/README.md index 97890ce8..1413a4b4 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,11 @@ MeshCore provides the ability to create wireless mesh networks, similar to Mesht ## 🚀 How to Get Started -Flash the Firmware: Download the pre-built firmware binary for Heltec V3 and flash it using Adafruit ESPTool. -Install [PlatformIO](https://docs.platformio.org) in Visual Studio Code (optional for developers who want to modify the firmware). +Andy Kirby has published a very useful [intro video](https://www.youtube.com/watch?v=t1qne8uJBAc) which explains the steps for beginners. + +For developers, install [PlatformIO](https://docs.platformio.org) in Visual Studio Code. Download & Open the MeshCore repository. -Select a Sample Application: Choose from chat, ping, repeater, or admin test tools. +Select a Sample Application: Choose from chat, repeater, other example app. Monitor & Communicate using the Serial Monitor (e.g., Serial USB Terminal on Android). 📁 Included Example Applications @@ -36,8 +37,6 @@ Monitor & Communicate using the Serial Monitor (e.g., Serial USB Terminal on And * 📡 Simple Repeater: Extends network coverage by relaying messages. * 📡 Companion Radio: For use with an external chat app, over BLE or USB. * 📡 Room Server: A simple BBS server for shared Posts. -* 📡 Ping Client, Ping Server & Client: Basic client/server example. -* 📡 Test Admin: Monitors and manages repeaters remotely. ## 🛠 Hardware Compatibility @@ -49,10 +48,18 @@ MeshCore is designed for use with: * LilyGo T3S3 * Heltec T114 * Station G2 +* Sensecap T1000e +* Heltec V2 +* LilyGo TLora32 v1.6 ## 📜 License MeshCore is open-source software released under the MIT License. You are free to use, modify, and distribute it for personal and commercial projects. +## Contributing + +Please submit PR's using 'dev' as the base branch! +For minor changes just submit your PR and I'll try to review it, but for anything more 'impactful' please open an Issue first and start a discussion. Is better to sound out what it is you want to achieve first, and try to come to a consensus on what the best approach is, especially when it impacts the structure or architecture of this codebase. + ## 📞 Get Support Check out the GitHub Issues page to report bugs or request features. diff --git a/boards/promicro_nrf52840.json b/boards/promicro_nrf52840.json new file mode 100644 index 00000000..346f0f26 --- /dev/null +++ b/boards/promicro_nrf52840.json @@ -0,0 +1,78 @@ +{ + "build": { + "arduino":{ + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x00B3" + ], + [ + "0x239A", + "0x8029" + ], + [ + "0x239A", + "0x0029" + ], + [ + "0x239A", + "0x002A" + ], + [ + "0x239A", + "0x802A" + ] + ], + "usb_product": "ProMicro NRF52840", + "mcu": "nrf52840", + "variant": "promicro_nrf52840", + "variants_dir": "variants", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino", + "zephyr" + ], + "name": "ProMicro NRF52840", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.nologo.tech/en/product/otherboard/NRF52840.html", + "vendor": "Nologo" + } \ No newline at end of file diff --git a/boards/station-g2.json b/boards/station-g2.json new file mode 100644 index 00000000..8ea6ebc6 --- /dev/null +++ b/boards/station-g2.json @@ -0,0 +1,43 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=0" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "BQ Station G2", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://wiki.uniteng.com/en/meshtastic/station-g2", + "vendor": "BQ Consulting" +} diff --git a/boards/t-echo.json b/boards/t-echo.json index dee7297c..6bfa64df 100644 --- a/boards/t-echo.json +++ b/boards/t-echo.json @@ -1,62 +1,62 @@ { - "build": { - "arduino": { - "ldscript": "nrf52840_s140_v6.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA", - "f_cpu": "64000000L", - "hwids": [ - [ - "0x239A", - "0x8029" - ] - ], - "usb_product": "NRF52 DK", - "mcu": "nrf52840", - "variant": "pca10056", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS140", - "sd_name": "s140", - "sd_version": "6.1.1", - "sd_fwid": "0x00B6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": [ - "bluetooth" - ], - "debug": { - "jlink_device": "nRF52840_xxAA", - "onboard_tools": [ - "jlink" - ], - "svd_path": "nrf52840.svd" - }, - "frameworks": [ - "arduino" - ], - "name": "LilyGo T-ECHO", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": [ - "jlink", - "nrfjprog", - "stlink", - "cmsis-dap", - "blackmagic" - ] - }, - "url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/", - "vendor": "Nordic" + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x8029" + ] + ], + "usb_product": "NRF52 DK", + "mcu": "nrf52840", + "variant": "pca10056", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "onboard_tools": [ + "jlink" + ], + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino" + ], + "name": "LilyGo T-ECHO", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "require_upload_port": true, + "speed": 115200, + "protocol": "jlink", + "protocols": [ + "jlink", + "nrfjprog", + "stlink", + "cmsis-dap", + "blackmagic" + ] + }, + "url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/", + "vendor": "Nordic" } \ No newline at end of file diff --git a/boards/t3_s3_v1_x.json b/boards/t3_s3_v1_x.json index 49f9171c..d09c354a 100644 --- a/boards/t3_s3_v1_x.json +++ b/boards/t3_s3_v1_x.json @@ -1,45 +1,45 @@ { - "build": { - "arduino": { - "ldscript": "esp32s3_out.ld", - "partitions": "default.csv", - "memory_type": "qio_qspi" - }, - "core": "esp32", - "extra_flags": [ - "-DARDUINO_LILYGO_T3_S3_V1_X", - "-DBOARD_HAS_PSRAM", - "-DARDUINO_USB_CDC_ON_BOOT=1", - "-DARDUINO_RUNNING_CORE=1", - "-DARDUINO_EVENT_RUNNING_CORE=1", - "-DARDUINO_USB_MODE=1" - ], - "f_cpu": "240000000L", - "f_flash": "80000000L", - "flash_mode": "qio", - "mcu": "esp32s3", - "variant": "esp32s3" + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default.csv", + "memory_type": "qio_qspi" }, - "connectivity": [ - "wifi" + "core": "esp32", + "extra_flags": [ + "-DARDUINO_LILYGO_T3_S3_V1_X", + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1", + "-DARDUINO_USB_MODE=1" ], - "debug": { - "openocd_target": "esp32s3.cfg" - }, - "frameworks": [ - "arduino", - "espidf" - ], - "name": "LilyGo T3-S3 Radio", - "upload": { - "flash_size": "4MB", - "maximum_ram_size": 327680, - "maximum_size": 4194304, - "use_1200bps_touch": true, - "wait_for_upload_port": true, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://www.lilygo.cc", - "vendor": "LilyGo" + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "LilyGo T3-S3 Radio", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.lilygo.cc", + "vendor": "LilyGo" } \ No newline at end of file diff --git a/build.sh b/build.sh index d3ce6d46..f14b2f1c 100644 --- a/build.sh +++ b/build.sh @@ -57,8 +57,8 @@ build_firmware() { pio run -t mergebin -e $1 fi - # build .uf2 for RAK_4631 and t1000e - if [[ $1 == *"RAK_4631"* || $1 == *"t1000e"* ]]; then + # build .uf2 for nrf52 boards + if [[ $1 == *"RAK_4631"* || $1 == *"t1000e"* || $1 == *"t114"* || $1 == *"T-Echo"* ]]; then python bin/uf2conv/uf2conv.py .pio/build/$1/firmware.hex -c -o .pio/build/$1/firmware.uf2 -f 0xADA52840 fi diff --git a/docs/faq.md b/docs/faq.md index a7169a0e..fd578ad2 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,6 +1,9 @@ # MeshCore-FAQ A list of frequently-asked questions and answers for MeshCore +The current version of this MeshCore FAQ is at https://github.com/ripplebiz/MeshCore/blob/main/docs/faq.md. +This MeshCore FAQ is also mirrored at https://github.com/LitBomb/MeshCore-FAQ and might have newer updates if pull requests on Scott's MeshCore repo are not approved yet. + author: https://github.com/LitBomb --- @@ -25,6 +28,8 @@ Anyone is able to build anything they like on top of MeshCore without paying any Firmware Flasher: https://flasher.meshcore.co.uk/ Phone Client Applications: https://meshcore.co.uk/apps.html MeshCore Fimrware Github: https://github.com/ripplebiz/MeshCore + + NOTE: Andy Kirby has a very useful [intro video](https://www.youtube.com/watch?v=t1qne8uJBAc) for beginners. You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server). @@ -78,6 +83,12 @@ If you have two supported devices, and there are not many MeshCore users near yo If you have two supported devices, and there are other MeshcCore users near by, you can flash one of your devices with BLE Companion firmware, and flash another supported device to repeater firmware. Place the repeater high above ground o extend your MeshCore network's reach. +After you flashed the latest firmware onto your repeater device, keep the device connected to your computer via USB serial, use the console feature on the web flasher and set the frequency for your region or country, so your client can remote administer the rpeater or room server over RF: + +`set freq {frequency}` + +The repeater and room server CLI reference is here: https://github.com/ripplebiz/MeshCore/wiki/Repeater-&-Room-Server-CLI-Reference + If you have more supported devices, you can use your additional deivces with the room server firmware. ### Q: Does MeshCore cost any money? @@ -91,10 +102,10 @@ The T-Deck firmware is free to download and most features are available without ### Q: What frequencies are supported by MeshCore? **A:** It supports the 868MHz range in the UK/EU and the 915MHz range in New Zealand, Australia, and the USA. Countries and regions in these two frequency ranges are also supported. The firmware and client allow users to set their preferred frequency. -- Australia and New Zealand are using **915.8MHz** -- UK and EU are gravitating toward **867.5MHz** -- There are discussions on discord for UK to move to 869.525MHz (https://discord.com/channels/826570251612323860/1330643963501351004/1342554454498742374) -- USA is gravitating toward **910.525MHz** +- Australia and New Zealand are on **915.8MHz** +- UK and EU are on **869.525MHz** +- Canada and USA are on **910.525MHz** +- For other regions and countries, please check your local LoRa frequency the rest of the radio settings are the same for all frequencies: - Spread Factor (SF): 10 @@ -126,11 +137,11 @@ MeshCore clients only advertise themselves when the user initiates it. A repeate ### Q: How do you configure a repeater or a room server? **A:** One of these servers can be administered with one of the options below: -- Connect the server device through a USB serial connection to a computer running Chrome on this site: - +- Connect the server device using a USB cable to a computer running Chrome on https://flasher.meshcore.co.uk/, then use the `console` feature to connect to the device + - this is necessary to set the server device's frequency if it doesn't match the frequency for your local region or country +- MeshCore smart device clients have the ability to remotely administer servers. - A T-Deck running unlocked/registered MeshCore firmware. Remote server administration is enabled through registering your T-Deck with Ripple Radios. It is one of the ways to support MeshCore development. You can register your T-Deck at: -- MeshCore smart device clients may have the ability to remotely administer servers in the future. ### Q: Do I need to set the location for a repeater? **A:** With location set for a repeater, it can show up on a MeshCore map in the future. Set location with the following commands: @@ -226,12 +237,23 @@ Lowering the spreading factor makes it more difficult for the gateway to receive So it's balancing act between speed of the transmission and resistance to noise. things network is mainly focused on LoRaWAN, but the LoRa low-level stuff still checks out for any LoRa project +### Q: What happens when a node learns a route via a mobile repeater, and that repeater is gone? + +**A:** If you used to reach a node through a repeater and the repeater is no longer reachable, the client will send the message using the existing (but now broken) known path, the message will fail after 3 retries, and the app will reset the path and send the message as flood on the last retry by default. This can be turned off in settings. If the destination is reachable directly or through another repeater, the new path will be used going forward. Or you can set the path manually if you know a specific repeater to use to reach that destination. + +In the case if users are moving around frequently, and the paths are breaking, they just see the phone client retries and revert to flood to attempt to reestablish a path. + + ### Q: Is MeshCore open source? **A:** Most of the firmware is freely available. Everything is open source except the T-Deck firmware and Liam's native mobile apps. - Firmware repo: ### Q: How can I support MeshCore? -**A:** Provide your honest feedback on GitHub and on AndyKirby's Discord server . Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support MeshCore development at . +**A:** Provide your honest feedback on GitHub and on AndyKirby's Discord server . Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at . + +Support Liam Cottle's smartphone client development by unlocking the server administration wait gate with in-app purchase + +Support Rastislav Vysoky (recrof)'s flasher web site and the map web site development through [PayPal](https://www.paypal.com/donate/?business=DREHF5HM265ES&no_recurring=0&item_name=If+you+enjoy+my+work%2C+you+can+support+me+here%3A¤cy_code=EUR) or [Revolut](https://revolut.me/recrof) ### Q: How do I build MeshCore firmware from source? **A:** See instructions here: @@ -250,6 +272,12 @@ Javascript: https://github.com/liamcottle/meshcore.js ### Q: Does MeshCore support ATAK **A:** ATAK is not currently on MeshCore's roadmap. + +### Q: How do I add a node to the [MeshCore Map]([url](https://meshcore.co.uk/map.html)) +**A:** From the smartphone app, connect to a BLE Companion radio +- To add the BLE Companion radio your smartphone is connected to to the map, tap the `advert` icon, then tap `Advert (To Clipboard)`. +- To add a Repeater or Room Server to the map, tap the 3 dots next to the Repeater or Room Server you want to add to the map, then tap `Share (To Clipboard)`. +- Go to the [MeshCore Map web site]([url](https://meshcore.co.uk/map.html)), tap the plus sign on the lower right corner and paste in the meshcore://... blob, then tap `Add Node` --- @@ -274,7 +302,74 @@ 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 WiFi and Bluetooth connectivty. 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. +--- +## Other Questions: +### Q: How to Update repeater and room server firmware over the air? + +**A:** Only nRF-based RAK4631 and Heltec T114 OTA firmware update are verified using nRF smartphone app. Lilygo T-Echo doesn't work currently. +You can update repeater and room server firmware with a bluetooth connection between your smartphone and your LoRa radio using the nRF app. + +1. Download the ZIP file for the specific node from the web flasher to your smartphone +2. On the phone client, log on to the repeater as administrator (default password is `password`) to issue the `start ota`command to the repeater or room server to get the device into OTA DFU mode + +![image](https://github.com/user-attachments/assets/889bb81b-7214-4a1c-955a-396b5a05d8ad) + 1. `start ota` can be initiated from USB serial console on the web flasher page or a T-Deck +4. On the smartphone, download and run the nRF app and scan for Bluetooth devices +5. Connect to the repeater/room server node you want to update + 1. nRF app is available on both Android and iOS + +**Android continues after the iOS section:** + +**iOS continues here:** +5. Once connected successfully, a `DFU` icon ![Pasted image 20250309173039](https://github.com/user-attachments/assets/af7a9f78-8739-4946-b734-02bade9c8e71) + appears in the top right corner of the app![Pasted image 20250309171919](https://github.com/user-attachments/assets/08007ec8-4924-49c1-989f-ca2611e78793) + +6. Scroll down to change the `PRN(s)` number: + +![Pasted image 20250309190158](https://github.com/user-attachments/assets/11f69cdd-12f3-4696-a6fc-14a78c85fe32) + +- For the T114, change the number of packets `(PRN(s)` to 8 +- For RAK, it can be 10, but it also works on 8. + +7. Click the `DFU` icon ![Pasted image 20250309173039](https://github.com/user-attachments/assets/af7a9f78-8739-4946-b734-02bade9c8e71), select the type of file to upload (choose ZIP), then select the ZIP file that was downloaded earlier from the web flasher +8. The upload process will start now. If everything goes well, the node resets and is flashed successfully. +![Pasted image 20250309190342](https://github.com/user-attachments/assets/a60e25d0-33b8-46cf-af90-20a7d8ac2adb) + + + +**Android steps continues below:** +1. on the top left corner of the nRF Connect app on Android, tap the 3-bar hamburger menu, then `Settings`, then `nRF5 DFU Options` + +![Android nRF Hamberger](https://github.com/user-attachments/assets/ea6dfeef-9367-4830-bd70-1441d517c706) + +![Android nRF Settings](https://github.com/user-attachments/assets/c63726bf-cecd-4987-be68-afb6358c7190) + +![Android nRF DFU Options](https://github.com/user-attachments/assets/b20e872f-5122-41d9-90df-0215cff5fbc9) + +2. Change `Number of packets` to `10` for RAK, `8` for Heltec T114 + +![Android nRF Number of Packets](https://github.com/user-attachments/assets/c092adaf-4cb3-460b-b7ef-8d7f450d602b) + +3. Go back to the main screen +4. Your LoRa device should already ben in DFU mode from previous steps +5. tap `SCANNER` and then `SCAN` to find the device you want to update, tap `CONNECT` + +![Android nRF Scanner Scan Connect](https://github.com/user-attachments/assets/37218717-f167-48b6-a6ca-93d132ef77ca) + +6. On the top left corner of the nRF Connect app, tap the `DFU` icon next to the three dots + +![Android nRF DFU](https://github.com/user-attachments/assets/1ec3b818-bf0c-461f-8fdf-37c41a63cafa) + +7. Choose `Distribution packet (ZIP)` and then `OK` + +![Android nRF Distribution Packet (ZIP)](https://github.com/user-attachments/assets/e65f5616-9793-44f5-95c0-a3eb15aa7152) + +8. Choose the firmware file in ZIP formate that you downloaded earlier from the MeshCore web flasher, update will start as soon as you tap the file + +![Android nRF FW Updating](https://github.com/user-attachments/assets/0814d123-85ce-4c87-90a7-e1a25dc71900) + +9. When the update process is done, the device will disconnect from nRF app and the LoRa device is updated + --- - diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 8ffb2c1f..b454d939 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -4,6 +4,10 @@ #define AUTO_OFF_MILLIS 15000 // 15 seconds +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + // 'meshcore', 128x13px static const uint8_t meshcore_logo [] PROGMEM = { 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, @@ -22,13 +26,21 @@ static const uint8_t meshcore_logo [] PROGMEM = { }; void UITask::begin(const char* node_name, const char* build_date, uint32_t pin_code) { - _prevBtnState = HIGH; _auto_off = millis() + AUTO_OFF_MILLIS; clearMsgPreview(); _node_name = node_name; _build_date = build_date; _pin_code = pin_code; - _display->turnOn(); + if (_display != NULL) { + _display->turnOn(); + } +} + +void UITask::msgRead(int msgcount) { + _msgcount = msgcount; + if (msgcount == 0) { + clearMsgPreview(); + } } void UITask::clearMsgPreview() { @@ -36,7 +48,9 @@ void UITask::clearMsgPreview() { _msg[0] = 0; } -void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) { +void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { + _msgcount = msgcount; + if (path_len == 0xFF) { sprintf(_origin, "(F) %s", from_name); } else { @@ -44,11 +58,15 @@ void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* } StrHelper::strncpy(_msg, text, sizeof(_msg)); - if (!_display->isOn()) _display->turnOn(); - _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + if (_display != NULL) { + if (!_display->isOn()) _display->turnOn(); + _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + } } void UITask::renderCurrScreen() { + if (_display == NULL) return; // assert() ?? + char tmp[80]; if (_origin[0] && _msg[0]) { // render message preview @@ -61,9 +79,10 @@ void UITask::renderCurrScreen() { _display->setCursor(0, 24); _display->print(_msg); - //_display->setCursor(100, 9); TODO - //_display->setTextSize(2); - //_display->printf("%d", msgs); + _display->setCursor(100, 9); + _display->setTextSize(2); + sprintf(tmp, "%d", _msgcount); + _display->print(tmp); } else { // render 'home' screen _display->drawXbm(0, 0, meshcore_logo, 128, 13); @@ -87,24 +106,72 @@ void UITask::renderCurrScreen() { } } -void UITask::loop() { - if (millis() >= _next_read) { - int btnState = digitalRead(PIN_USER_BTN); - if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? - if (_display->isOn()) { - clearMsgPreview(); - } else { - _display->turnOn(); - } - _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer +void UITask::userLedHandler() { +#ifdef PIN_STATUS_LED + static int state = 0; + static int next_change = 0; + int cur_time = millis(); + if (cur_time > next_change) { + if (state == 0) { + state = 1; // led on, short = unread msg + if (_msgcount > 0) { + next_change = cur_time + 500; + } else { + next_change = cur_time + 2000; + } + } else { + state = 0; + if (_board->getBattMilliVolts() > 3800) { + next_change = cur_time + 2000; + } else { + next_change = cur_time + 4000; // 4s blank if bat level low } - _prevBtnState = btnState; } - _next_read = millis() + 100; // 10 reads per second + digitalWrite(PIN_STATUS_LED, state); } +#endif +} - if (_display->isOn()) { +void UITask::buttonHandler() { +#ifdef PIN_USER_BTN + static int prev_btn_state = !USER_BTN_PRESSED; + static unsigned long btn_state_change_time = 0; + static unsigned long next_read = 0; + int cur_time = millis(); + if (cur_time >= next_read) { + int btn_state = digitalRead(PIN_USER_BTN); + if (btn_state != prev_btn_state) { + if (btn_state == USER_BTN_PRESSED) { // pressed? + if (_display != NULL) { + if (_display->isOn()) { + clearMsgPreview(); + } else { + _display->turnOn(); + } + _auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer + } + } else { // unpressed ? check pressed time ... + if ((cur_time - btn_state_change_time) > 5000) { + #ifdef PIN_STATUS_LED + digitalWrite(PIN_STATUS_LED, LOW); + delay(10); + #endif + _board->powerOff(); + } + } + btn_state_change_time = millis(); + prev_btn_state = btn_state; + } + next_read = millis() + 100; // 10 reads per second + } +#endif +} + +void UITask::loop() { + buttonHandler(); + userLedHandler(); + + if (_display != NULL && _display->isOn()) { if (millis() >= _next_refresh) { _display->startFrame(); renderCurrScreen(); diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index f1a63225..4cea0b8d 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,25 +1,35 @@ #pragma once +#include #include class UITask { DisplayDriver* _display; - unsigned long _next_read, _next_refresh, _auto_off; - int _prevBtnState; + mesh::MainBoard* _board; + unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; const char* _node_name; const char* _build_date; char _origin[62]; char _msg[80]; + int _msgcount; void renderCurrScreen(); + void buttonHandler(); + void userLedHandler(); + public: - UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; } + + UITask(mesh::MainBoard* board, DisplayDriver* display) : _board(board), _display(display){ + _next_refresh = 0; + _connected = false; + } void begin(const char* node_name, const char* build_date, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } void clearMsgPreview(); - void showMsgPreview(uint8_t path_len, const char* from_name, const char* text); + void msgRead(int msgcount); + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void loop(); -}; \ No newline at end of file +}; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index ff2cd134..05260c43 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -46,6 +46,10 @@ #define OFFLINE_QUEUE_SIZE 16 #endif +#ifndef BLE_NAME_PREFIX + #define BLE_NAME_PREFIX "MeshCore-" +#endif + #include #define SEND_TIMEOUT_BASE_MILLIS 500 @@ -92,17 +96,25 @@ #include #include static TechoBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif #ifdef DISPLAY_CLASS + #include "UITask.h" #include static DISPLAY_CLASS display; - + static UITask ui_task(&board, &display); + #define HAS_UI +#elif defined(HAS_UI) #include "UITask.h" - static UITask ui_task(display); + + static UITask ui_task(&board, NULL); #endif // Believe it or not, this std C function is busted on some platforms! @@ -120,11 +132,11 @@ static uint32_t _atoi(const char* sp) { #define FIRMWARE_VER_CODE 3 #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "3 Mar 2025" + #define FIRMWARE_BUILD_DATE "13 Mar 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.0.0" + #define FIRMWARE_VERSION "v1.3.0" #endif #define CMD_APP_START 1 @@ -157,6 +169,8 @@ static uint32_t _atoi(const char* sp) { #define CMD_HAS_CONNECTION 28 #define CMD_LOGOUT 29 // 'Disconnect' #define CMD_GET_CONTACT_BY_KEY 30 +#define CMD_GET_CHANNEL 31 +#define CMD_SET_CHANNEL 32 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -176,6 +190,7 @@ static uint32_t _atoi(const char* sp) { #define RESP_CODE_DISABLED 15 #define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) #define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3) +#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL // these are _pushed_ to client app at any time #define PUSH_CODE_ADVERT 0x80 @@ -186,6 +201,7 @@ static uint32_t _atoi(const char* sp) { #define PUSH_CODE_LOGIN_SUCCESS 0x85 #define PUSH_CODE_LOGIN_FAIL 0x86 #define PUSH_CODE_STATUS_RESPONSE 0x87 +#define PUSH_CODE_LOG_RX_DATA 0x88 /* -------------------------------------------------------------------------------------- */ @@ -212,7 +228,6 @@ class MyMesh : public BaseChatMesh { NodePrefs _prefs; uint32_t pending_login; uint32_t pending_status; - mesh::GroupChannel* _public; BaseSerialInterface* _serial; ContactsIterator _iter; uint32_t _iter_filter_since; @@ -314,6 +329,58 @@ class MyMesh : public BaseChatMesh { } } + void loadChannels() { + if (_fs->exists("/channels2")) { + File file = _fs->open("/channels2"); + if (file) { + bool full = false; + uint8_t channel_idx = 0; + while (!full) { + ChannelDetails ch; + uint8_t unused[4]; + + bool success = (file.read(unused, 4) == 4); + success = success && (file.read((uint8_t *) ch.name, 32) == 32); + success = success && (file.read((uint8_t *) ch.channel.secret, 32) == 32); + + if (!success) break; // EOF + + if (setChannel(channel_idx, ch)) { + channel_idx++; + } else { + full = true; + } + } + file.close(); + } + } + } + + void saveChannels() { + #if defined(NRF52_PLATFORM) + File file = _fs->open("/channels2", FILE_O_WRITE); + if (file) { file.seek(0); file.truncate(); } + #else + File file = _fs->open("/channels2", "w", true); + #endif + if (file) { + uint8_t channel_idx = 0; + ChannelDetails ch; + uint8_t unused[4]; + memset(unused, 0, 4); + + while (getChannel(channel_idx, ch)) { + bool success = (file.write(unused, 4) == 4); + success = success && (file.write((uint8_t *) ch.name, 32) == 32); + success = success && (file.write((uint8_t *) ch.channel.secret, 32) == 32); + + if (!success) break; // write failed + channel_idx++; + } + file.close(); + } + } + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { char path[64]; char fname[18]; @@ -443,6 +510,18 @@ protected: return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override { + if (_serial->isConnected()) { + int i = 0; + out_frame[i++] = PUSH_CODE_LOG_RX_DATA; + out_frame[i++] = (int8_t)(snr * 4); + out_frame[i++] = (int8_t)(rssi); + memcpy(&out_frame[i], raw, len); i += len; + + _serial->writeFrame(out_frame, i); + } + } + void onDiscoveredContact(ContactInfo& contact, bool is_new) override { if (_serial->isConnected()) { out_frame[0] = PUSH_CODE_ADVERT; @@ -512,8 +591,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(path_len, from.name, text); + #ifdef HAS_UI + ui_task.newMsg(path_len, from.name, text, offline_queue_len); #endif } @@ -544,8 +623,9 @@ protected: out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } - out_frame[i++] = 0; // FUTURE: channel_idx (will just be 'public' for now) + out_frame[i++] = findChannelIdx(channel); uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + out_frame[i++] = TXT_TYPE_PLAIN; memcpy(&out_frame[i], ×tamp, 4); i += 4; int tlen = strlen(text); // TODO: UTF-8 ?? @@ -562,8 +642,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(path_len, "Public", text); + #ifdef HAS_UI + ui_task.newMsg(path_len, "Public", text, offline_queue_len); #endif } @@ -726,7 +806,8 @@ public: _fs->mkdir("/bl"); loadContacts(); - _public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel + addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel + loadChannels(); _phy->setFrequency(_prefs.freq); _phy->setSpreadingFactor(_prefs.sf); @@ -782,7 +863,9 @@ public: int i = 0; out_frame[i++] = RESP_CODE_DEVICE_INFO; out_frame[i++] = FIRMWARE_VER_CODE; - memset(&out_frame[i], 0, 6); i += 6; // reserved + out_frame[i++] = MAX_CONTACTS / 2; // v3+ + out_frame[i++] = MAX_GROUP_CHANNELS; // v3+ + memset(&out_frame[i], 0, 4); i += 4; // reserved memset(&out_frame[i], 0, 12); strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12; StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40; @@ -862,12 +945,14 @@ public: } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN - uint8_t channel_idx = cmd_frame[i++]; // reserved future + uint8_t channel_idx = cmd_frame[i++]; uint32_t msg_timestamp; memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4; const char *text = (char *) &cmd_frame[i]; - if (txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, *_public, _prefs.node_name, text, len - i)) { // hard-coded to 'public' channel for now + ChannelDetails channel; + bool success = getChannel(channel_idx, channel); + if (success && txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { writeOKFrame(); } else { writeErrFrame(); @@ -1032,6 +1117,9 @@ public: int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); + #ifdef HAS_UI + ui_task.msgRead(offline_queue_len); + #endif } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); @@ -1179,6 +1267,33 @@ public: uint8_t* pub_key = &cmd_frame[1]; stopConnection(pub_key); writeOKFrame(); + } else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + if (getChannel(channel_idx, channel)) { + int i = 0; + out_frame[i++] = RESP_CODE_CHANNEL_INFO; + out_frame[i++] = channel_idx; + strcpy((char *)&out_frame[i], channel.name); i += 32; + memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported + _serial->writeFrame(out_frame, i); + } else { + writeErrFrame(); + } + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+32) { + writeErrFrame(); // not supported (yet) + } else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+16) { + uint8_t channel_idx = cmd_frame[1]; + ChannelDetails channel; + StrHelper::strncpy(channel.name, (char *) &cmd_frame[2], 32); + memset(channel.channel.secret, 0, sizeof(channel.channel.secret)); + memcpy(channel.channel.secret, &cmd_frame[2+32], 16); // NOTE: only 128-bit supported + if (setChannel(channel_idx, channel)) { + saveChannels(); + writeOKFrame(); + } else { + writeErrFrame(); + } } else { writeErrFrame(); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); @@ -1212,7 +1327,7 @@ public: checkConnections(); } - #ifdef DISPLAY_CLASS + #ifdef HAS_UI ui_task.setHasConnection(_serial->isConnected()); ui_task.loop(); #endif @@ -1274,10 +1389,6 @@ void setup() { float tcxo = 1.6f; #endif -#ifdef DISPLAY_CLASS - display.begin(); -#endif - #if defined(NRF52_PLATFORM) SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); @@ -1285,21 +1396,30 @@ void setup() { 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 defined(FAKETEC) + 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 = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); Serial.println(status); halt(); } - radio.setCRC(0); + 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 fast_rng.begin(radio.random(0x7FFFFFFF)); @@ -1310,11 +1430,10 @@ void setup() { the_mesh.begin(InternalFS, trng); #ifdef BLE_PIN_CODE - char dev_name[32+10]; - sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName()); + char dev_name[32+16]; + sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName()); serial_interface.begin(dev_name, the_mesh.getBLEPin()); #else - pinMode(WB_IO2, OUTPUT); serial_interface.begin(Serial); #endif the_mesh.startInterface(serial_interface); @@ -1326,8 +1445,8 @@ void setup() { WiFi.begin(WIFI_SSID, WIFI_PWD); serial_interface.begin(TCP_PORT); #elif defined(BLE_PIN_CODE) - char dev_name[32+10]; - sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName()); + char dev_name[32+16]; + sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName()); serial_interface.begin(dev_name, the_mesh.getBLEPin()); #else serial_interface.begin(Serial); @@ -1338,6 +1457,9 @@ void setup() { #endif #ifdef DISPLAY_CLASS + display.begin(); +#endif +#ifdef HAS_UI ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin()); #endif } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index c9ccfe9e..04072f84 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 "3 Mar 2025" + #define FIRMWARE_BUILD_DATE "13 Mar 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.0.0" + #define FIRMWARE_VERSION "v1.3.0" #endif #ifndef LORA_FREQ @@ -76,10 +76,14 @@ #include #include static ESP32Board board; - #elif defined(LILYGO_TLORA) +#elif defined(LILYGO_TLORA) #include #include static LilyGoTLoraBoard board; +#elif defined(STATION_G2) + #include + #include + static StationG2Board board; #elif defined(RAK_4631) #include #include @@ -92,6 +96,10 @@ #include #include static TechoBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif @@ -231,7 +239,9 @@ protected: } bool allowPacketForward(const mesh::Packet* packet) override { - return !_prefs.disable_fwd; + if (_prefs.disable_fwd) return false; + if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; + return true; } const char* getLogDateTime() override { @@ -242,6 +252,15 @@ protected: return tmp; } + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override { + #if MESH_PACKET_LOGGING + Serial.print(getLogDateTime()); + Serial.print(" RAW: "); + mesh::Utils::printHex(Serial, raw, len); + Serial.println(); + #endif + } + void logRx(mesh::Packet* pkt, int len, float score) override { if (_logging) { File f = openAppend(PACKET_LOG_FILE); @@ -525,6 +544,7 @@ public: _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs + _prefs.flood_max = 64; } CommonCLI* getCLI() { return &_cli; } @@ -664,10 +684,6 @@ void setup() { float tcxo = 1.6f; #endif -#ifdef DISPLAY_CLASS - display.begin(); -#endif - #if defined(NRF52_PLATFORM) SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); @@ -675,6 +691,13 @@ void setup() { 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 defined(FAKETEC) + 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 = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { delay(5000); Serial.print("ERROR: radio init failed: "); @@ -682,15 +705,17 @@ void setup() { halt(); } - radio.setCRC(0); + 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 fast_rng.begin(radio.random(0x7FFFFFFF)); @@ -721,6 +746,7 @@ void setup() { the_mesh.begin(fs); #ifdef DISPLAY_CLASS + display.begin(); ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE); #endif diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 04266e11..e2572613 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 "6 Mar 2025" + #define FIRMWARE_BUILD_DATE "13 Mar 2025" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.1.0" + #define FIRMWARE_VERSION "v1.3.0" #endif #ifndef LORA_FREQ @@ -59,7 +59,7 @@ #define ADMIN_PASSWORD "password" #endif -#ifndef MAX_CLIENTS +#ifndef MAX_CLIENTS #define MAX_CLIENTS 32 #endif @@ -84,10 +84,26 @@ #include #include static LilyGoTLoraBoard board; +#elif defined(STATION_G2) + #include + #include + static StationG2Board board; #elif defined(RAK_4631) #include #include static RAK4631Board board; +#elif defined(HELTEC_T114) + #include + #include + static T114Board board; +#elif defined(LILYGO_TECHO) + #include + #include + static TechoBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif @@ -260,12 +276,21 @@ protected: return _prefs.airtime_factor; } + void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override { + #if MESH_PACKET_LOGGING + Serial.print(getLogDateTime()); + Serial.print(" RAW: "); + mesh::Utils::printHex(Serial, raw, len); + Serial.println(); + #endif + } + int calcRxDelay(float score, uint32_t air_time) const override { if (_prefs.rx_delay_base <= 0.0f) return 0; return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); } - const char* getLogDateTime() override { + const char* getLogDateTime() override { static char tmp[32]; uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); @@ -283,7 +308,9 @@ protected: } bool allowPacketForward(const mesh::Packet* packet) override { - return !_prefs.disable_fwd; + if (_prefs.disable_fwd) return false; + if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; + return true; } void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override { @@ -470,7 +497,7 @@ protected: } else { memcpy(&data[5], &forceSince, 4); // make sure there are zeroes in payload (for ack_hash calc below) } - if (forceSince > 0) { + if (forceSince > 0) { client->sync_since = forceSince; // force-update the 'sync since' } @@ -525,7 +552,7 @@ protected: public: MyMesh(RADIO_CLASS& phy, mesh::MainBoard& board, RadioLibWrapper& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) - : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), + : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _phy(&phy), _board(&board), _cli(board, this, &_prefs, this) { my_radio = &radio; @@ -547,6 +574,7 @@ public: _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.disable_fwd = 1; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs + _prefs.flood_max = 64; #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif @@ -593,7 +621,7 @@ public: return false; #endif } - + void sendSelfAdvertisement(int delay_millis) override { mesh::Packet* pkt = createSelfAdvert(); if (pkt) { @@ -688,7 +716,7 @@ SimpleMeshTables tables; #ifdef ESP32 ESP32RTCClock fallback_clock; #else -VolatileRTCClock fallback_clock; +VolatileRTCClock fallback_clock; #endif AutoDiscoverRTCClock rtc_clock(fallback_clock); @@ -716,10 +744,6 @@ void setup() { float tcxo = 1.6f; #endif -#ifdef DISPLAY_CLASS - display.begin(); -#endif - #if defined(NRF52_PLATFORM) SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); @@ -727,6 +751,13 @@ void setup() { 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 defined(FAKETEC) + 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 = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { delay(5000); Serial.print("ERROR: radio init failed: "); @@ -734,15 +765,17 @@ void setup() { halt(); } - radio.setCRC(0); + 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 fast_rng.begin(radio.random(0x7FFFFFFF)); @@ -772,6 +805,7 @@ void setup() { the_mesh.begin(fs); #ifdef DISPLAY_CLASS + display.begin(); ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE); #endif @@ -783,7 +817,7 @@ void loop() { int len = strlen(command); while (Serial.available() && len < sizeof(command)-1) { char c = Serial.read(); - if (c != '\n') { + if (c != '\n') { command[len++] = c; command[len] = 0; } diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index b03c479f..ce166d78 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -78,6 +78,10 @@ #include #include static T1000eBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif @@ -107,7 +111,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor { FILESYSTEM* _fs; NodePrefs _prefs; uint32_t expected_ack_crc; - mesh::GroupChannel* _public; + ChannelDetails* _public; unsigned long last_msg_sent; ContactInfo* curr_recipient; char command[512+10]; @@ -341,7 +345,7 @@ public: } loadContacts(); - _public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel + _public = addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel } void savePrefs() { @@ -409,7 +413,7 @@ public: temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long int len = strlen((char *) &temp[5]); - auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, *_public, temp, 5 + len); + auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, _public->channel, temp, 5 + len); if (pkt) { sendFlood(pkt); Serial.println(" Sent."); @@ -589,7 +593,7 @@ void setup() { halt(); } - radio.setCRC(0); + radio.setCRC(1); #ifdef SX126X_CURRENT_LIMIT radio.setCurrentLimit(SX126X_CURRENT_LIMIT); diff --git a/platformio.ini b/platformio.ini index 631e78e5..2b118561 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ monitor_speed = 115200 lib_deps = SPI Wire - jgromes/RadioLib @ ^6.3.0 + jgromes/RadioLib @ ^7.1.2 rweather/Crypto @ ^0.4.0 adafruit/RTClib @ ^2.1.3 melopero/Melopero RV3028 @ ^1.1.0 @@ -22,13 +22,17 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=867.5 -D LORA_BW=250 -D LORA_SF=10 -build_src_filter = +<*.cpp> + +build_src_filter = + +<*.cpp> + + [esp32_base] extends = arduino_base platform = espressif32 monitor_filters = esp32_exception_decoder extra_scripts = merge-bin.py +build_flags = ${arduino_base.build_flags} +; -D ESP32_CPU_FREQ=80 ; change it to your need build_src_filter = ${arduino_base.build_src_filter} ; ================ @@ -48,13 +52,14 @@ build_src_filter = ${esp32_base.build_src_filter} extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} - -D ADVERT_NAME="\"Heltec Repeater\"" + -D ADVERT_NAME='"Heltec Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v2.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + +<../examples/simple_repeater/main.cpp> [env:Heltec_v2_terminal_chat] extends = Heltec_lora32_v2 @@ -64,7 +69,8 @@ build_flags = -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v2.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -74,10 +80,12 @@ extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -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 = ${Heltec_lora32_v2.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -87,19 +95,20 @@ extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v2.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 - ; ================ [Heltec_lora32_v3] extends = esp32_base @@ -117,6 +126,7 @@ build_flags = -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=130.0f ; for best TX power! + -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${esp32_base.build_src_filter} lib_deps = ${esp32_base.lib_deps} @@ -127,25 +137,29 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D DISPLAY_CLASS=SSD1306Display - -D ADVERT_NAME="\"Heltec Repeater\"" + -D ADVERT_NAME='"Heltec Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_repeater> [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_room_server> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_room_server> build_flags = ${Heltec_lora32_v3.build_flags} -D DISPLAY_CLASS=SSD1306Display - -D ADVERT_NAME="\"Heltec Room\"" + -D ADVERT_NAME='"Heltec Room"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" - -D ROOM_PASSWORD="\"hello\"" + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -157,7 +171,8 @@ build_flags = -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -167,13 +182,15 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/companion_radio> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -183,7 +200,7 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display -D BLE_PIN_CODE=0 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 @@ -191,24 +208,10 @@ build_flags = ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> -lib_deps = - ${Heltec_lora32_v3.lib_deps} - densaugeo/base64 @ ~1.4.0 - -[env:Heltec_WSL3_companion_radio_ble] -extends = Heltec_lora32_v3 -build_flags = - ${Heltec_lora32_v3.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 - -D BLE_PIN_CODE=123456 - -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/companion_radio> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -218,16 +221,65 @@ extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 - -D WIFI_SSID="\"myssid\"" - -D WIFI_PWD="\"mypwd\"" + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + +<../examples/companion_radio> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_WSL3_repeater] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"Heltec Repeater"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> + +[env:Heltec_WSL3_room_server] +extends = Heltec_lora32_v3 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"Heltec Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_WSL3_companion_radio_ble] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -253,31 +305,34 @@ build_flags = [env:Xiao_C3_Repeater_sx1262] extends = Xiao_esp32_C3 -build_src_filter = ${Xiao_esp32_C3.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${Xiao_esp32_C3.build_flags} -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper + -D SX126X_RX_BOOSTED_GAIN=1 -D LORA_TX_POWER=22 - -D ADVERT_NAME="\"Xiao Repeater\"" + -D ADVERT_NAME='"Xiao Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 [env:Xiao_C3_Repeater_sx1268] extends = Xiao_esp32_C3 -build_src_filter = ${Xiao_esp32_C3.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${Xiao_esp32_C3.build_flags} -D RADIO_CLASS=CustomSX1268 -D WRAPPER_CLASS=CustomSX1268Wrapper -D LORA_TX_POWER=22 - -D ADVERT_NAME="\"Xiao Repeater\"" + -D ADVERT_NAME='"Xiao Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -302,16 +357,18 @@ build_flags = ${esp32_base.build_flags} -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 [env:Xiao_S3_WIO_Repeater] extends = Xiao_S3_WIO -build_src_filter = ${Xiao_S3_WIO.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${Xiao_S3_WIO.build_flags} - -D ADVERT_NAME="\"XiaoS3 Repeater\"" + -D ADVERT_NAME='"XiaoS3 Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -320,10 +377,11 @@ extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Xiao_S3_WIO.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -333,14 +391,16 @@ extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Xiao_S3_WIO.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -365,10 +425,98 @@ build_flags = -D P_LORA_MOSI=27 ; SPI MOSI -D P_LORA_TX_LED=2 ; LED pin for TX indication -D PIN_VBAT_READ=35 ; Battery voltage reading (analog pin) + -D PIN_USER_BTN=0 -D RADIO_CLASS=CustomSX1276 -D ARDUINO_LOOP_STACK_SIZE=16384 + -D DISPLAY_CLASS=SSD1306Display -D WRAPPER_CLASS=CustomSX1276Wrapper -D LORA_TX_POWER=20 +lib_deps = + ${esp32_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + +; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments === +[env:LilyGo_TLora_V2_1_1_6_Repeater] +extends = LilyGo_TLora_V2_1_1_6 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/simple_repeater> +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D ADVERT_NAME='"TLora-V2.1-1.6 Repeater"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +; -D CORE_DEBUG_LEVEL=3 + +[env:LilyGo_TLora_V2_1_1_6_terminal_chat] +extends = LilyGo_TLora_V2_1_1_6 +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TLora_V2_1_1_6_companion_radio_usb] +extends = LilyGo_TLora_V2_1_1_6 +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/companion_radio> +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble] +extends = LilyGo_TLora_V2_1_1_6 +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 +; -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + + + +<../examples/companion_radio> +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TLora_V2_1_1_6_room_server] +extends = LilyGo_TLora_V2_1_1_6 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/simple_room_server> +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D ADVERT_NAME='"TLora-V2.1-1.6 Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 ; ============= [LilyGo_T3S3_sx1262] @@ -391,90 +539,19 @@ build_flags = ${esp32_base.build_flags} -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 - - -; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments === -[env:LilyGo_TLora_V2_1_1_6_Repeater] -extends = LilyGo_TLora_V2_1_1_6 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} +<../examples/simple_repeater/main.cpp> -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D ADVERT_NAME="\"TLora-V2.1-1.6 Repeater\"" - -D ADVERT_LAT=-37.0 - -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -; -D CORE_DEBUG_LEVEL=3 - -[env:LilyGo_TLora_V2_1_1_6_terminal_chat] -extends = LilyGo_TLora_V2_1_1_6 -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} +<../examples/simple_secure_chat/main.cpp> -lib_deps = - ${LilyGo_TLora_V2_1_1_6.lib_deps} - densaugeo/base64 @ ~1.4.0 - -[env:LilyGo_TLora_V2_1_1_6_companion_radio_usb] -extends = LilyGo_TLora_V2_1_1_6 -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 -; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 -; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} +<../examples/companion_radio/main.cpp> -lib_deps = - ${LilyGo_TLora_V2_1_1_6.lib_deps} - densaugeo/base64 @ ~1.4.0 - -[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble] -extends = LilyGo_TLora_V2_1_1_6 -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 - -D BLE_PIN_CODE=123456 -; -D BLE_DEBUG_LOGGING=1 -; -D ENABLE_PRIVATE_KEY_IMPORT=1 -; -D ENABLE_PRIVATE_KEY_EXPORT=1 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + +<../examples/companion_radio/main.cpp> -lib_deps = - ${LilyGo_TLora_V2_1_1_6.lib_deps} - densaugeo/base64 @ ~1.4.0 - -[env:LilyGo_TLora_V2_1_1_6_room_server] -extends = LilyGo_TLora_V2_1_1_6 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} +<../examples/simple_room_server/main.cpp> -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D ADVERT_NAME="\"TLora-V2.1-1.6 Room\"" - -D ADVERT_LAT=-37.0 - -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" - -D ROOM_PASSWORD="\"hello\"" -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 + -D SX126X_RX_BOOSTED_GAIN=1 ; === LilyGo T3S3 with SX1262 environments === [env:LilyGo_T3S3_sx1262_Repeater] extends = LilyGo_T3S3_sx1262 -build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${LilyGo_T3S3_sx1262.build_flags} - -D ADVERT_NAME="\"T3S3-1262 Repeater\"" + -D ADVERT_NAME='"T3S3-1262 Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -486,7 +563,8 @@ build_flags = -D MAX_GROUP_CHANNELS=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -496,12 +574,13 @@ extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +<../examples/companion_radio/main.cpp> +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -511,18 +590,69 @@ extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[Station_G2] +extends = esp32_base +board = station-g2 +build_flags = + ${esp32_base.build_flags} + -D STATION_G2 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=7 +; -D P_LORA_TX_LED=35 +; -D PIN_BOARD_SDA=5 +; -D PIN_BOARD_SCL=6 + -D PIN_USER_BTN=0 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=130.0f ; for best TX power! +; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS! +; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance + +build_src_filter = ${esp32_base.build_src_filter} +lib_deps = + ${esp32_base.lib_deps} + +[env:Station_G2_repeater] +extends = Station_G2 +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"Station G2 Repeater"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Station_G2.build_src_filter} + +<../examples/simple_repeater> + +[env:Station_G2_room_server] +extends = Station_G2 +build_src_filter = ${Station_G2.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"Station G2 Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 ; ----------------- NRF52 --------------------- [nrf52_base] @@ -544,36 +674,40 @@ extends = nrf52840_base platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak board = wiscore_rak4631 board_check = true -build_src_filter = ${nrf52840_base.build_src_filter} + +build_src_filter = ${nrf52840_base.build_src_filter} + + build_flags = ${nrf52840_base.build_flags} -D RAK_4631 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=130 + -D SX126X_RX_BOOSTED_GAIN=1 [env:RAK_4631_Repeater] extends = rak4631 -build_src_filter = ${rak4631.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${rak4631.build_flags} - -D ADVERT_NAME="\"RAK4631 Repeater\"" + -D ADVERT_NAME='"RAK4631 Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 [env:RAK_4631_room_server] extends = rak4631 -build_src_filter = ${rak4631.build_src_filter} +<../examples/simple_room_server/main.cpp> +build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_room_server/main.cpp> build_flags = ${rak4631.build_flags} - -D ADVERT_NAME="\"Test Room\"" + -D ADVERT_NAME='"Test Room"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" - -D ROOM_PASSWORD="\"hello\"" + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -585,7 +719,8 @@ build_flags = -D MAX_GROUP_CHANNELS=1 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -595,12 +730,13 @@ extends = rak4631 build_flags = ${rak4631.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} +<../examples/companion_radio/main.cpp> +build_src_filter = ${rak4631.build_src_filter} + +<../examples/companion_radio/main.cpp> lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -610,14 +746,16 @@ extends = rak4631 build_flags = ${rak4631.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${rak4631.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${rak4631.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -644,10 +782,13 @@ board = tracker-t1000-e board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52840_t1000e.build_flags} -Ivariants/t1000-e - -DT1000_E + -D T1000_E -D PIN_USER_BTN=6 + -D USER_BTN_PRESSED=HIGH + -D PIN_STATUS_LED=24 -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper + -D MAX_LORA_TX_POWER=22 build_src_filter = ${nrf52840_t1000e.build_src_filter} + + @@ -659,11 +800,12 @@ upload_protocol = nrfutil extends = t1000-e build_flags = ${t1000-e.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 + -D HAS_UI build_src_filter = ${t1000-e.build_src_filter} + +<../examples/companion_radio/*.cpp> @@ -688,10 +830,12 @@ board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52840_t114.build_flags} -Ivariants/t114 -DHELTEC_T114 + -D P_LORA_TX_LED=35 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=130 + -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${nrf52840_t114.build_src_filter} + + @@ -701,13 +845,28 @@ upload_protocol = nrfutil [env:Heltec_t114_repeater] extends = Heltec_t114 -build_src_filter = ${Heltec_t114.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Heltec_t114.build_src_filter} + +<../examples/simple_repeater/main.cpp> build_flags = ${Heltec_t114.build_flags} - -D ADVERT_NAME="\"Heltec_T114 Repeater\"" + -D ADVERT_NAME='"Heltec_T114 Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_t114_room_server] +extends = Heltec_t114 +build_src_filter = ${Heltec_t114.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Heltec_t114.build_flags} + -D ADVERT_NAME='"Heltec_T114 Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -716,14 +875,16 @@ extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_t114.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${Heltec_t114.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -750,6 +911,7 @@ build_flags = ${nrf52840_techo.build_flags} -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=130 + -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${nrf52840_techo.build_src_filter} + + @@ -762,10 +924,22 @@ extends = LilyGo_Techo build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeater/main.cpp> build_flags = ${LilyGo_Techo.build_flags} - -D ADVERT_NAME="\"T-Echo Repeater\"" + -D ADVERT_NAME='"T-Echo Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 - -D ADMIN_PASSWORD="\"password\"" + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:LilyGo_T-Echo_room_server] +extends = LilyGo_Techo +build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_server/main.cpp> +build_flags = + ${LilyGo_Techo.build_flags} + -D ADVERT_NAME='"T-Echo Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 @@ -774,14 +948,106 @@ extends = LilyGo_Techo build_flags = ${LilyGo_Techo.build_flags} -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=1 + -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_Techo.build_src_filter} + +<../examples/companion_radio/main.cpp> +build_src_filter = ${LilyGo_Techo.build_src_filter} + + + +<../examples/companion_radio/main.cpp> lib_deps = ${LilyGo_Techo.lib_deps} densaugeo/base64 @ ~1.4.0 + + +[faketec] +extends = nrf52840_base +board = promicro_nrf52840 +build_src_filter = ${nrf52840_base.build_src_filter} + +build_flags = ${nrf52840_base.build_flags} + -D FAKETEC + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=130 + -D SX126X_RX_BOOSTED_GAIN=1 + +[env:Faketec_Repeater] +extends = faketec +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_flags = + ${faketec.build_flags} + -D ADVERT_NAME="\"Faketec Repeater\"" + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD="\"password\"" +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_room_server] +extends = faketec +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_room_server/main.cpp> +build_flags = + ${faketec.build_flags} + -D ADVERT_NAME="\"Test Room\"" + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD="\"password\"" + -D ROOM_PASSWORD="\"hello\"" +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_terminal_chat] +extends = faketec +build_flags = + ${faketec.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${faketec.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_companion_radio_usb] +extends = faketec +build_flags = + ${faketec.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 = ${faketec.build_src_filter} +<../examples/companion_radio/main.cpp> +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 + +[env:Faketec_companion_radio_ble] +extends = faketec +build_flags = + ${faketec.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D ENABLE_PRIVATE_KEY_EXPORT=1 + -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${faketec.build_src_filter} + +<../examples/companion_radio/main.cpp> +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index b67ba7cf..7588bcf2 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -78,9 +78,11 @@ void Dispatcher::checkRecv() { float score; uint32_t air_time; { - uint8_t raw[MAX_TRANS_UNIT]; + uint8_t raw[MAX_TRANS_UNIT+1]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); if (len > 0) { + logRxRaw(_radio->getLastSNR(), _radio->getLastRSSI(), raw, len); + pkt = _mgr->allocNew(); if (pkt == NULL) { MESH_DEBUG_PRINTLN("%s Dispatcher::checkRecv(): WARNING: received data, no unused packets available!", getLogDateTime()); @@ -106,10 +108,17 @@ void Dispatcher::checkRecv() { memcpy(pkt->path, &raw[i], pkt->path_len); i += pkt->path_len; pkt->payload_len = len - i; // payload is remainder - memcpy(pkt->payload, &raw[i], pkt->payload_len); + if (pkt->payload_len > sizeof(pkt->payload)) { + MESH_DEBUG_PRINTLN("%s Dispatcher::checkRecv(): packet payload too big, payload_len=%d", getLogDateTime(), (uint32_t)pkt->payload_len); + _mgr->free(pkt); // put back into pool + pkt = NULL; + } else { + memcpy(pkt->payload, &raw[i], pkt->payload_len); - score = _radio->packetScore(pkt->_snr = (_radio->getLastSNR() * 4.0f), len); - air_time = _radio->getEstAirtimeFor(len); + pkt->_snr = _radio->getLastSNR() * 4.0f; + score = _radio->packetScore(_radio->getLastSNR(), len); + air_time = _radio->getEstAirtimeFor(len); + } } } } else { @@ -122,6 +131,12 @@ void Dispatcher::checkRecv() { Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", 2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len, (int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000)); + + static uint8_t packet_hash[MAX_HASH_SIZE]; + pkt->calculatePacketHash(packet_hash); + Serial.print(" hash="); + mesh::Utils::printHex(Serial, packet_hash, MAX_HASH_SIZE); + if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ || pkt->getPayloadType() == PAYLOAD_TYPE_RESPONSE || pkt->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) { Serial.printf(" [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]); diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 7d75c219..0b00f1a9 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -117,6 +117,8 @@ protected: virtual DispatcherAction onRecvPacket(Packet* pkt) = 0; + virtual void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { } // custom hook + virtual void logRx(Packet* packet, int len, float score) { } // hooks for custom logging virtual void logTx(Packet* packet, int len) { } virtual void logTxFail(Packet* packet, int len) { } diff --git a/src/MeshCore.h b/src/MeshCore.h index 6bb11989..9593877d 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -40,6 +40,7 @@ public: virtual void onBeforeTransmit() { } virtual void onAfterTransmit() { } virtual void reboot() = 0; + virtual void powerOff() { /* no op */ } virtual uint8_t getStartupReason() const = 0; virtual bool startOTAUpdate() { return false; } // not supported }; diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index 43613af2..e84f6d37 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -23,7 +23,12 @@ public: #ifdef LILYGO_T3S3 void begin(HWCDC& serial) { _serial = &serial; } #elif defined(NRF52_PLATFORM) - void begin(Adafruit_USBD_CDC& serial) { _serial = &serial; } + void begin(Adafruit_USBD_CDC& serial) { + _serial = &serial; + #ifdef RAK_4631 + pinMode(WB_IO2, OUTPUT); + #endif + } #else void begin(HardwareSerial& serial) { _serial = &serial; } #endif diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 4ae0b1b1..813bb8e9 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -247,9 +247,9 @@ void BaseChatMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) { #ifdef MAX_GROUP_CHANNELS int BaseChatMesh::searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel dest[], int max_matches) { int n = 0; - for (int i = 0; i < num_channels && n < max_matches; i++) { - if (channels[i].hash[0] == hash[0]) { - dest[n++] = channels[i]; + for (int i = 0; i < MAX_GROUP_CHANNELS && n < max_matches; i++) { + if (channels[i].channel.hash[0] == hash[0]) { + dest[n++] = channels[i].channel; } } return n; @@ -651,24 +651,61 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) { #ifdef MAX_GROUP_CHANNELS #include -mesh::GroupChannel* BaseChatMesh::addChannel(const char* psk_base64) { +ChannelDetails* BaseChatMesh::addChannel(const char* name, const char* psk_base64) { if (num_channels < MAX_GROUP_CHANNELS) { auto dest = &channels[num_channels]; - memset(dest->secret, 0, sizeof(dest->secret)); - int len = decode_base64((unsigned char *) psk_base64, strlen(psk_base64), dest->secret); + memset(dest->channel.secret, 0, sizeof(dest->channel.secret)); + int len = decode_base64((unsigned char *) psk_base64, strlen(psk_base64), dest->channel.secret); if (len == 32 || len == 16) { - mesh::Utils::sha256(dest->hash, sizeof(dest->hash), dest->secret, len); + mesh::Utils::sha256(dest->channel.hash, sizeof(dest->channel.hash), dest->channel.secret, len); + StrHelper::strncpy(dest->name, name, sizeof(dest->name)); num_channels++; return dest; } } return NULL; } +bool BaseChatMesh::getChannel(int idx, ChannelDetails& dest) { + if (idx >= 0 && idx < MAX_GROUP_CHANNELS) { + dest = channels[idx]; + return true; + } + return false; +} +bool BaseChatMesh::setChannel(int idx, const ChannelDetails& src) { + static uint8_t zeroes[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + + if (idx >= 0 && idx < MAX_GROUP_CHANNELS) { + channels[idx] = src; + if (memcmp(&src.channel.secret[16], zeroes, 16) == 0) { + mesh::Utils::sha256(channels[idx].channel.hash, sizeof(channels[idx].channel.hash), src.channel.secret, 16); // 128-bit key + } else { + mesh::Utils::sha256(channels[idx].channel.hash, sizeof(channels[idx].channel.hash), src.channel.secret, 32); // 256-bit key + } + return true; + } + return false; +} +int BaseChatMesh::findChannelIdx(const mesh::GroupChannel& ch) { + for (int i = 0; i < MAX_GROUP_CHANNELS; i++) { + if (memcmp(ch.secret, channels[i].channel.secret, sizeof(ch.secret)) == 0) return i; + } + return -1; // not found +} #else -mesh::GroupChannel* BaseChatMesh::addChannel(const char* psk_base64) { +ChannelDetails* BaseChatMesh::addChannel(const char* name, const char* psk_base64) { return NULL; // not supported } +bool BaseChatMesh::getChannel(int idx, ChannelDetails& dest) { + return false; +} +bool BaseChatMesh::setChannel(int idx, const ChannelDetails& src) { + return false; +} +int BaseChatMesh::findChannelIdx(const mesh::GroupChannel& ch) { + return -1; // not found +} #endif ContactsIterator BaseChatMesh::startContactsIterator() { diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 8d22cbea..6a577828 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -61,6 +61,11 @@ struct ConnectionInfo { uint32_t expected_ack; }; +struct ChannelDetails { + mesh::GroupChannel channel; + char name[32]; +}; + /** * \brief abstract Mesh class for common 'chat' client */ @@ -74,8 +79,8 @@ class BaseChatMesh : public mesh::Mesh { int matching_peer_indexes[MAX_SEARCH_RESULTS]; unsigned long txt_send_timeout; #ifdef MAX_GROUP_CHANNELS - mesh::GroupChannel channels[MAX_GROUP_CHANNELS]; - int num_channels; + ChannelDetails channels[MAX_GROUP_CHANNELS]; + int num_channels; // only for addChannel() #endif mesh::Packet* _pendingLoopback; uint8_t temp_buf[MAX_TRANS_UNIT]; @@ -89,6 +94,7 @@ protected: { num_contacts = 0; #ifdef MAX_GROUP_CHANNELS + memset(channels, 0, sizeof(channels)); num_channels = 0; #endif txt_send_timeout = 0; @@ -154,7 +160,10 @@ public: bool addContact(const ContactInfo& contact); int getNumContacts() const { return num_contacts; } ContactsIterator startContactsIterator(); - mesh::GroupChannel* addChannel(const char* psk_base64); + ChannelDetails* addChannel(const char* name, const char* psk_base64); + bool getChannel(int idx, ChannelDetails& dest); + bool setChannel(int idx, const ChannelDetails& src); + int findChannelIdx(const mesh::GroupChannel& ch); void loop(); }; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 21cd690f..30777b4b 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -41,6 +41,7 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) { file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.read(pad, 4); // 120 + file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -91,6 +92,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.write(pad, 4); // 120 + file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 file.close(); } @@ -176,6 +178,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base)); } else if (memcmp(config, "txdelay", 7) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); + } else if (memcmp(config, "flood.max", 9) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) { @@ -262,6 +266,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { strcpy(reply, "Error, cannot be negative"); } + } else if (memcmp(config, "flood.max ", 10) == 0) { + uint8_t m = atoi(&config[10]); + if (m <= 64) { + _prefs->flood_max = m; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, max 64"); + } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); if (f >= 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ec038d07..5538f339 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -23,6 +23,7 @@ struct NodePrefs { // persisted to file uint8_t reserved1; uint8_t reserved2; float bw; + uint8_t flood_max; }; class CommonCLICallbacks { diff --git a/src/helpers/CustomSX1262.h b/src/helpers/CustomSX1262.h index 6f66d129..70a1077f 100644 --- a/src/helpers/CustomSX1262.h +++ b/src/helpers/CustomSX1262.h @@ -9,7 +9,7 @@ class CustomSX1262 : public SX1262 { CustomSX1262(Module *mod) : SX1262(mod) { } bool isReceiving() { - uint16_t irq = getIrqStatus(); + uint16_t irq = getIrqFlags(); bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID); return hasPreamble; } diff --git a/src/helpers/CustomSX1268.h b/src/helpers/CustomSX1268.h index 46ce922a..50024693 100644 --- a/src/helpers/CustomSX1268.h +++ b/src/helpers/CustomSX1268.h @@ -9,7 +9,7 @@ class CustomSX1268 : public SX1268 { CustomSX1268(Module *mod) : SX1268(mod) { } bool isReceiving() { - uint16_t irq = getIrqStatus(); + uint16_t irq = getIrqFlags(); bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID); return hasPreamble; } diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 45fb16bd..70e29575 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -17,6 +17,10 @@ public: // for future use, sub-classes SHOULD call this from their begin() startup_reason = BD_STARTUP_NORMAL; + #ifdef ESP32_CPU_FREQ + setCpuFrequencyMhz(ESP32_CPU_FREQ); + #endif + #ifdef PIN_VBAT_READ // battery read support pinMode(PIN_VBAT_READ, INPUT); diff --git a/src/helpers/HeltecV3Board.h b/src/helpers/HeltecV3Board.h index ce742efa..605bfe21 100644 --- a/src/helpers/HeltecV3Board.h +++ b/src/helpers/HeltecV3Board.h @@ -24,12 +24,22 @@ #include class HeltecV3Board : public ESP32Board { +private: + bool adc_active_state; + public: void begin() { ESP32Board::begin(); + // Auto-detect correct ADC_CTRL pin polarity (different for boards >3.2) + pinMode(PIN_ADC_CTRL, INPUT); + adc_active_state = !digitalRead(PIN_ADC_CTRL); + pinMode(PIN_ADC_CTRL, OUTPUT); - //pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_ADC_CTRL, !adc_active_state); // Initially inactive + + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, LOW); // for V3.2 boards esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { @@ -66,9 +76,14 @@ public: 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(10); - digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_ACTIVE); + digitalWrite(PIN_ADC_CTRL, adc_active_state); uint32_t raw = 0; for (int i = 0; i < 8; i++) { @@ -76,7 +91,7 @@ public: } raw = raw / 8; - digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_INACTIVE); + digitalWrite(PIN_ADC_CTRL, !adc_active_state); return (5.2 * (3.3 / 1024.0) * raw) * 1000; } diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index 8d133ccd..03f8f3ce 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -52,10 +52,11 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) { int err = _radio->readData(bytes, len); if (err != RADIOLIB_ERR_NONE) { MESH_DEBUG_PRINTLN("RadioLibWrapper: error: readData(%d)", err); + len = 0; } else { // Serial.print(" readData() -> "); Serial.println(len); + n_recv++; } - n_recv++; } state = STATE_IDLE; // need another startReceive() return len; diff --git a/src/helpers/StationG2Board.h b/src/helpers/StationG2Board.h new file mode 100644 index 00000000..2e31571f --- /dev/null +++ b/src/helpers/StationG2Board.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +// LoRa radio module pins for Station G2 +#define P_LORA_DIO_1 48 +#define P_LORA_NSS 11 +#define P_LORA_RESET 21 +#define P_LORA_BUSY 47 +#define P_LORA_SCLK 12 +#define P_LORA_MISO 14 +#define P_LORA_MOSI 13 + +// built-ins +//#define PIN_LED_BUILTIN 35 +//#define PIN_VEXT_EN 36 + +#include "ESP32Board.h" + +#include + +class StationG2Board : 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 hold 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! + } + + uint16_t getBattMilliVolts() override { + return 0; + } + + const char* getManufacturerName() const override { + return "Station G2"; + } +}; diff --git a/src/helpers/nrf52/T1000eBoard.h b/src/helpers/nrf52/T1000eBoard.h index 14272edb..ce2206e8 100644 --- a/src/helpers/nrf52/T1000eBoard.h +++ b/src/helpers/nrf52/T1000eBoard.h @@ -3,8 +3,6 @@ #include #include -#define HAS_T1000e_POWEROFF - // LoRa and SPI pins #define P_LORA_DIO_1 (32 + 1) // P1.1 #define P_LORA_NSS (0 + 12) // P0.12 @@ -31,8 +29,13 @@ public: uint16_t getBattMilliVolts() override { #ifdef BATTERY_PIN + analogReference(AR_INTERNAL_3_0); analogReadResolution(12); float volts = (analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE) / 4096; + + analogReference(AR_DEFAULT); // put back to default + analogReadResolution(10); + return volts * 1000; #else return 0; @@ -56,7 +59,7 @@ public: return 0; } - void powerOff() { + void powerOff() override { #ifdef HAS_GPS digitalWrite(GPS_VRTC_EN, LOW); digitalWrite(GPS_RESET, LOW); diff --git a/src/helpers/nrf52/T114Board.cpp b/src/helpers/nrf52/T114Board.cpp index 4171bf34..d3d93999 100644 --- a/src/helpers/nrf52/T114Board.cpp +++ b/src/helpers/nrf52/T114Board.cpp @@ -32,6 +32,11 @@ void T114Board::begin() { Wire.begin(); #endif +#ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, HIGH); +#endif + pinMode(SX126X_POWER_EN, OUTPUT); digitalWrite(SX126X_POWER_EN, HIGH); delay(10); // give sx1262 some time to power up diff --git a/src/helpers/nrf52/T114Board.h b/src/helpers/nrf52/T114Board.h index 47dde5c2..35056592 100644 --- a/src/helpers/nrf52/T114Board.h +++ b/src/helpers/nrf52/T114Board.h @@ -28,6 +28,15 @@ public: void begin(); uint8_t getStartupReason() const override { return startup_reason; } +#if defined(P_LORA_TX_LED) + void onBeforeTransmit() override { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + } + void onAfterTransmit() override { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + } +#endif + #define BATTERY_SAMPLES 8 uint16_t getBattMilliVolts() override { diff --git a/src/helpers/nrf52/faketecBoard.cpp b/src/helpers/nrf52/faketecBoard.cpp new file mode 100644 index 00000000..6ef294bc --- /dev/null +++ b/src/helpers/nrf52/faketecBoard.cpp @@ -0,0 +1,63 @@ +#include +#include "faketecBoard.h" + +#include +#include + +static BLEDfu bledfu; + +static void connect_callback(uint16_t conn_handle) +{ + (void)conn_handle; + MESH_DEBUG_PRINTLN("BLE client connected"); +} + +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ + (void)conn_handle; + (void)reason; + + MESH_DEBUG_PRINTLN("BLE client disconnected"); +} + +bool faketecBoard::startOTAUpdate() { + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4 + Bluefruit.setTxPower(4); + // Set the BLE device name + Bluefruit.setName("Faketec_OTA"); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + + // Set up and start advertising + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + + /* Start Advertising + - Enable auto advertising if disconnected + - Interval: fast mode = 20 ms, slow mode = 152.5 ms + - Timeout for fast mode is 30 seconds + - Start(timeout) with timeout = 0 will advertise forever (until connected) + + For recommended advertising interval + https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + + return true; +} diff --git a/src/helpers/nrf52/faketecBoard.h b/src/helpers/nrf52/faketecBoard.h new file mode 100644 index 00000000..b5cebb78 --- /dev/null +++ b/src/helpers/nrf52/faketecBoard.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#define P_LORA_NSS 13 //P1.13 45 +#define P_LORA_DIO_1 11 //P0.10 10 +#define P_LORA_RESET 10 //P0.09 9 +#define P_LORA_BUSY 16 //P0.29 29 +#define P_LORA_MISO 15 //P0.02 2 +#define P_LORA_SCLK 12 //P1.11 43 +#define P_LORA_MOSI 14 //P1.15 47 +#define SX126X_POWER_EN 21 //P0.13 13 +#define SX126X_RXEN 2 //P0.17 +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) + +#define PIN_VBAT_READ 17 +#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking + +class faketecBoard : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + + pinMode(PIN_VBAT_READ, INPUT); + + #if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); + #else + Wire.begin(); + #endif + + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up + } + + uint8_t getStartupReason() const override { return startup_reason; } + + #define BATTERY_SAMPLES 8 + + uint16_t getBattMilliVolts() override { + 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); + } + + const char* getManufacturerName() const override { + return "Faketec DIY"; + } + + void reboot() override { + NVIC_SystemReset(); + } + + bool startOTAUpdate() override; +}; diff --git a/src/helpers/ui/SSD1306Display.cpp b/src/helpers/ui/SSD1306Display.cpp index 84db7d13..fe5be0ca 100644 --- a/src/helpers/ui/SSD1306Display.cpp +++ b/src/helpers/ui/SSD1306Display.cpp @@ -1,7 +1,7 @@ #include "SSD1306Display.h" bool SSD1306Display::begin() { - return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS); + return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false); } void SSD1306Display::turnOn() { diff --git a/variants/promicro_nrf52840/variant.cpp b/variants/promicro_nrf52840/variant.cpp new file mode 100644 index 00000000..0a4c3aac --- /dev/null +++ b/variants/promicro_nrf52840/variant.cpp @@ -0,0 +1,15 @@ +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + 8, 6, 17, 20, 22, 24, 32, 11, 36, 38, + 9, 10, 43, 45, 47, 2, 29, 31, + 33, 34, 37, + 13, 15 +}; + +void initVariant() +{ +} + diff --git a/variants/promicro_nrf52840/variant.h b/variants/promicro_nrf52840/variant.h new file mode 100644 index 00000000..98489da1 --- /dev/null +++ b/variants/promicro_nrf52840/variant.h @@ -0,0 +1,82 @@ +/* + * variant.h + * Copyright (C) 2023 Seeed K.K. + * MIT License + */ + + #pragma once + + #include "WVariant.h" + + //////////////////////////////////////////////////////////////////////////////// + // Low frequency clock source + +#define VARIANT_MCK (64000000ul) + +//#define USE_LFXO // 32.768 kHz crystal oscillator +#define USE_LFRC // 32.768 kHz RC oscillator + +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define PIN_EXT_VCC (21) +#define EXT_VCC (PIN_EXT_VCC) + +#define BATTERY_PIN (17) +#define ADC_RESOLUTION 12 + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (23) +#define NUM_DIGITAL_PINS (23) +#define NUM_ANALOG_INPUTS (3) +#define NUM_ANALOG_OUTPUTS (0) + +//////////////////////////////////////////////////////////////////////////////// +// UART pin definition + +#define PIN_SERIAL1_TX (1) +#define PIN_SERIAL1_RX (0) + +//////////////////////////////////////////////////////////////////////////////// +// I2C pin definition + +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA (6) +#define PIN_WIRE_SCL (7) +#define PIN_WIRE1_SDA (13) +#define PIN_WIRE1_SCL (14) + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_SCK (2) +#define PIN_SPI_MISO (3) +#define PIN_SPI_MOSI (4) + +#define PIN_SPI_NSS (5) + +#define PIN_SPI1_SCK (18) +#define PIN_SPI1_MISO (19) +#define PIN_SPI1_MOSI (20) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs + +#define PIN_LED (22) +#define LED_PIN PIN_LED +#define LED_BLUE PIN_LED +#define LED_BUILTIN PIN_LED +#define LED_STATE_ON 1 + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (6) +#define BUTTON_PIN PIN_BUTTON1 + + diff --git a/variants/t114/variant.cpp b/variants/t114/variant.cpp index 8387e038..4d07d1ae 100644 --- a/variants/t114/variant.cpp +++ b/variants/t114/variant.cpp @@ -11,6 +11,4 @@ const uint32_t g_ADigitalPinMap[] = { void initVariant() { - pinMode(LED_BUILTIN, OUTPUT); - ledOff(LED_BUILTIN); }