diff --git a/.gitignore b/.gitignore
index 51449c2d..7ca9335a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ out/
.direnv/
.DS_Store
.vscode/settings.json
+.vscode/extensions.json
diff --git a/boards/rak3172.json b/boards/rak3172.json
new file mode 100644
index 00000000..dbb70b03
--- /dev/null
+++ b/boards/rak3172.json
@@ -0,0 +1,33 @@
+{
+ "build": {
+ "arduino": {
+ "variant_h": "variant_RAK3172_MODULE.h"
+ },
+ "core": "stm32",
+ "cpu": "cortex-m4",
+ "extra_flags": "-DSTM32WL -DSTM32WLxx -DSTM32WLE5xx",
+ "framework_extra_flags": {
+ "arduino": "-DUSE_CM4_STARTUP_FILE -DARDUINO_RAK3172_MODULE"
+ },
+ "f_cpu": "48000000L",
+ "mcu": "stm32wle5ccu",
+ "product_line": "STM32WLE5xx",
+ "variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U"
+ },
+ "debug": {
+ "default_tools": ["stlink"],
+ "jlink_device": "STM32WLE5CC",
+ "openocd_target": "stm32wlx",
+ "svd_path": "STM32WLE5_CM4.svd"
+ },
+ "frameworks": ["arduino"],
+ "name": "BB-STM32WL",
+ "upload": {
+ "maximum_ram_size": 65536,
+ "maximum_size": 262144,
+ "protocol": "stlink",
+ "protocols": ["stlink", "jlink"]
+ },
+ "url": "https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172",
+ "vendor": "RAK"
+}
diff --git a/docs/faq.md b/docs/faq.md
index 1bb4a0e5..030c5531 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -16,7 +16,7 @@ author: https://github.com/LitBomb
- [1.2.4. Repeater](#124-repeater)
- [1.2.5. Room Server](#125-room-server)
- [2. Initial Setup](#2-initial-setup)
- - [2.1. Q: How many devices do I need to start using meshcore?](#21-q-how-many-devices-do-i-need-to-start-using-meshcore)
+ - [2.1. Q: How many devices do I need to start using MeshCore?](#21-q-how-many-devices-do-i-need-to-start-using-meshcore)
- [2.2. Q: Does MeshCore cost any money?](#22-q-does-meshcore-cost-any-money)
- [2.3. Q: What frequencies are supported by MeshCore?](#23-q-what-frequencies-are-supported-by-meshcore)
- [2.4. Q: What is an "advert" in MeshCore?](#24-q-what-is-an-advert-in-meshcore)
@@ -61,8 +61,8 @@ author: https://github.com/LitBomb
- [6. Troubleshooting](#6-troubleshooting)
- [6.1. Q: My client says another client or a repeater or a room server was last seen many, many days ago.](#61-q-my-client-says-another-client-or-a-repeater-or-a-room-server-was-last-seen-many-many-days-ago)
- [6.2. Q: A repeater or a client or a room server I expect to see on my discover list (on T-Deck) or contact list (on a smart device client) are not listed.](#62-q-a-repeater-or-a-client-or-a-room-server-i-expect-to-see-on-my-discover-list-on-t-deck-or-contact-list-on-a-smart-device-client-are-not-listed)
- - [6.3. Q: How to connect to a repeater via BLE (bluetooth)?](#63-q-how-to-connect-to-a-repeater-via-ble-bluetooth)
- - [6.4. Q: I can't connect via bluetooth, what is the bluetooth pairing code?](#64-q-i-cant-connect-via-bluetooth-what-is-the-bluetooth-pairing-code)
+ - [6.3. Q: How to connect to a repeater via BLE (Bluetooth)?](#63-q-how-to-connect-to-a-repeater-via-ble-bluetooth)
+ - [6.4. Q: I can't connect via Bluetooth, what is the Bluetooth pairing code?](#64-q-i-cant-connect-via-bluetooth-what-is-the-bluetooth-pairing-code)
- [6.5. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.](#65-q-my-heltec-v3-keeps-disconnecting-from-my-smartphone--it-cant-hold-a-solid-bluetooth-connection)
- [7. Other Questions:](#7-other-questions)
- [7.1. Q: How to Update repeater and room server firmware over the air?](#71-q-how-to--update-repeater-and-room-server-firmware-over-the-air)
@@ -89,7 +89,7 @@ Anyone is able to build anything they like on top of MeshCore without paying any
Main web site: [https://meshcore.co.uk/](https://meshcore.co.uk/)
Firmware Flasher: https://flasher.meshcore.co.uk/
Phone Client Applications: https://meshcore.co.uk/apps.html
- MeshCore Fimrware Github: https://github.com/ripplebiz/MeshCore
+ 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.
@@ -119,16 +119,16 @@ Companion radios are for connecting to the Android app or web app as a messenger
#### 1.2.4. Repeater
Repeaters are used to extend the range of a MeshCore network. Repeater firmware runs on the same devices that run client firmware. A repeater's job is to forward MeshCore packets to the destination device. It does **not** forward or retransmit every packet it receives, unlike other LoRa mesh systems.
-A repeater can be remotely administered using a T-Deck running the MeshCore firwmware with remote admistration features unlocked, or from a BLE Companion client connected to a smartphone running the MeshCore app.
+A repeater can be remotely administered using a T-Deck running the MeshCore firmware with remote administration features unlocked, or from a BLE Companion client connected to a smartphone running the MeshCore app.
#### 1.2.5. Room Server
A room server is a simple BBS server for sharing posts. T-Deck devices running MeshCore firmware or a BLE Companion client connected to a smartphone running the MeshCore app can connect to a room server.
-room servers store message history on them, and push the stored messages to users. Room servers allow roaming users to come back later and retrieve message history. Contrast to channels, messages are either received when it's sent, or not received and missed if the a room user is out of range. You can think of room servers like email servers where you can come back later and get your emails from your mail server
+Room servers store message history on them and push the stored messages to users. Room servers allow roaming users to come back later and retrieve message history. With channels, messages are either received when it's sent, or not received and missed if the channel user is out of range. Room servers are different and more like email servers where you can come back later and get your emails from your mail server.
-A room server can be remotely administered using a T-Deck running the MeshCore firwmware with remote admistration features unlocked, or from a BLE Companion client connected to a smartphone running the MeshCore app.
+A room server can be remotely administered using a T-Deck running the MeshCore firmware with remote administration features unlocked, or from a BLE Companion client connected to a smartphone running the MeshCore app.
-When a client logs into a room server, the client will receive the previously 16 unseen messages.
+When a client logs into a room server, the client will receive the previously 32 unseen messages.
A room server can also take on the repeater role. To enable repeater role on a room server, use this command:
@@ -138,26 +138,26 @@ A room server can also take on the repeater role. To enable repeater role on a
## 2. Initial Setup
-### 2.1. Q: How many devices do I need to start using meshcore?
-**A:** If you have one supported device, flash the BLE Companion firmware and use your device as a client. You can connect to the device using the Android client via bluetooth (iOS client will be available later). You can start communiating with other MeshCore users near you.
+### 2.1. Q: How many devices do I need to start using MeshCore?
+**A:** If you have one supported device, flash the BLE Companion firmware and use your device as a client. You can connect to the device using the Android client via Bluetooth (iOS client will be available later). You can start communicating with other MeshCore users near you.
-If you have two supported devices, and there are not many MeshCore users near you, flash both of them to BLE Companion firmware so you can use your devices to communiate with your near-by friends and family.
+If you have two supported devices, and there are not many MeshCore users near you, flash both to BLE Companion firmware so you can use your devices to communicate with your near-by friends and family.
-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.
+If you have two supported devices, and there are other MeshcCore users nearby, 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 to 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:
+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 repeater 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.
+If you have more supported devices, you can use your additional devices with the room server firmware.
### 2.2. Q: Does MeshCore cost any money?
**A:** All radio firmware versions (e.g. for Heltec V3, RAK, T-1000E, etc) are free and open source developed by Scott at Ripple Radios.
-The native Android and iOS client uses the freemium model and is developed by Liam Cottle, developer of meshtastic map at [meshtastic.liamcottle.net](https://meshtastic.liamcottle.net) on [github ](https://github.com/liamcottle/meshtastic-map)and [reticulum-meshchat on github](https://github.com/liamcottle/reticulum-meshchat).
+The native Android and iOS client uses the freemium model and is developed by Liam Cottle, developer of meshtastic map at [meshtastic.liamcottle.net](https://meshtastic.liamcottle.net) on [GitHub](https://github.com/liamcottle/meshtastic-map) and [reticulum-meshchat on github](https://github.com/liamcottle/reticulum-meshchat).
The T-Deck firmware is free to download and most features are available without cost. To support the firmware developer, you can pay for a registration key to unlock your T-Deck for deeper map zoom and remote server administration over RF using the T-Deck. You do not need to pay for the registration to use your T-Deck for direct messaging and connecting to repeaters and room servers.
@@ -178,16 +178,16 @@ the rest of the radio settings are the same for all frequencies:
- Coding Rate (CR): 5
- Bandwidth (BW): 250.00
-(originally MeshCore started with SF 10. recently (as of late April 2025) the community has avocated SF 11 also a viable option for longer range but a little slower transmissions. Currently there are MeshCore meshes with SF 10 and SF 11. Liam Cottle's smartphone app's presets now recommend SF 10 for Australia and SF 11 for all other regions and countries. EU and UK has SF 10 and SF 11 presets. Work with your local meshers on diciding with SF number is best for your use cases. In the future, there may be bridge nodes that can bridge SF 10 and SF 11 (or even different frequencies) traffic.)
+(Originally MeshCore started with SF 10. recently (as of late April 2025) the community has advocated SF 11 also a viable option for longer range but a little slower transmission. Currently there are MeshCore meshes with SF 10 and SF 11. Liam Cottle's smartphone app's presets now recommend SF 10 for Australia and SF 11 for all other regions and countries. EU and UK has SF 10 and SF 11 presets. Work with your local meshers on deciding with SF number is best for your use cases. In the future, there may be bridge nodes that can bridge SF 10 and SF 11 (or even different frequencies) traffic.)
### 2.4. Q: What is an "advert" in MeshCore?
**A:**
-Advert means to advertise yourself on the network. In Reticulum terms it would be to announce. In Meshtastic terms it would be the node sending it's node info.
+Advert means to advertise yourself on the network. In Reticulum terms it would be to announce. In Meshtastic terms it would be the node sending its node info.
MeshCore allows you to manually broadcast your name, position and public encryption key, which is also signed to prevent spoofing. When you click the advert button, it broadcasts that data over LoRa. MeshCore calls that an Advert. There's two ways to advert, "zero hop" and "flood".
* Zero hop means your advert is broadcasted out to anyone that can hear it, and that's it.
-* Flooded means it's broadcasted out, and then repeated by all the repeaters that hear it.
+* Flooded means it's broadcasted out and then repeated by all the repeaters that hear it.
MeshCore clients only advertise themselves when the user initiates it. A repeater (and room server?) advertises its presence once every 240 minutes. This interval can be configured using the following command:
@@ -259,9 +259,9 @@ You can get the latitude and longitude from Google Maps by right-clicking the lo
8. At this point you can begin flashing using
### 4.2. Q: Why is my T-Deck Plus not getting any satellite lock?
-**A:** For T-Deck Plus, the GPS baud rate should be set to **38400**. Also, a number of T-Deck Plus devices were found to have the GPS module installed upside down, with the GPS antenna facing down instead of up. If your T-Deck Plus still doesn't get any satellite lock after setting the baud rate to 38400, you might need to open up the device to check the GPS orientation.
+**A:** For T-Deck Plus, the GPS baud rate should be set to **38400**. Also, some T-Deck Plus devices were found to have the GPS module installed upside down, with the GPS antenna facing down instead of up. If your T-Deck Plus still doesn't get any satellite lock after setting the baud rate to 38400, you might need to open the device to check the GPS orientation.
-GPS on T-Deck is always enabled. You can skip the "GPS clock sync" and the T-Deck will continue to try to get a GPS lock. You can go to the `GPS Info` screen, you should see the `Sentences:` coutner increasing if the baud rate is correct.
+GPS on T-Deck is always enabled. You can skip the "GPS clock sync" and the T-Deck will continue to try to get a GPS lock. You can go to the `GPS Info` screen; you should see the `Sentences:` counter increasing if the baud rate is correct.
[Source]([https://](https://discord.com/channels/826570251612323860/1330643963501351004/1356609240302616689))
@@ -294,7 +294,7 @@ Unlock page:
### 4.8. Q: How to decipher the diagnostics screen on T-Deck?
-**A: ** Space is tight on T-Deck's screen so the information is a bit cryptic. Format is :
+**A: ** Space is tight on T-Deck's screen, so the information is a bit cryptic. The format is :
`{hops} l:{packet-length}({payload-len}) t:{packet-type} snr:{n} rssi:{n}`
See here for packet-type: [https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19](https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19 "https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19")
@@ -337,12 +337,12 @@ Making the bandwidth 2x wider (from BW125 to BW250) allows you to send 2x more b
Lowering the spreading factor makes it more difficult for the gateway to receive a transmission, as it will be more sensitive to noise. You could compare this to two people taking in a noisy place (a bar for example). If you’re far from each other, you have to talk slow (SF10), but if you’re close, you can talk faster (SF7)
-So it's balancing act between speed of the transmission and resistance to noise.
+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
### 5.2. Q: Do MeshCore clients repeat?
-**A:** No, MeshCore clients do not repeat. This is the core of MeshCore's messaging-first design. This is to avoid devices flooding the air ware and create endless collisions so messages sent aren't received.
-In MeshCore, only repeaters and room server with '`set repeat on` repeat.
+**A:** No, MeshCore clients do not repeat. This is the core of MeshCore's messaging-first design. This is to avoid devices flooding the air ware and create endless collisions, so messages sent aren't received.
+In MeshCore, only repeaters and room server with `set repeat on` repeat.
### 5.3. Q: What happens when a node learns a route via a mobile repeater, and that repeater is gone?
@@ -352,13 +352,13 @@ In the case if users are moving around frequently, and the paths are breaking, t
### 5.4. Q: How does a node discovery a path to its destination and then use it to send messages in the future, instead of flooding every message it sends like Meshtastic?
-Routes are stored in sender's contact list. When you send a message the first time, the message first gets to your destination by flood routing, When your destination node gets the message, it sends back to the sender a delivery report with all repeaters that the original message went through. This delivery report is flood-routed back to you the sender and is a basis for future direct path. when you send the next message, the path will get embedded into the packet and be evaluated by repeaters. if the hop and address of the repeater matches, it will retransmit the message, otherwise it will not retransmit, hence minimizing utilization.
+Routes are stored in sender's contact list. When you send a message the first time, the message first gets to your destination by flood routing. When your destination node gets the message, it will send back a delivery report to the sender with all repeaters that the original message went through. This delivery report is flood-routed back to you the sender and is a basis for future direct path. When you send the next message, the path will get embedded into the packet and be evaluated by repeaters. If the hop and address of the repeater matches, it will retransmit the message, otherwise it will not retransmit, hence minimizing utilization.
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1351279141630119996)
### 5.5. Q: Do public channels always flood? Do private channels always flood?
-**A:** Yes, group channels are A to B, so there is no defined path. They have to flood. Repeaters can however deny flood traffic up to some hop limit, with the `set flood.max` CLI command. Admistrators of repeaters get to set the rules of their repeaters.
+**A:** Yes, group channels are A to B, so there is no defined path. They have to flood. Repeaters can however deny flood traffic up to some hop limit, with the `set flood.max` CLI command. Administrators of repeaters get to set the rules of their repeaters.
[Source](https://discord.com/channels/1343693475589263471/1343693475589263474/1350023009527664672)
@@ -490,7 +490,7 @@ adafruit-nrfutil --verbose dfu serial --package t1000_e_bootloader-0.9.1-5-g4887
**A:** Yes. See the following:
#### 5.14.1. meshcoremqtt
-A python based script to send meshore debug and packet capture data to MQTT for analysis
+A Python script to send meshore debug and packet capture data to MQTT for analysis
https://github.com/Andrew-a-g/meshcoretomqtt
#### 5.14.2. MeshCore for Home Assistant
@@ -506,7 +506,7 @@ CLI interface to MeshCore companion radio over BLE, TCP, or serial. Uses Pyton
https://github.com/fdlamotte/meshcore-cli
#### 5.14.5. meshcore.js
-A Javascript library for interacting with a MeshCore device running the companion radio firmware
+A JavaScript library for interacting with a MeshCore device running the companion radio firmware
https://github.com/liamcottle/meshcore.js
---
@@ -521,23 +521,23 @@ https://github.com/liamcottle/meshcore.js
You can get the epoch time on and use it to set your T-Deck clock. For a repeater and room server, the admin can use a T-Deck to remotely set their clock (clock sync), or use the `time` command in the USB serial console with the server device connected.
-### 6.3. Q: How to connect to a repeater via BLE (bluetooth)?
-**A:** You can't connect to a device running repeater firmware via bluetooth. Devices running the BLE companion firmware you can connect to it via bluetooth using the android app
+### 6.3. Q: How to connect to a repeater via BLE (Bluetooth)?
+**A:** You can't connect to a device running repeater firmware via Bluetooth. Devices running the BLE companion firmware you can connect to it via Bluetooth using the android app
-### 6.4. Q: I can't connect via bluetooth, what is the bluetooth pairing code?
+### 6.4. Q: I can't connect via Bluetooth, what is the Bluetooth pairing code?
-**A:** the default bluetooth pairing code is `123456`
+**A:** the default Bluetooth pairing code is `123456`
### 6.5. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.
-**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.
+**A:** Heltec V3 has a very small coil antenna on its PCB for Wi-Fi and Bluetooth connectivity. It has a very short range, only a few feet. It is possible to remove the coil antenna and replace it with a 31mm wire. The BT range is much improved with the modification.
---
## 7. Other Questions:
### 7.1. 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.
+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
diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h
index 2f209c54..09d04266 100644
--- a/examples/companion_radio/NodePrefs.h
+++ b/examples/companion_radio/NodePrefs.h
@@ -19,6 +19,7 @@ struct NodePrefs { // persisted to file
uint8_t tx_power_dbm;
uint8_t telemetry_mode_base;
uint8_t telemetry_mode_loc;
+ uint8_t telemetry_mode_env;
float rx_delay_base;
uint32_t ble_pin;
};
diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp
index 1932512f..675972dc 100644
--- a/examples/companion_radio/UITask.cpp
+++ b/examples/companion_radio/UITask.cpp
@@ -67,6 +67,8 @@ switch(bet){
buzzer.play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7");
break;
case UIEventType::channelMessage:
+ buzzer.play("kerplop:d=16,o=6,b=120:32g#,32c#");
+ break;
case UIEventType::roomMessage:
case UIEventType::newContactMessage:
case UIEventType::none:
@@ -232,40 +234,50 @@ void UITask::userLedHandler() {
}
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();
- _need_refresh = true;
+ #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA)
+ static int prev_btn_state = !USER_BTN_PRESSED;
+ static int prev_btn_state_ana = !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 = 0;
+ int btn_state_ana = 0;
+ #ifdef PIN_USER_BTN
+ btn_state = digitalRead(PIN_USER_BTN);
+ #endif
+ #ifdef PIN_USER_BTN_ANA
+ btn_state_ana = (analogRead(PIN_USER_BTN_ANA) < 20); // analogRead returns a value hopefully below 20 when button is pressed.
+ #endif
+ if (btn_state != prev_btn_state || btn_state_ana != prev_btn_state_ana) { // check for either digital or analogue button change of state
+ if (btn_state == USER_BTN_PRESSED || btn_state_ana == USER_BTN_PRESSED) { // pressed?
+ if (_display != NULL) {
+ if (_display->isOn()) {
+ clearMsgPreview();
+ } else {
+ _display->turnOn();
+ _need_refresh = true;
+ }
+ _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
+ shutdown(); // without restart
}
- _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
- shutdown();
}
+ btn_state_change_time = millis();
+ prev_btn_state = btn_state;
+ prev_btn_state_ana = btn_state_ana;
}
- btn_state_change_time = millis();
- prev_btn_state = btn_state;
+ next_read = millis() + 100; // 10 reads per second
}
- next_read = millis() + 100; // 10 reads per second
+ #endif
}
-#endif
-}
+
/* hardware-agnostic pre-shutdown activity should be done here
*/
diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp
index ecf03b6a..6eee0591 100644
--- a/examples/companion_radio/main.cpp
+++ b/examples/companion_radio/main.cpp
@@ -81,11 +81,11 @@ static uint32_t _atoi(const char* sp) {
#define FIRMWARE_VER_CODE 5
#ifndef FIRMWARE_BUILD_DATE
- #define FIRMWARE_BUILD_DATE "17 May 2025"
+ #define FIRMWARE_BUILD_DATE "24 May 2025"
#endif
#ifndef FIRMWARE_VERSION
- #define FIRMWARE_VERSION "v1.6.1"
+ #define FIRMWARE_VERSION "v1.6.2"
#endif
#define CMD_APP_START 1
@@ -661,6 +661,12 @@ protected:
permissions |= cp & TELEM_PERM_LOCATION;
}
+ if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) {
+ permissions |= TELEM_PERM_ENVIRONMENT;
+ } else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) {
+ permissions |= cp & TELEM_PERM_ENVIRONMENT;
+ }
+
if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set
telemetry.reset();
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
@@ -689,6 +695,7 @@ protected:
if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS;
out_frame[i++] = 0; // legacy: is_admin = false
+ memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix
} else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response
uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16;
if (keep_alive_secs > 0) {
@@ -696,11 +703,13 @@ protected:
}
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS;
out_frame[i++] = data[6]; // permissions (eg. is_admin)
+ memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix
+ memcpy(&out_frame[i], &tag, 4); i += 4; // NEW: include server timestamp
} else {
out_frame[i++] = PUSH_CODE_LOGIN_FAIL;
out_frame[i++] = 0; // reserved
+ memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix
}
- memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix
_serial->writeFrame(out_frame, i);
} else if (len > 4 && // check for status response
pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0 // legacy matching scheme
@@ -824,7 +833,7 @@ public:
file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
file.read((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
file.read((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
- file.read(pad, 1); // 71
+ file.read((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
file.read(pad, 4); // 76
file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
@@ -945,7 +954,7 @@ public:
file.write((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
file.write((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
file.write((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
- file.write(pad, 1); // 71
+ file.write((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
file.write((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
file.write(pad, 4); // 76
file.write((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
@@ -990,7 +999,7 @@ public:
memcpy(&out_frame[i], &lon, 4); i += 4;
out_frame[i++] = 0; // reserved
out_frame[i++] = 0; // reserved
- out_frame[i++] = (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+
+ out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+
out_frame[i++] = _prefs.manual_add_contacts;
uint32_t freq = _prefs.freq * 1000;
@@ -1282,6 +1291,7 @@ public:
if (len >= 3) {
_prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+
_prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03;
+ _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03;
}
savePrefs();
writeOKFrame();
diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp
index 5e04de54..12c843b7 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 "17 May 2025"
+ #define FIRMWARE_BUILD_DATE "24 May 2025"
#endif
#ifndef FIRMWARE_VERSION
- #define FIRMWARE_VERSION "v1.6.1"
+ #define FIRMWARE_VERSION "v1.6.2"
#endif
#ifndef LORA_FREQ
diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp
index 5ffef515..dad7ce78 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 "17 May 2025"
+ #define FIRMWARE_BUILD_DATE "24 May 2025"
#endif
#ifndef FIRMWARE_VERSION
- #define FIRMWARE_VERSION "v1.6.1"
+ #define FIRMWARE_VERSION "v1.6.2"
#endif
#ifndef LORA_FREQ
diff --git a/src/helpers/TBeamS3SupremeBoard.h b/src/helpers/TBeamS3SupremeBoard.h
index 74d9ca2e..ccb8e24c 100644
--- a/src/helpers/TBeamS3SupremeBoard.h
+++ b/src/helpers/TBeamS3SupremeBoard.h
@@ -22,7 +22,7 @@
#define PIN_BOARD_SCL1 41 //SCL for PMU and PFC8563 (RTC)
#define PIN_PMU_IRQ 40 //IRQ pin for PMU
-#define PIN_USER_BTN 0
+//#define PIN_USER_BTN 0
#define P_BOARD_SPI_MOSI 35 //SPI for SD Card and QMI8653 (IMU)
#define P_BOARD_SPI_MISO 37 //SPI for SD Card and QMI8653 (IMU)
@@ -61,10 +61,10 @@ public:
void begin() {
- power_init();
-
ESP32Board::begin();
+ power_init();
+
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) {
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
diff --git a/src/helpers/nrf52/RAK4631Board.cpp b/src/helpers/nrf52/RAK4631Board.cpp
index 8b368734..c75ecf29 100644
--- a/src/helpers/nrf52/RAK4631Board.cpp
+++ b/src/helpers/nrf52/RAK4631Board.cpp
@@ -26,6 +26,10 @@ void RAK4631Board::begin() {
pinMode(PIN_USER_BTN, INPUT_PULLUP);
#endif
+#ifdef PIN_USER_BTN_ANA
+ pinMode(PIN_USER_BTN_ANA, INPUT_PULLUP);
+#endif
+
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
#endif
@@ -76,6 +80,11 @@ bool RAK4631Board::startOTAUpdate(const char* id, char reply[]) {
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
- strcpy(reply, "OK - started");
+ uint8_t mac_addr[6];
+ memset(mac_addr, 0, sizeof(mac_addr));
+ Bluefruit.getAddr(mac_addr);
+ sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
+
return true;
}
diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp
new file mode 100644
index 00000000..dda1fd55
--- /dev/null
+++ b/src/helpers/sensors/EnvironmentSensorManager.cpp
@@ -0,0 +1,231 @@
+#include "EnvironmentSensorManager.h"
+
+#if ENV_INCLUDE_AHTX0
+#define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address
+#include
+static Adafruit_AHTX0 AHTX0;
+#endif
+
+#if ENV_INCLUDE_BME280
+#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address
+#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level
+#include
+static Adafruit_BME280 BME280;
+#endif
+
+#if ENV_INCLUDE_INA3221
+#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address
+#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts
+#define TELEM_INA3221_NUM_CHANNELS 3
+#include
+static Adafruit_INA3221 INA3221;
+#endif
+
+#if ENV_INCLUDE_INA219
+#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address
+#include
+static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS);
+#endif
+
+bool EnvironmentSensorManager::begin() {
+ #if ENV_INCLUDE_GPS
+ initBasicGPS();
+ #endif
+
+ #if ENV_INCLUDE_AHTX0
+ if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) {
+ MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS);
+ AHTX0_initialized = true;
+ } else {
+ AHTX0_initialized = false;
+ MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS);
+ }
+ #endif
+
+ #if ENV_INCLUDE_BME280
+ if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) {
+ MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS);
+ MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID());
+ BME280_initialized = true;
+ } else {
+ BME280_initialized = false;
+ MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS);
+ }
+ #endif
+
+ #if ENV_INCLUDE_INA3221
+ if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) {
+ MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS);
+ MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID());
+
+ for(int i = 0; i < 3; i++) {
+ INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE);
+ }
+ INA3221_initialized = true;
+ } else {
+ INA3221_initialized = false;
+ MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS);
+ }
+ #endif
+
+ #if ENV_INCLUDE_INA219
+ if (INA219.begin(&Wire)) {
+ MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS);
+ INA219_initialized = true;
+ } else {
+ INA219_initialized = false;
+ MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS);
+ }
+ #endif
+
+ return true;
+}
+
+bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
+ next_available_channel = TELEM_CHANNEL_SELF + 1;
+
+ if (requester_permissions & TELEM_PERM_LOCATION) {
+ telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, 0.0f); // allow lat/lon via telemetry even if no GPS is detected
+ }
+
+ if (requester_permissions & TELEM_PERM_ENVIRONMENT) {
+
+ #if ENV_INCLUDE_AHTX0
+ if (AHTX0_initialized) {
+ sensors_event_t humidity, temp;
+ AHTX0.getEvent(&humidity, &temp);
+ telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature);
+ telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity);
+ }
+ #endif
+
+ #if ENV_INCLUDE_BME280
+ if (BME280_initialized) {
+ telemetry.addTemperature(TELEM_CHANNEL_SELF, BME280.readTemperature());
+ telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME280.readHumidity());
+ telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME280.readPressure());
+ telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA));
+ }
+ #endif
+
+ #if ENV_INCLUDE_INA3221
+ if (INA3221_initialized) {
+ for(int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) {
+ // add only enabled INA3221 channels to telemetry
+ if (INA3221.isChannelEnabled(i)) {
+ float voltage = INA3221.getBusVoltage(i);
+ float current = INA3221.getCurrentAmps(i);
+ telemetry.addVoltage(next_available_channel, voltage);
+ telemetry.addCurrent(next_available_channel, current);
+ telemetry.addPower(next_available_channel, voltage * current);
+ next_available_channel++;
+ }
+ }
+ }
+ #endif
+
+ #if ENV_INCLUDE_INA219
+ if (INA219_initialized) {
+ telemetry.addVoltage(next_available_channel, INA219.getBusVoltage_V());
+ telemetry.addCurrent(next_available_channel, INA219.getCurrent_mA() / 1000);
+ telemetry.addPower(next_available_channel, INA219.getPower_mW() / 1000);
+ next_available_channel++;
+ }
+ #endif
+
+ }
+
+ return true;
+}
+
+
+int EnvironmentSensorManager::getNumSettings() const {
+ #if ENV_INCLUDE_GPS
+ return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected
+ #else
+ return 0;
+ #endif
+}
+
+const char* EnvironmentSensorManager::getSettingName(int i) const {
+ #if ENV_INCLUDE_GPS
+ return (gps_detected && i == 0) ? "gps" : NULL;
+ #else
+ return NULL;
+ #endif
+}
+
+const char* EnvironmentSensorManager::getSettingValue(int i) const {
+ #if ENV_INCLUDE_GPS
+ if (gps_detected && i == 0) {
+ return gps_active ? "1" : "0";
+ }
+ #endif
+ return NULL;
+}
+
+bool EnvironmentSensorManager::setSettingValue(const char* name, const char* value) {
+ #if ENV_INCLUDE_GPS
+ if (gps_detected && strcmp(name, "gps") == 0) {
+ if (strcmp(value, "0") == 0) {
+ stop_gps();
+ } else {
+ start_gps();
+ }
+ return true;
+ }
+ #endif
+ return false; // not supported
+}
+
+#if ENV_INCLUDE_GPS
+void EnvironmentSensorManager::initBasicGPS() {
+ Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX);
+ Serial1.begin(9600);
+
+ // Try to detect if GPS is physically connected to determine if we should expose the setting
+ pinMode(PIN_GPS_EN, OUTPUT);
+ digitalWrite(PIN_GPS_EN, HIGH); // Power on GPS
+
+ // Give GPS a moment to power up and send data
+ delay(1000);
+
+ // We'll consider GPS detected if we see any data on Serial1
+ gps_detected = (Serial1.available() > 0);
+
+ if (gps_detected) {
+ MESH_DEBUG_PRINTLN("GPS detected");
+ digitalWrite(PIN_GPS_EN, LOW); // Power off GPS until the setting is changed
+ } else {
+ MESH_DEBUG_PRINTLN("No GPS detected");
+ digitalWrite(PIN_GPS_EN, LOW);
+ }
+}
+
+void EnvironmentSensorManager::start_gps() {
+ gps_active = true;
+ pinMode(PIN_GPS_EN, OUTPUT);
+ digitalWrite(PIN_GPS_EN, HIGH);
+}
+
+void EnvironmentSensorManager::stop_gps() {
+ gps_active = false;
+ pinMode(PIN_GPS_EN, OUTPUT);
+ digitalWrite(PIN_GPS_EN, LOW);
+}
+
+void EnvironmentSensorManager::loop() {
+ static long next_gps_update = 0;
+
+ _location->loop();
+
+ if (millis() > next_gps_update) {
+ if (_location->isValid()) {
+ node_lat = ((double)_location->getLatitude())/1000000.;
+ node_lon = ((double)_location->getLongitude())/1000000.;
+ MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
+ }
+ next_gps_update = millis() + 1000;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h
new file mode 100644
index 00000000..c9ec6870
--- /dev/null
+++ b/src/helpers/sensors/EnvironmentSensorManager.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+#include
+
+class EnvironmentSensorManager : public SensorManager {
+protected:
+ int next_available_channel = TELEM_CHANNEL_SELF + 1;
+
+ bool AHTX0_initialized = false;
+ bool BME280_initialized = false;
+ bool INA3221_initialized = false;
+ bool INA219_initialized = false;
+
+ bool gps_detected = false;
+ bool gps_active = false;
+
+ #if ENV_INCLUDE_GPS
+ LocationProvider* _location;
+ void start_gps();
+ void stop_gps();
+ void initBasicGPS();
+ #endif
+
+
+public:
+ #if ENV_INCLUDE_GPS
+ EnvironmentSensorManager(LocationProvider &location): _location(&location){};
+ #else
+ EnvironmentSensorManager(){};
+ #endif
+ bool begin() override;
+ bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
+ #if ENV_INCLUDE_GPS
+ void loop() override;
+ #endif
+ int getNumSettings() const override;
+ const char* getSettingName(int i) const override;
+ const char* getSettingValue(int i) const override;
+ bool setSettingValue(const char* name, const char* value) override;
+};
diff --git a/src/helpers/stm32/InternalFileSystem.cpp b/src/helpers/stm32/InternalFileSystem.cpp
index 47706bac..2714ec6b 100644
--- a/src/helpers/stm32/InternalFileSystem.cpp
+++ b/src/helpers/stm32/InternalFileSystem.cpp
@@ -126,6 +126,9 @@ InternalFileSystem::InternalFileSystem(void)
bool InternalFileSystem::begin(void)
{
+ #ifdef FORMAT_FS
+ this->format();
+ #endif
// failed to mount, erase all sector then format and mount again
if ( !Adafruit_LittleFS::begin() )
{
diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini
index f54eda86..d62771f4 100644
--- a/variants/heltec_tracker/platformio.ini
+++ b/variants/heltec_tracker/platformio.ini
@@ -31,6 +31,8 @@ build_src_filter = ${esp32_base.build_src_filter}
+<../variants/heltec_tracker>
lib_deps =
${esp32_base.lib_deps}
+ stevemarple/MicroNMEA @ ^2.0.6
+ adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0
[env:Heltec_Wireless_Tracker_companion_radio_ble]
extends = Heltec_tracker_base
@@ -56,5 +58,43 @@ build_src_filter = ${Heltec_tracker_base.build_src_filter}
lib_deps =
${Heltec_tracker_base.lib_deps}
densaugeo/base64 @ ~1.4.0
- stevemarple/MicroNMEA @ ^2.0.6
- adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0
+
+[env:Heltec_Wireless_Tracker_repeater]
+extends = Heltec_tracker_base
+build_flags =
+ ${Heltec_tracker_base.build_flags}
+ -D DISPLAY_ROTATION=1
+ -D DISPLAY_CLASS=ST7735Display
+ -D ADVERT_NAME='"Heltec Repeater"'
+ -D ADVERT_LAT=0.0
+ -D ADVERT_LON=0.0
+ -D ADMIN_PASSWORD='"password"'
+ -D MAX_NEIGHBOURS=8
+; -D MESH_PACKET_LOGGING=1
+; -D MESH_DEBUG=1
+build_src_filter = ${Heltec_tracker_base.build_src_filter}
+ +
+ +<../examples/simple_repeater>
+lib_deps =
+ ${Heltec_tracker_base.lib_deps}
+ ${esp32_ota.lib_deps}
+
+[env:Heltec_Wireless_Tracker_room_server]
+extends = Heltec_tracker_base
+build_flags =
+ ${Heltec_tracker_base.build_flags}
+ -D DISPLAY_ROTATION=1
+ -D DISPLAY_CLASS=ST7735Display
+ -D ADVERT_NAME='"Heltec Room"'
+ -D ADVERT_LAT=0.0
+ -D ADVERT_LON=0.0
+ -D ADMIN_PASSWORD='"password"'
+ -D ROOM_PASSWORD='"hello"'
+; -D MESH_PACKET_LOGGING=1
+; -D MESH_DEBUG=1
+build_src_filter = ${Heltec_tracker_base.build_src_filter}
+ +
+ +<../examples/simple_room_server>
+lib_deps =
+ ${Heltec_tracker_base.lib_deps}
+ ${esp32_ota.lib_deps}
diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini
index 0814ad4d..28c6d562 100644
--- a/variants/heltec_v3/platformio.ini
+++ b/variants/heltec_v3/platformio.ini
@@ -17,11 +17,20 @@ build_flags =
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
+ -D ENV_INCLUDE_AHTX0=1
+ -D ENV_INCLUDE_BME280=1
+ -D ENV_INCLUDE_INA3221=1
+ -D ENV_INCLUDE_INA219=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/heltec_v3>
+ +
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
+ adafruit/Adafruit INA3221 Library @ ^1.0.1
+ adafruit/Adafruit INA219 @ ^1.2.3
+ adafruit/Adafruit AHTX0 @ ^2.0.5
+ adafruit/Adafruit BME280 Library @ ^2.3.0
[env:Heltec_v3_repeater]
extends = Heltec_lora32_v3
diff --git a/variants/heltec_v3/target.cpp b/variants/heltec_v3/target.cpp
index ab9b709f..be99a1e3 100644
--- a/variants/heltec_v3/target.cpp
+++ b/variants/heltec_v3/target.cpp
@@ -14,7 +14,7 @@ WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
-SensorManager sensors;
+EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h
index 76ad58a7..701f9cd5 100644
--- a/variants/heltec_v3/target.h
+++ b/variants/heltec_v3/target.h
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#ifdef DISPLAY_CLASS
#include
#endif
@@ -14,7 +15,7 @@
extern HeltecV3Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
-extern SensorManager sensors;
+extern EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini
index e618613c..4e6721f9 100644
--- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini
+++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini
@@ -8,19 +8,22 @@ build_flags =
-D P_LORA_TX_LED=6
-D PIN_BOARD_SDA=17
-D PIN_BOARD_SCL=18
+ -D PIN_USER_BTN=0
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
- ;-D DISPLAY_CLASS=SSD1306Display ;Needs to be modified for SH1106
+ -D DISPLAY_CLASS=SH1106Display
-D SX126X_RX_BOOSTED_GAIN=1
-D SX126X_CURRENT_LIMIT=140
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lilygo_tbeam_supreme_SX1262>
+ +
board_build.partitions = min_spiffs.csv ; get around 4mb flash limit
lib_deps =
${esp32_base.lib_deps}
lewisxhe/XPowersLib @ ^0.2.7
- ;adafruit/Adafruit SSD1306 @ ^2.5.13
+ adafruit/Adafruit SH110X @ ^2.1.13
stevemarple/MicroNMEA @ ^2.0.6
+ adafruit/Adafruit BME280 Library @ ^2.3.0
; === LILYGO T-Beam S3 Supreme with SX1262 environments ===
[env:T_Beam_S3_Supreme_SX1262_repeater]
@@ -64,8 +67,8 @@ build_flags =
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
- -D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
+; -D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=8
diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp
index f5b7080b..4b38b11d 100644
--- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp
+++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp
@@ -4,6 +4,10 @@
TBeamS3SupremeBoard board;
+#ifdef DISPLAY_CLASS
+ DISPLAY_CLASS display;
+#endif
+
bool pmuIntFlag;
#ifndef LORA_CR
@@ -46,7 +50,7 @@ void scanDevices(TwoWire *w)
switch (addr) {
case 0x77:
case 0x76:
- Serial.println("\tFound BMX280 Sensor");
+ Serial.println("\tFound BME280 Sensor");
deviceOnline |= BME280_ONLINE;
break;
case 0x34:
@@ -107,6 +111,26 @@ void TBeamS3SupremeBoard::printPMU()
Serial.println();
}
+void TbeamSupSensorManager::printBMEValues() {
+ Serial.print("Temperature = ");
+ Serial.print(bme.readTemperature());
+ Serial.println(" *C");
+
+ Serial.print("Pressure = ");
+
+ Serial.print(bme.readPressure() / 100.0F);
+ Serial.println(" hPa");
+
+ Serial.print("Approx. Altitude = ");
+ Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
+ Serial.println(" m");
+
+ Serial.print("Humidity = ");
+ Serial.print(bme.readHumidity());
+ Serial.println(" %");
+
+ Serial.println();
+}
#endif
bool TBeamS3SupremeBoard::power_init()
@@ -119,9 +143,9 @@ bool TBeamS3SupremeBoard::power_init()
PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
// Set up PMU interrupts
- // MESH_DEBUG_PRINTLN("Setting up PMU interrupts");
- // pinMode(PIN_PMU_IRQ, INPUT_PULLUP);
- // attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING);
+ MESH_DEBUG_PRINTLN("Setting up PMU interrupts");
+ pinMode(PIN_PMU_IRQ, INPUT_PULLUP);
+ attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING);
// GPS
MESH_DEBUG_PRINTLN("Setting and enabling a-ldo4 for GPS");
@@ -164,22 +188,22 @@ bool TBeamS3SupremeBoard::power_init()
PMU.enableBLDO1();
// Out to header pins
- // MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header");
- // PMU.setBLDO2Voltage(3300);
- // PMU.enableBLDO2();
+ MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header");
+ PMU.setBLDO2Voltage(3300);
+ PMU.enableBLDO2();
- // MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header");
- // PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V
- // PMU.enableDC4();
+ MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header");
+ PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V
+ PMU.enableDC4();
- // MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header");
- // PMU.setDC5Voltage(3300);
- // PMU.enableDC5();
+ MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header");
+ PMU.setDC5Voltage(3300);
+ PMU.enableDC5();
// Unused power rails
- MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dldo1 and dldo2");
+ MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dcdc5, dldo1 and dldo2");
PMU.disableDC2();
- PMU.disableDC5();
+ //PMU.disableDC5();
PMU.disableDLDO1();
PMU.disableDLDO2();
@@ -199,18 +223,18 @@ bool TBeamS3SupremeBoard::power_init()
PMU.enableVbusVoltageMeasure();
// Reset and re-enable PMU interrupts
- // MESH_DEBUG_PRINTLN("Re-enable interrupts");
- // PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
- // PMU.clearIrqStatus();
- // PMU.enableIRQ(
- // XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts
- // XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts
- // XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts
- // XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts
- // );
+ MESH_DEBUG_PRINTLN("Re-enable interrupts");
+ PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
+ PMU.clearIrqStatus();
+ PMU.enableIRQ(
+ XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts
+ XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts
+ XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts
+ XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts
+ );
#ifdef MESH_DEBUG
- // scanDevices(&Wire);
- // scanDevices(&Wire1);
+ scanDevices(&Wire);
+ scanDevices(&Wire1);
printPMU();
#endif
@@ -235,60 +259,14 @@ static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, u
return millis() < timeout; // false, if timed out
}
-static bool l76kProbe()
-{
- bool result = false;
- uint32_t startTimeout ;
- Serial1.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
- delay(5);
- // Get version information
- startTimeout = millis() + 3000;
- MESH_DEBUG_PRINTLN("Trying to init L76K GPS");
- // Serial1.flush();
- while (Serial1.available()) {
- int c = Serial1.read();
- // Serial.write(c);
- // Serial.print(".");
- // Serial.flush();
- // Serial1.flush();
- if (millis() > startTimeout) {
- MESH_DEBUG_PRINTLN("L76K NMEA timeout!");
- return false;
- }
- };
-
- Serial1.flush();
- delay(200);
-
- Serial1.write("$PCAS06,0*1B\r\n");
-
- char ver[100];
- if (!readStringUntil(Serial1, ver, sizeof(ver), '\n', 500)) {
- MESH_DEBUG_PRINTLN("Get L76K timeout!");
- return false;
- }
-
- if (memcmp(ver, "$GPTXT,01,01,02", 15) == 0) {
- MESH_DEBUG_PRINTLN("L76K GNSS init succeeded, using L76K GNSS Module\n");
- result = true;
- }
- delay(500);
-
- // Initialize the L76K Chip, use GPS + GLONASS
- Serial1.write("$PCAS04,5*1C\r\n");
- delay(250);
- // only ask for RMC and GGA
- Serial1.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
- delay(250);
- // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
- Serial1.write("$PCAS11,3*1E\r\n");
- return result;
-}
-
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire1);
+
+ // #ifdef MESH_DEBUG
+ // printBMEValues();
+ // #endif
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
@@ -340,55 +318,95 @@ void TbeamSupSensorManager::sleep_gps() {
}
bool TbeamSupSensorManager::begin() {
+ //init BME280
+ if (! bme.begin(0x77, &Wire)) {
+ MESH_DEBUG_PRINTLN("Could not find a valid BME280 sensor");
+ bme_active = false;
+ }
+ else
+ MESH_DEBUG_PRINTLN("BME280 found and init!");
+ bme_active = true;
+
// init GPS port
-
Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, P_GPS_RX, P_GPS_TX);
- bool result = false;
- for ( int i = 0; i < 3; ++i) {
- result = l76kProbe();
- if (result) {
- gps_active = true;
- return result;
- }
- }
- return result;
+ MESH_DEBUG_PRINTLN("Sleeping GPS for initial state");
+ sleep_gps();
+ return true;
}
bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission?
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
}
+ if (requester_permissions & TELEM_PERM_ENVIRONMENT && bme_active) { // does requester have permission?
+ telemetry.addTemperature(TELEM_CHANNEL_SELF, node_temp);
+ telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, node_hum);
+ telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, node_pres);
+ //telemetry.addAltitude(TELEM_CHANNEL_SELF, node_alt);
+ }
return true;
}
void TbeamSupSensorManager::loop() {
- static long next_gps_update = 0;
+ static long next_update = 0;
_nmea->loop();
- if (millis() > next_gps_update) {
- if (_nmea->isValid()) {
+ if (millis() > next_update) {
+ if (_nmea->isValid() && gps_active) {
node_lat = ((double)_nmea->getLatitude())/1000000.;
node_lon = ((double)_nmea->getLongitude())/1000000.;
node_altitude = ((double)_nmea->getAltitude()) / 1000.0;
- //Serial.printf("lat %f lon %f\r\n", _lat, _lon);
+ MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude);
}
- next_gps_update = millis() + 1000;
+
+ //read BME280 values
+ if(bme_active){
+ //node_alt = bme.readAltitude(SEALEVELPRESSURE_HPA);
+ node_temp = bme.readTemperature();
+ node_hum = bme.readHumidity();
+ node_pres = (bme.readPressure() / 100.0F);
+
+ #ifdef MESH_DEBUG
+ // Serial.print("Temperature = ");
+ // Serial.print(node_temp);
+ // Serial.println(" *C");
+
+ // Serial.print("Humidity = ");
+ // Serial.print(node_hum);
+ // Serial.println(" %");
+
+ // Serial.print("Pressure = ");
+ // Serial.print(node_pres);
+ // Serial.println(" hPa");
+
+ // Serial.print("Approx. Altitude = ");
+ // Serial.print(node_alt);
+ // Serial.println(" m");
+ #endif
+ }
+
+ next_update = millis() + 1000;
}
}
-int TbeamSupSensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch)
+int TbeamSupSensorManager::getNumSettings() const {
+ return 1;
+}
const char* TbeamSupSensorManager::getSettingName(int i) const {
- return i == 0 ? "gps" : NULL;
+ switch(i){
+ case 0: return "gps";
+ default: NULL;
+ }
}
const char* TbeamSupSensorManager::getSettingValue(int i) const {
- if (i == 0) {
- return gps_active ? "1" : "0";
+ switch(i){
+ case 0: return gps_active == true ? "1" : "0";
+ default: NULL;
}
- return NULL;
}
bool TbeamSupSensorManager::setSettingValue(const char* name, const char* value) {
diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h
index 107e2950..a3023750 100644
--- a/variants/lilygo_tbeam_supreme_SX1262/target.h
+++ b/variants/lilygo_tbeam_supreme_SX1262/target.h
@@ -8,15 +8,21 @@
#include
#include
#include
+#include
class TbeamSupSensorManager: public SensorManager {
bool gps_active = false;
+ bool bme_active = false;
LocationProvider * _nmea;
-
+ Adafruit_BME280 bme;
+ double node_temp, node_hum, node_pres;
+
+ #define SEALEVELPRESSURE_HPA (1013.25)
+
void start_gps();
void sleep_gps();
public:
- TbeamSupSensorManager(LocationProvider &nmea): _nmea(&nmea) { }
+ TbeamSupSensorManager(LocationProvider &nmea): _nmea(&nmea) {node_temp = 0; node_hum = 0; node_pres = 0;}
bool begin() override;
bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
void loop() override;
@@ -24,6 +30,11 @@ class TbeamSupSensorManager: public SensorManager {
const char* getSettingName(int i) const override;
const char* getSettingValue(int i) const override;
bool setSettingValue(const char* name, const char* value) override;
+
+ #ifdef MESH_DEBUG
+ void printBMEValues();
+ #endif
+
};
extern TBeamS3SupremeBoard board;
@@ -31,6 +42,11 @@ extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern TbeamSupSensorManager sensors;
+#ifdef DISPLAY_CLASS
+ #include
+ extern DISPLAY_CLASS display;
+#endif
+
enum {
POWERMANAGE_ONLINE = _BV(0),
DISPLAY_ONLINE = _BV(1),
@@ -48,7 +64,6 @@ enum {
OSC32768_ONLINE = _BV(13),
};
-bool power_init();
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini
index 51fbc92f..11f73d81 100644
--- a/variants/promicro/platformio.ini
+++ b/variants/promicro/platformio.ini
@@ -13,15 +13,26 @@ build_flags = ${nrf52840_base.build_flags}
-D PIN_BOARD_SDA=8
-D PIN_OLED_RESET=-1
-D PIN_USER_BTN=6
+ -D PIN_GPS_RX=3
+ -D PIN_GPS_TX=4
+ -D PIN_GPS_EN=5
+ -D ENV_INCLUDE_GPS=1
+ -D ENV_INCLUDE_AHTX0=1
+ -D ENV_INCLUDE_BME280=1
+ -D ENV_INCLUDE_INA3221=1
+ -D ENV_INCLUDE_INA219=1
build_src_filter = ${nrf52840_base.build_src_filter}
+
+ +
+<../variants/promicro>
lib_deps= ${nrf52840_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
- robtillaart/INA3221 @ ^0.4.1
- robtillaart/INA219 @ ^0.4.1
- adafruit/Adafruit AHTX0@^2.0.5
-
+ adafruit/Adafruit INA3221 Library @ ^1.0.1
+ adafruit/Adafruit INA219 @ ^1.2.3
+ adafruit/Adafruit AHTX0 @ ^2.0.5
+ adafruit/Adafruit BME280 Library @ ^2.3.0
+ stevemarple/MicroNMEA @ ^2.0.6
+
[env:Faketec_Repeater]
extends = Faketec
build_src_filter = ${Faketec.build_src_filter}
@@ -97,7 +108,7 @@ build_flags = ${Faketec.build_flags}
-D OFFLINE_QUEUE_SIZE=256
-D DISPLAY_CLASS=SSD1306Display
; -D MESH_PACKET_LOGGING=1
-; -D MESH_DEBUG=1
+ -D MESH_DEBUG=1
build_src_filter = ${Faketec.build_src_filter}
+
+<../examples/companion_radio>
@@ -120,11 +131,13 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter =
${nrf52840_base.build_src_filter}
+
+ +
+<../variants/promicro>
lib_deps= ${nrf52840_base.lib_deps}
- robtillaart/INA3221 @ ^0.4.1
- robtillaart/INA219 @ ^0.4.1
- adafruit/Adafruit AHTX0@^2.0.5
+ adafruit/Adafruit INA3221 Library @ ^1.0.1
+ adafruit/Adafruit INA219 @ ^1.2.3
+ adafruit/Adafruit AHTX0 @ ^2.0.5
+ adafruit/Adafruit BME280 Library @ ^2.3.0
[env:ProMicroLLCC68_Repeater]
extends = ProMicroLLCC68
diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp
index 6e3dc938..2369bd7a 100644
--- a/variants/promicro/target.cpp
+++ b/variants/promicro/target.cpp
@@ -2,6 +2,9 @@
#include "target.h"
#include
+#if ENV_INCLUDE_GPS
+#endif
+
PromicroBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
@@ -10,7 +13,13 @@ WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
-PromicroSensorManager sensors;
+#if ENV_INCLUDE_GPS
+ #include
+ MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
+ EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
+#else
+ EnvironmentSensorManager sensors;
+#endif
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
@@ -79,120 +88,3 @@ mesh::LocalIdentity radio_new_identity() {
return mesh::LocalIdentity(&rng); // create new random identity
}
-static INA3221 INA_3221(TELEM_INA3221_ADDRESS, &Wire);
-static INA219 INA_219(TELEM_INA219_ADDRESS, &Wire);
-static Adafruit_AHTX0 AHTX;
-
-bool PromicroSensorManager::begin() {
- initINA3221();
- initINA219();
- initAHTX();
-
- return true;
-}
-
-bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
- int nextAvalableChannel = TELEM_CHANNEL_SELF + 1;
- if (requester_permissions & TELEM_PERM_ENVIRONMENT) {
- if (INA3221initialized) {
- for(int i = 0; i < 3; i++) {
- // add only enabled INA3221 channels to telemetry
- if (INA3221_CHANNEL_ENABLED[i]) {
- telemetry.addVoltage(nextAvalableChannel, INA_3221.getBusVoltage(i));
- telemetry.addCurrent(nextAvalableChannel, INA_3221.getCurrent(i));
- telemetry.addPower(nextAvalableChannel, INA_3221.getPower(i));
- nextAvalableChannel++;
- }
- }
- }
- if (INA219initialized) {
- telemetry.addVoltage(nextAvalableChannel, INA_219.getBusVoltage());
- telemetry.addCurrent(nextAvalableChannel, INA_219.getCurrent());
- telemetry.addPower(nextAvalableChannel, INA_219.getPower());
- nextAvalableChannel++;
- }
- if (AHTXinitialized) {
- sensors_event_t humidity, temp;
- AHTX.getEvent(&humidity, &temp);
- telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature);
- telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity);
- }
- }
-
- return true;
-}
-
-int PromicroSensorManager::getNumSettings() const {
- return NUM_SENSOR_SETTINGS;
-}
-
-const char* PromicroSensorManager::getSettingName(int i) const {
- if (i >= 0 && i < NUM_SENSOR_SETTINGS) {
- return INA3221_CHANNEL_NAMES[i];
- }
- return NULL;
-}
-
-const char* PromicroSensorManager::getSettingValue(int i) const {
- if (i >= 0 && i < NUM_SENSOR_SETTINGS) {
- return INA3221_CHANNEL_ENABLED[i] ? "1" : "0";
- }
- return NULL;
-}
-
-bool PromicroSensorManager::setSettingValue(const char* name, const char* value) {
- for (int i = 0; i < NUM_SENSOR_SETTINGS; i++) {
- if (strcmp(name, INA3221_CHANNEL_NAMES[i]) == 0) {
- int channelEnabled = INA_3221.getEnableChannel(i);
- if (strcmp(value, "1") == 0) {
- INA3221_CHANNEL_ENABLED[i] = true;
- if (!channelEnabled) {
- INA_3221.enableChannel(i);
- }
- } else {
- INA3221_CHANNEL_ENABLED[i] = false;
- if (channelEnabled) {
- INA_3221.disableChannel(i);
- }
- }
- return true;
- }
- }
- return false;
-}
-
-void PromicroSensorManager::initINA3221() {
- if (INA_3221.begin()) {
- MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", INA_3221.getAddress());
- MESH_DEBUG_PRINTLN("%04X %04X %04X", INA_3221.getDieID(), INA_3221.getManufacturerID(), INA_3221.getConfiguration());
-
- for(int i = 0; i < 3; i++) {
- INA_3221.setShuntR(i, TELEM_INA3221_SHUNT_VALUE);
- }
- INA3221initialized = true;
- } else {
- INA3221initialized = false;
- MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS);
- }
-}
-
-void PromicroSensorManager::initINA219() {
- if (INA_219.begin()) {
- MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", INA_219.getAddress());
- INA_219.setMaxCurrentShunt(TELEM_INA219_MAX_CURRENT, TELEM_INA219_SHUNT_VALUE);
- INA219initialized = true;
- } else {
- INA219initialized = false;
- MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS);
- }
-}
-
-void PromicroSensorManager::initAHTX() {
- if (AHTX.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) {
- MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS);
- AHTXinitialized = true;
- } else {
- AHTXinitialized = false;
- MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS);
- }
-}
diff --git a/variants/promicro/target.h b/variants/promicro/target.h
index b7475b4a..c634d18a 100644
--- a/variants/promicro/target.h
+++ b/variants/promicro/target.h
@@ -7,19 +7,16 @@
#include
#include
#include
-#include
-#include
-#include
-#include
#ifdef DISPLAY_CLASS
#include
#endif
-#define NUM_SENSOR_SETTINGS 3
+#include
extern PromicroBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
+extern EnvironmentSensorManager sensors;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
@@ -31,39 +28,3 @@ void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();
-#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address
-#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address
-#define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address
-
-#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts
-#define TELEM_INA3221_SETTING_CH1 "INA3221-1"
-#define TELEM_INA3221_SETTING_CH2 "INA3221-2"
-#define TELEM_INA3221_SETTING_CH3 "INA3221-3"
-
-#define TELEM_INA219_SHUNT_VALUE 0.100 // shunt value in ohms (may differ between manufacturers)
-#define TELEM_INA219_MAX_CURRENT 5
-
-class PromicroSensorManager: public SensorManager {
- bool INA3221initialized = false;
- bool INA219initialized = false;
- bool AHTXinitialized = false;
-
- // INA3221 channels in telemetry
- const char * INA3221_CHANNEL_NAMES[NUM_SENSOR_SETTINGS] = { TELEM_INA3221_SETTING_CH1, TELEM_INA3221_SETTING_CH2, TELEM_INA3221_SETTING_CH3};
- bool INA3221_CHANNEL_ENABLED[NUM_SENSOR_SETTINGS] = {true, true, true};
-
- void initINA3221();
- void initINA219();
- void initAHTX();
-public:
- PromicroSensorManager(){};
- bool begin() override;
- bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
- int getNumSettings() const override;
- const char* getSettingName(int i) const override;
- const char* getSettingValue(int i) const override;
- bool setSettingValue(const char* name, const char* value) override;
-};
-
-
-extern PromicroSensorManager sensors;
\ No newline at end of file
diff --git a/variants/rak3x72/platformio.ini b/variants/rak3x72/platformio.ini
new file mode 100644
index 00000000..94e8f595
--- /dev/null
+++ b/variants/rak3x72/platformio.ini
@@ -0,0 +1,33 @@
+[rak3x72]
+extends = stm32_base
+board = rak3172
+board_upload.maximum_size = 229376 ; 32kb for FS
+build_flags = ${stm32_base.build_flags}
+ -D RADIO_CLASS=CustomSTM32WLx
+ -D WRAPPER_CLASS=CustomSTM32WLxWrapper
+ -D SPI_INTERFACES_COUNT=0
+ -D RX_BOOSTED_GAIN=true
+ -I variants/rak3x72
+build_src_filter = ${stm32_base.build_src_filter}
+ +<../variants/rak3x72>
+
+[env:rak3x72-repeater]
+extends = rak3x72
+build_flags = ${rak3x72.build_flags}
+ -D LORA_TX_POWER=22
+ -D ADVERT_NAME='"RAK3x72 Repeater"'
+ -D ADMIN_PASSWORD='"password"'
+build_src_filter = ${rak3x72.build_src_filter}
+ +<../examples/simple_repeater/main.cpp>
+
+[env:rak3x72_companion_radio_usb]
+extends = rak3x72
+build_flags = ${rak3x72.build_flags}
+; -D FORMAT_FS=true
+ -D LORA_TX_POWER=22
+ -D MAX_CONTACTS=100
+ -D MAX_GROUP_CHANNELS=8
+build_src_filter = ${rak3x72.build_src_filter}
+ +<../examples/companion_radio/*.cpp>
+lib_deps = ${rak3x72.lib_deps}
+ densaugeo/base64 @ ~1.4.0
diff --git a/variants/rak3x72/target.cpp b/variants/rak3x72/target.cpp
new file mode 100644
index 00000000..4cb5929a
--- /dev/null
+++ b/variants/rak3x72/target.cpp
@@ -0,0 +1,67 @@
+#include
+#include "target.h"
+#include
+
+RAK3x72Board board;
+
+RADIO_CLASS radio = new STM32WLx_Module();
+
+WRAPPER_CLASS radio_driver(radio, board);
+
+static const uint32_t rfswitch_pins[] = {LORAWAN_RFSWITCH_PINS, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
+static const Module::RfSwitchMode_t rfswitch_table[] = {
+ {STM32WLx::MODE_IDLE, {LOW, LOW}},
+ {STM32WLx::MODE_RX, {HIGH, LOW}},
+ {STM32WLx::MODE_TX_LP, {LOW, HIGH}},
+ {STM32WLx::MODE_TX_HP, {LOW, HIGH}},
+ END_OF_MODE_TABLE,
+};
+
+VolatileRTCClock rtc_clock;
+SensorManager sensors;
+
+#ifndef LORA_CR
+ #define LORA_CR 5
+#endif
+
+bool radio_init() {
+// rtc_clock.begin(Wire);
+
+ radio.setRfSwitchTable(rfswitch_pins, rfswitch_table);
+
+ int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, 0, 0); // TCXO set to 0 for RAK3172
+
+ if (status != RADIOLIB_ERR_NONE) {
+ Serial.print("ERROR: radio init failed: ");
+ Serial.println(status);
+ return false; // fail
+ }
+
+ #ifdef RX_BOOSTED_GAIN
+ radio.setRxBoostedGainMode(RX_BOOSTED_GAIN);
+ #endif
+
+ radio.setCRC(1);
+
+ return true; // success
+}
+
+uint32_t radio_get_rng_seed() {
+ return radio.random(0x7FFFFFFF);
+}
+
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
+ radio.setFrequency(freq);
+ radio.setSpreadingFactor(sf);
+ radio.setBandwidth(bw);
+ radio.setCodingRate(cr);
+}
+
+void radio_set_tx_power(uint8_t dbm) {
+ radio.setOutputPower(dbm);
+}
+
+mesh::LocalIdentity radio_new_identity() {
+ RadioNoiseListener rng(radio);
+ return mesh::LocalIdentity(&rng); // create new random identity
+}
diff --git a/variants/rak3x72/target.h b/variants/rak3x72/target.h
new file mode 100644
index 00000000..c39b69b1
--- /dev/null
+++ b/variants/rak3x72/target.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#define RADIOLIB_STATIC_ONLY 1
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define PIN_VBAT_READ A0
+#define ADC_MULTIPLIER (5 * 1.73 * 1000)
+
+class RAK3x72Board : public STM32Board {
+public:
+ const char* getManufacturerName() const override {
+ return "RAK 3x72";
+ }
+
+ uint16_t getBattMilliVolts() override {
+ uint32_t raw = analogRead(PIN_VBAT_READ);
+ return (ADC_MULTIPLIER * raw) / 1024;
+ }
+};
+
+extern RAK3x72Board board;
+extern WRAPPER_CLASS radio_driver;
+extern VolatileRTCClock rtc_clock;
+extern SensorManager sensors;
+
+bool radio_init();
+uint32_t radio_get_rng_seed();
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
+void radio_set_tx_power(uint8_t dbm);
+mesh::LocalIdentity radio_new_identity();
diff --git a/variants/rak3x72/variant.h b/variants/rak3x72/variant.h
new file mode 100644
index 00000000..4405be0b
--- /dev/null
+++ b/variants/rak3x72/variant.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include
+
+#undef RNG
diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini
index d7584456..c2845fd4 100644
--- a/variants/rak4631/platformio.ini
+++ b/variants/rak4631/platformio.ini
@@ -7,6 +7,7 @@ build_flags = ${nrf52840_base.build_flags}
-I variants/rak4631
-D RAK_4631
-D PIN_USER_BTN=9
+ -D PIN_USER_BTN_ANA=31
-D PIN_BOARD_SCL=14
-D PIN_BOARD_SDA=13
-D PIN_OLED_RESET=-1
diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini
index 4de13e7f..d8f7954f 100644
--- a/variants/wio-e5/platformio.ini
+++ b/variants/wio-e5/platformio.ini
@@ -6,6 +6,7 @@ build_flags = ${stm32_base.build_flags}
-D RADIO_CLASS=CustomSTM32WLx
-D WRAPPER_CLASS=CustomSTM32WLxWrapper
-D SPI_INTERFACES_COUNT=0
+ -D RX_BOOSTED_GAIN=true
-I variants/wio-e5
build_src_filter = ${stm32_base.build_src_filter}
+<../variants/wio-e5>
@@ -13,7 +14,7 @@ build_src_filter = ${stm32_base.build_src_filter}
[env:wio-e5-repeater]
extends = lora_e5
build_flags = ${lora_e5.build_flags}
- -D LORA_TX_POWER=20
+ -D LORA_TX_POWER=22
-D ADVERT_NAME='"WIO-E5 Repeater"'
-D ADMIN_PASSWORD='"password"'
build_src_filter = ${lora_e5.build_src_filter}
@@ -22,7 +23,7 @@ build_src_filter = ${lora_e5.build_src_filter}
[env:wio-e5_companion_radio_usb]
extends = lora_e5
build_flags = ${lora_e5.build_flags}
- -D LORA_TX_POWER=20
+ -D LORA_TX_POWER=22
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
build_src_filter = ${lora_e5.build_src_filter}
diff --git a/variants/wio-e5/target.cpp b/variants/wio-e5/target.cpp
index aee0dafb..8ccbe384 100644
--- a/variants/wio-e5/target.cpp
+++ b/variants/wio-e5/target.cpp
@@ -2,7 +2,7 @@
#include "target.h"
#include
-STM32Board board;
+WIOE5Board board;
RADIO_CLASS radio = new STM32WLx_Module();
@@ -42,7 +42,11 @@ bool radio_init() {
Serial.println(status);
return false; // fail
}
-
+
+ #ifdef RX_BOOSTED_GAIN
+ radio.setRxBoostedGainMode(RX_BOOSTED_GAIN);
+ #endif
+
radio.setCRC(1);
return true; // success
diff --git a/variants/wio-e5/target.h b/variants/wio-e5/target.h
index f2dae108..cc6131cf 100644
--- a/variants/wio-e5/target.h
+++ b/variants/wio-e5/target.h
@@ -8,7 +8,14 @@
#include
#include
-extern STM32Board board;
+class WIOE5Board : public STM32Board {
+public:
+ const char* getManufacturerName() const override {
+ return "Seeed Wio E5";
+ }
+};
+
+extern WIOE5Board board;
extern WRAPPER_CLASS radio_driver;
extern VolatileRTCClock rtc_clock;
extern SensorManager sensors;
diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini
index 5083b1a3..2f468f4f 100644
--- a/variants/xiao_nrf52/platformio.ini
+++ b/variants/xiao_nrf52/platformio.ini
@@ -16,6 +16,11 @@ lib_ignore =
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
+ adafruit/Adafruit INA3221 Library @ ^1.0.1
+ adafruit/Adafruit INA219 @ ^1.2.3
+ adafruit/Adafruit AHTX0 @ ^2.0.5
+ adafruit/Adafruit BME280 Library @ ^2.3.0
+
[Xiao_nrf52]
extends = nrf52840_xiao
@@ -35,8 +40,15 @@ build_flags = ${nrf52840_xiao.build_flags}
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
+ -D PIN_WIRE_SCL=6
+ -D PIN_WIRE_SDA=7
+ -D ENV_INCLUDE_AHTX0=1
+ -D ENV_INCLUDE_BME280=1
+ -D ENV_INCLUDE_INA3221=1
+ -D ENV_INCLUDE_INA219=1
build_src_filter = ${nrf52840_xiao.build_src_filter}
+
+ +
+
+<../variants/xiao_nrf52>
debug_tool = jlink
diff --git a/variants/xiao_nrf52/target.cpp b/variants/xiao_nrf52/target.cpp
index 1d23e76a..c78ea159 100644
--- a/variants/xiao_nrf52/target.cpp
+++ b/variants/xiao_nrf52/target.cpp
@@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock rtc_clock;
-SensorManager sensors;
+EnvironmentSensorManager sensors;
#ifndef LORA_CR
#define LORA_CR 5
diff --git a/variants/xiao_nrf52/target.h b/variants/xiao_nrf52/target.h
index 868664b5..ec298a43 100644
--- a/variants/xiao_nrf52/target.h
+++ b/variants/xiao_nrf52/target.h
@@ -6,12 +6,12 @@
#include
#include
#include
-#include
+#include
extern XiaoNrf52Board board;
extern WRAPPER_CLASS radio_driver;
extern VolatileRTCClock rtc_clock;
-extern SensorManager sensors;
+extern EnvironmentSensorManager sensors;
bool radio_init();
uint32_t radio_get_rng_seed();
diff --git a/variants/xiao_nrf52/variant.h b/variants/xiao_nrf52/variant.h
index 0d0950af..d941463e 100644
--- a/variants/xiao_nrf52/variant.h
+++ b/variants/xiao_nrf52/variant.h
@@ -110,8 +110,8 @@ static const uint8_t A5 = PIN_A5;
// Wire Interfaces
#define WIRE_INTERFACES_COUNT (1)
-#define PIN_WIRE_SDA (17) // 4 and 5 are used for the sx1262 !
-#define PIN_WIRE_SCL (16) // use WIRE1_SDA
+// #define PIN_WIRE_SDA (17) // 4 and 5 are used for the sx1262 !
+// #define PIN_WIRE_SCL (16) // use WIRE1_SDA
static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;