Compare commits

...

23 Commits

Author SHA1 Message Date
Ben Meadors
12680ad9cd Update README.md 2025-06-19 20:35:40 -05:00
github-actions[bot]
0561f2ca4b [create-pull-request] automated change (#7082)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-06-19 18:57:18 -05:00
Andrew Yong
58743021c8 XIAO BLE cleanup (supporting changes to seeed_xiao_nrf52840_kit too) (#7024)
* chore(seeed_xiao_nrf52840_kit): Use build flag for L76K GNSS, rename variant.h ifdef

Signed-off-by: Andrew Yong <me@ndoo.sg>

* feat(seeed_xiao_nrf52840_kit): Support multiple SX126x pinouts via build flags

Signed-off-by: Andrew Yong <me@ndoo.sg>

* feat(seeed_xiao_nrf52840_kit): Pin D0 as user button if pin is unused

Signed-off-by: Andrew Yong <me@ndoo.sg>

* feat: EBYTE E22 and NiceRF gain and SX1262 max power defines

Signed-off-by: Andrew Yong <me@ndoo.sg>

* chore(xiao_ble): Move variant to DIY and extend from seeed_xiao_nrf52840_kit

Signed-off-by: Andrew Yong <me@ndoo.sg>

* feat(seeed_xiao_nrf52840_kit): Pin D6, D7 as I2C SDA, SCL if pins are unused

Signed-off-by: Andrew Yong <me@ndoo.sg>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-06-19 18:51:33 -05:00
Justin E. Mann
2fb46ce5d5 Add rak12035 VB Soil Monitor Tested & Working (#6741)
* [WIP] Add RAK12035VB Soil Moisture Sensor support

Introduce the RAK12035 sensor as an environmental telemetry sensor,
including necessary calibration checks and default values. Update
relevant files to integrate the sensor into the existing telemetry system.

This hardware is not just one module, but a couple.. RAK12023 and
RAK12035 is the component stack, the RAK12023 does not seem to matter
much and allows for multiple RAK12035 devices to be used.

Co-Authored-By: @Justin-Mann

* [WIP] Add RAK12035VB Soil Moisture Sensor support

Introduce the RAK12035 sensor as an environmental telemetry sensor,
including necessary calibration checks and default values. Update
relevant files to integrate the sensor into the existing telemetry system.

This hardware is not just one module, but a couple.. RAK12023 and
RAK12035 is the component stack, the RAK12023 does not seem to matter
much and allows for multiple RAK12035 devices to be used.

Co-Authored-By: @Justin-Mann

* [WIP] Add RAK12035VB Soil Moisture Sensor support

Introduce the RAK12035 sensor as an environmental telemetry sensor,
including necessary calibration checks and default values. Update
relevant files to integrate the sensor into the existing telemetry system.

This hardware is not just one module, but a couple.. RAK12023 and
RAK12035 is the component stack, the RAK12023 does not seem to matter
much and allows for multiple RAK12035 devices to be used.

Co-Authored-By: @Justin-Mann

* [WIP] Add RAK12035VB Soil Moisture Sensor support

Introduce the RAK12035 sensor as an environmental telemetry sensor,
including necessary calibration checks and default values. Update
relevant files to integrate the sensor into the existing telemetry system.

This hardware is not just one module, but a couple.. RAK12023 and
RAK12035 is the component stack, the RAK12023 does not seem to matter
much and allows for multiple RAK12035 devices to be used.

Co-Authored-By: @Justin-Mann

* Update to 1.0.4 release of RAK12035_SoilMoisture

* cleanup

* cool

* .

* ..

* little bit of cleanup and recompile/upload/test on RAK WISBLAOCK STACK: RAK19007/RAK4631/RAK12035VB/RAK12500

looks like soil monitor is working correctly, new environmental metrics are comming thru [new protos soil_moisture, soil_temperature] and GPS is working again with the RAK 12500.

improvements could be made around the configuration of the monitor.

next steps include updating the client(s) to react to, log and display the new proto metrics for soil temp and humidity.

* . comments about current limitations and TODOs

* trunk update

* trying to autoformat..

* fix formatting attempt 2

* ..

* ...

* ...

* .

* some corrections and local build success

* correction in temp code

* grr formatting

* cleanup after a few experiments

* remove temp code to overwrite values for temp and humidity protos.. next step just update the clients to know about soil_temperature and soil_humidity protos.

* update some values in varient for rak wistap

* working out trunk formatting..

* wip
. corrections to other build variants

* .

* protobuffs?

* protobufs?

* Update protobufs ref

* Protobufs ref

* Trunk

* Update RAK12035Sensor.cpp

* Fmt

* comment changes

* dumb mistakes... resolved, actually built and tested.. all good..

* Update src/modules/Telemetry/Sensor/RAK12035Sensor.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/modules/Telemetry/Sensor/RAK12035Sensor.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* . proto submod

* proto

* proto

* merge master

* mabe a fix for GPS pin conflict, waiting on a new gps module to try

* merge master, attempt to fix gps (RAK12500) pin conflict with RAK12023/12035

* .

* .

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-19 18:51:03 -05:00
Marek
8be76a56c7 PacketHistory - option to track entries' aging to log (#7067)
* PacketHistory debloat RAM allocations

* Removed FLOOD_EXPIRE_TIME option. We have static buffer now.

* Remove mx_ prefix from recentPackets

* Remember no less than 100 packet not to make reflood hell

* Cleanup

* PacketHistory max no less than 100

* no less than 100 means max of 100 or a given value of course.

* Care to not do duplicate entries. Cleanups.

* Packet History - option to log aging of entries

* Update comments for PACKET_HISTORY_TRACE_AGING and VERBOSE_PACKET_HISTORY definitions

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-06-19 18:48:35 -05:00
Hannes Fuchs
2c206febab Fix nugget s3 lora variant issues (#7070)
* Fix serial communication for nugget s3 lora

Without setting `ARDUINO_USB_CDC_ON_BOOT=1` the serial interface on the
nugget s3 lora board does not work.

* Fix nugget s3 lora variant definitions
2025-06-19 18:48:22 -05:00
github-actions[bot]
db1eac12af Upgrade trunk (#7073)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-06-20 09:22:03 +10:00
Jeremiah K
56e67cb434 Fix position exchange throttling issue (#7079)
* Fix position exchange throttling race condition

Separate tracking of position broadcasts vs replies to fix exchange position functionality.

Previously, allocReply() would refuse to send position replies if any position packet
(broadcast or reply) was sent within the last 3 minutes. This caused the exchange
position feature to fail when a device had recently sent a position broadcast.

Changes:
- Add lastSentReply member to track position reply timestamps separately
- Update allocReply() to only throttle based on previous replies, not broadcasts
- This allows position exchange to work even after recent position broadcasts

The fix maintains the 3-minute throttling for replies to prevent spam while allowing
legitimate position exchange functionality to work properly.

* Remove unused lastSentToMesh variable

Variable was no longer used after separating reply throttling logic.
2025-06-19 18:20:20 -05:00
Matt Smith
e9d5e36738 Replace blocking delay for wifi reconnect with non-blocking to keep button/display interactivity (#6983)
* Update WiFiAPClient.cpp to replace blocking delay() with non-blocking

* Update WiFiAPClient.cpp - fix extra endif

* Update WiFiAPClient.cpp remove duplicate section

* Update WiFiAPClient.cpp

* Update trunk_annotate_pr.yml

* Update trunk_annotate_pr.yml

* Update trunk_check.yml

* Update trunk_check.yml

* Update trunk_format_pr.yml

* Update trunk_annotate_pr.yml

* Attempted to address comments, and fix my other mess.
Thanks for your patience.

* Revert "Update trunk_annotate_pr.yml"

This reverts commit 7db4ff6444.

* Last mess cleanups (hopefully)

* Undid trunk.yaml changes

* Trunk format

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-06-19 18:18:55 -05:00
Jonathan Bennett
f71fdef3fd Update HostMetrics.cpp - don't try to print the user string (#7081)
* Update HostMetrics.cpp - don't try to print the user string

* Make Trunk Happy
2025-06-19 17:05:22 -05:00
Jonathan Bennett
5e92145324 Ensure incoming hostMetrics userstring is null terminated (#7068)
* Ensure incoming hostMetrics  userstring is null terminated

* Only null terminate user_string when has_user_string is true
2025-06-18 16:41:43 -05:00
github-actions[bot]
89a4589b68 Upgrade trunk (#7060)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-06-18 06:20:10 -05:00
Marek Veselý
20991d8b53 Add recognition for SHT40 with serial number starting with 0xc8d (#7061)
* Add recognition for SHT40 with serial number starting with 0xc8d

* fix a dumb typo :/
2025-06-18 06:19:52 -05:00
Jonathan Bennett
3ab9005b2f Make sure host_metrics user_string is null terminated 2025-06-17 11:11:36 -05:00
Marek
aabc5b7cf2 PacketHistory debloat RAM allocations (#7034)
* PacketHistory debloat RAM allocations

* Removed FLOOD_EXPIRE_TIME option. We have static buffer now.

* Remove mx_ prefix from recentPackets

* Remember no less than 100 packet not to make reflood hell

* Cleanup

* PacketHistory max no less than 100

* no less than 100 means max of 100 or a given value of course.

* Care to not do duplicate entries. Cleanups.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-06-17 07:18:59 -05:00
dylanliacc
afcd97c154 trunk fmt 2025-06-16 16:16:14 -05:00
Dylanliacc
cbdd7eae70 fix IIC port 2025-06-16 16:15:59 -05:00
Austin
6374ffea35 Run daily packaging earlier (PPA) (#7057) 2025-06-16 07:52:20 -05:00
Nivek-domo
1a6bb97f16 Fix RCWL9620Sensor for rak11310 support (#6617)
* Update RCWL9620Sensor.cpp

test on rak11310, work very wel now

* Update RCWL9620Sensor.cpp

* Trunk

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-06-16 06:54:55 -05:00
github-actions[bot]
4f0b95e910 Upgrade trunk (#7053)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-06-16 06:24:26 -05:00
github-actions[bot]
a81b41cbfb automated bumps (#7050)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-06-16 06:11:10 -05:00
todd-herbert
465fe18a89 Dismiss ExternalNotification nagging on InkHUD button press (#7056)
* Expose ExternalNotification::isNagging

* Dismiss external notification on button press
2025-06-16 06:09:55 -05:00
Taha
bd0e25f3f5 Fix Critical Error #3 for LilyGo T-Echo (#6791)
* Fix Critical Error #3

* clang format
2025-06-16 13:32:28 +10:00
51 changed files with 837 additions and 523 deletions

View File

@@ -1,7 +1,7 @@
name: Daily Packaging
on:
schedule:
- cron: 0 9 * * *
- cron: 0 2 * * *
workflow_dispatch:
push:
branches:

View File

@@ -8,15 +8,15 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.440
- renovate@40.51.0
- checkov@3.2.442
- renovate@40.60.3
- prettier@3.5.3
- trufflehog@3.89.1
- trufflehog@3.89.2
- yamllint@1.37.1
- bandit@1.8.3
- bandit@1.8.5
- trivy@0.63.0
- taplo@0.9.3
- ruff@0.11.13
- ruff@0.12.0
- isort@6.0.1
- markdownlint@0.45.0
- oxipng@9.1.5

View File

@@ -37,3 +37,4 @@ Join our community and help improve Meshtastic! 🚀
## Stats
![Alt](https://repobeats.axiom.co/api/embed/8025e56c482ec63541593cc5bd322c19d5c0bdcf.svg "Repobeats analytics image")

View File

@@ -87,6 +87,9 @@
</screenshots>
<releases>
<release version="2.6.13" date="2025-06-16">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.6.13</url>
</release>
<release version="2.6.12" date="2025-06-15">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.6.12</url>
</release>

7
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
meshtasticd (2.6.11.0) UNRELEASED; urgency=medium
meshtasticd (2.6.13.0) UNRELEASED; urgency=medium
[ Austin Lane ]
* Initial packaging
@@ -19,4 +19,7 @@ meshtasticd (2.6.11.0) UNRELEASED; urgency=medium
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Mon, 02 Jun 2025 20:00:55 +0000
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Mon, 16 Jun 2025 02:10:49 +0000

View File

@@ -81,7 +81,43 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// #define REGULATORY_LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
// Total system gain in dBm to subtract from Tx power to remain within regulatory and Tx PA limits
// This value should be set in variant.h and is PA gain + antenna gain (if variant has a non-removable antenna)
// The value consists of PA gain + antenna gain (if variant has a non-removable antenna)
// TX_GAIN_LORA should be set with definitions below for common modules, or in variant.h.
// Gain for common modules with transmit PAs
#ifdef EBYTE_E22_900M30S
// 10dB PA gain and 30dB rated output; based on measurements from
// https://github.com/S5NC/EBYTE_ESP32-S3/blob/main/E22-900M30S%20power%20output%20testing.txt
#define TX_GAIN_LORA 7
#define SX126X_MAX_POWER 22
#endif
#ifdef EBYTE_E22_900M33S
// 25dB PA gain and 33dB rated output; based on TX Power Curve from E22-900M33S_UserManual_EN_v1.0.pdf
#define TX_GAIN_LORA 25
#define SX126X_MAX_POWER 8
#endif
#ifdef NICERF_MINIF27
// Note that datasheet power level of 9 corresponds with SX1262 at 22dBm
// Maximum output power of 29dBm with VCC_PA = 5V
#define TX_GAIN_LORA 7
#define SX126X_MAX_POWER 22
#endif
#ifdef NICERF_F30_HF
// Maximum output power of 29.6dBm with VCC = 5V and SX1262 at 22dBm
#define TX_GAIN_LORA 8
#define SX126X_MAX_POWER 22
#endif
#ifdef NICERF_F30_LF
// Maximum output power of 32.0dBm with VCC = 5V and SX1262 at 22dBm
#define TX_GAIN_LORA 10
#define SX126X_MAX_POWER 22
#endif
// Default system gain to 0 if not defined
#ifndef TX_GAIN_LORA
#define TX_GAIN_LORA 0
#endif
@@ -193,6 +229,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
// - the default i2c address for this sensor is 0x20, and users are instructed to
// set 0x21 and 0x22 for the second and third sensor if present.
// -----------------------------------------------------------------------------
#define RAK120351_ADDR 0x20
#define RAK120352_ADDR 0x21
#define RAK120353_ADDR 0x22
// -----------------------------------------------------------------------------
// BIAS-T Generator
// -----------------------------------------------------------------------------

View File

@@ -70,6 +70,7 @@ class ScanI2C
DFROBOT_RAIN,
DPS310,
LTR390UV,
RAK12035,
TCA8418KB,
PCT2075,
} DeviceType;

View File

@@ -358,7 +358,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) {
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
@@ -423,9 +423,21 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
logFoundDevice("BMA423", (uint8_t)addr.address);
}
break;
case TCA9535_ADDR:
case RAK120352_ADDR:
case RAK120353_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x02), 1);
if (registerValue == addr.address) { // RAK12035 returns its I2C address at 0x02 (eg 0x20)
type = RAK12035;
logFoundDevice("RAK12035", (uint8_t)addr.address);
} else {
type = TCA9535;
logFoundDevice("TCA9535", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address);

View File

@@ -4,6 +4,7 @@
#include "RTC.h"
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
@@ -37,6 +38,10 @@ void InkHUD::Events::begin()
void InkHUD::Events::onButtonShort()
{
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
@@ -49,7 +54,7 @@ void InkHUD::Events::onButtonShort()
// If no system applet is handling input, default behavior instead is to cycle applets
if (consumer)
consumer->onButtonShortPress();
else
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
inkhud->nextApplet();
}
@@ -204,4 +209,24 @@ int InkHUD::Events::beforeLightSleep(void *unused)
}
#endif
// Silence all ongoing beeping, blinking, buzzing, coming from the external notification module
// Returns true if an external notification was active, and we dismissed it
// Button handling changes depending on our result
bool InkHUD::Events::dismissExternalNotification()
{
// Abort if not using external notifications
if (!moduleConfig.external_notification.enabled)
return false;
// Abort if nothing to dismiss
if (!externalNotificationModule->nagging())
return false;
// Stop the beep buzz blink
externalNotificationModule->stopNow();
// Inform that we did indeed dismiss an external notification
return true;
}
#endif

View File

@@ -62,6 +62,9 @@ class Events
CallbackObserver<Events, void *> lightSleepObserver = CallbackObserver<Events, void *>(this, &Events::beforeLightSleep);
#endif
// End any externalNotification beeping, buzzing, blinking etc
bool dismissExternalNotification();
// If set, InkHUD's data will be erased during onReboot
bool eraseOnReboot = false;
};

View File

@@ -729,6 +729,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LTR390UV, meshtastic_TelemetrySensorType_LTR390UV);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DPS310, meshtastic_TelemetrySensorType_DPS310);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
i2cScanner.reset();

View File

@@ -7,152 +7,377 @@
#endif
#include "Throttle.h"
PacketHistory::PacketHistory()
#define PACKETHISTORY_MAX \
max((int)(MAX_NUM_NODES * 2.0), 100) // x2..3 Should suffice. Empirical setup. 16B per record malloc'ed, but no less than 100
#define RECENT_WARN_AGE (10 * 60 * 1000L) // Warn if the packet that gets removed was more recent than 10 min
#define VERBOSE_PACKET_HISTORY 0 // Set to 1 for verbose logging, 2 for heavy debugging
#define PACKET_HISTORY_TRACE_AGING 1 // Set to 1 to enable logging of the age of re/used history slots
PacketHistory::PacketHistory(uint32_t size) : recentPacketsCapacity(0), recentPackets(NULL) // Initialize members
{
recentPackets.reserve(MAX_NUM_NODES); // Prealloc the worst case # of records - to prevent heap fragmentation
// setup our periodic task
if (size < 4 || size > PACKETHISTORY_MAX) { // Copilot suggested - makes sense
LOG_WARN("Packet History - Invalid size %d, using default %d", size, PACKETHISTORY_MAX);
size = PACKETHISTORY_MAX; // Use default size if invalid
}
// Allocate memory for the recent packets array
recentPacketsCapacity = size;
recentPackets = new PacketRecord[recentPacketsCapacity];
if (!recentPackets) { // No logging here, console/log probably uninitialized yet.
LOG_ERROR("Packet History - Memory allocation failed for size=%d entries / %d Bytes", size,
sizeof(PacketRecord) * recentPacketsCapacity);
recentPacketsCapacity = 0; // mark allocation fail
return; // return early
}
// Initialize the recent packets array to zero
memset(recentPackets, 0, sizeof(PacketRecord) * recentPacketsCapacity);
}
/**
* Update recentBroadcasts and return true if we have already seen this packet
*/
PacketHistory::~PacketHistory()
{
recentPacketsCapacity = 0;
delete[] recentPackets;
recentPackets = NULL;
}
/** Update recentPackets and return true if we have already seen this packet */
bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpdate, bool *wasFallback, bool *weWereNextHop)
{
if (!initOk()) {
LOG_ERROR("Packet History - Was Seen Recently: NOT INITIALIZED!");
return false;
}
if (p->id == 0) {
LOG_DEBUG("Ignore message with zero id");
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: ID is 0, not a floodable message");
#endif
return false; // Not a floodable message ID, so we don't care
}
PacketRecord r;
memset(&r, 0, sizeof(PacketRecord)); // Initialize the record to zero
// Save basic info from checked packet
r.id = p->id;
r.sender = getFrom(p);
r.rxTimeMsec = millis();
r.sender = getFrom(p); // If 0 then use our ID
r.next_hop = p->next_hop;
r.relayed_by[0] = p->relay_node;
// LOG_INFO("Add relayed_by 0x%x for id=0x%x", p->relay_node, r.id);
auto found = recentPackets.find(r);
bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently
r.rxTimeMsec = millis(); //
if (r.rxTimeMsec == 0) // =0 every 49.7 days? 0 is special
r.rxTimeMsec = 1;
if (seenRecently &&
!Throttle::isWithinTimespanMs(found->rxTimeMsec, FLOOD_EXPIRE_TIME)) { // Check whether found packet has already expired
recentPackets.erase(found); // Erase and pretend packet has not been seen recently
found = recentPackets.end();
seenRecently = false;
}
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: @start s=%08x id=%08x / to=%08x nh=%02x rn=%02x / wUpd=%s / wasFb?%d wWNH?%d",
r.sender, r.id, p->to, p->next_hop, p->relay_node, withUpdate ? "YES" : "NO", wasFallback ? *wasFallback : -1,
weWereNextHop ? *weWereNextHop : -1);
#endif
PacketRecord *found = find(r.sender, r.id); // Find the packet record in the recentPackets array
bool seenRecently = (found != NULL); // If found -> the packet was seen recently
if (seenRecently) {
LOG_DEBUG("Found existing packet record for fr=0x%x,to=0x%x,id=0x%x", p->from, p->to, p->id);
uint8_t ourRelayID = nodeDB->getLastByteOfNodeNum(nodeDB->getNodeNum());
uint8_t ourRelayID = nodeDB->getLastByteOfNodeNum(nodeDB->getNodeNum()); // Get our relay ID from our node number
if (wasFallback) {
// If it was seen with a next-hop not set to us and now it's NO_NEXT_HOP_PREFERENCE, and the relayer relayed already
// before, it's a fallback to flooding. If we didn't already relay and the next-hop neither, we might need to handle
// it now.
if (found->sender != nodeDB->getNodeNum() && found->next_hop != NO_NEXT_HOP_PREFERENCE &&
found->next_hop != ourRelayID && p->next_hop == NO_NEXT_HOP_PREFERENCE && wasRelayer(p->relay_node, found) &&
!wasRelayer(ourRelayID, found) && !wasRelayer(found->next_hop, found)) {
found->next_hop != ourRelayID && p->next_hop == NO_NEXT_HOP_PREFERENCE && wasRelayer(p->relay_node, *found) &&
!wasRelayer(ourRelayID, *found) &&
!wasRelayer(
found->next_hop,
*found)) { // If we were not the next hop and the next hop is not us, and we are not relaying this packet
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: f=%08x id=%08x nh=%02x rn=%02x oID=%02x, wasFbk=%d-set TRUE",
p->from, p->id, p->next_hop, p->relay_node, ourRelayID, wasFallback ? *wasFallback : -1);
#endif
*wasFallback = true;
} else {
// debug log only
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: f=%08x id=%08x nh=%02x rn=%02x oID=%02x, wasFbk=%d-no change",
p->from, p->id, p->next_hop, p->relay_node, ourRelayID, wasFallback ? *wasFallback : -1);
#endif
}
}
// Check if we were the next hop for this packet
if (weWereNextHop) {
*weWereNextHop = found->next_hop == ourRelayID;
*weWereNextHop = (found->next_hop == ourRelayID);
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: f=%08x id=%08x nh=%02x rn=%02x foundnh=%02x oID=%02x -> wWNH=%s",
p->from, p->id, p->next_hop, p->relay_node, found->next_hop, ourRelayID, (*weWereNextHop) ? "YES" : "NO");
#endif
}
}
if (withUpdate) {
if (found != recentPackets.end()) { // delete existing to updated timestamp and relayed_by (re-insert)
if (found != NULL) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: s=%08x id=%08x nh=%02x rby=%02x %02x %02x age=%d wUpd BEFORE",
found->sender, found->id, found->next_hop, found->relayed_by[0], found->relayed_by[1], found->relayed_by[2],
millis() - found->rxTimeMsec);
#endif
// Add the existing relayed_by to the new record
for (uint8_t i = 0; i < NUM_RELAYERS - 1; i++) {
if (found->relayed_by[i])
for (uint8_t i = 0; i < (NUM_RELAYERS - 1); i++) {
if (found->relayed_by[i] != 0)
r.relayed_by[i + 1] = found->relayed_by[i];
}
r.next_hop = found->next_hop; // keep the original next_hop (such that we check whether we were originally asked)
recentPackets.erase(found); // as unsorted_set::iterator is const (can't update - so re-insert..)
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: s=%08x id=%08x nh=%02x rby=%02x %02x %02x age=%d wUpd AFTER", r.sender,
r.id, r.next_hop, r.relayed_by[0], r.relayed_by[1], r.relayed_by[2], millis() - r.rxTimeMsec);
#endif
// TODO: have direct *found entry - can modify directly without local copy _vs_ not convolute the code by this
}
recentPackets.insert(r);
LOG_DEBUG("Add packet record fr=0x%x, id=0x%x", p->from, p->id);
}
// Capacity is reerved, so only purge expired packets if recentPackets fills past 90% capacity
// Expiry is normally dealt with after having searched/found a packet (above)
if (recentPackets.size() > (MAX_NUM_NODES * 0.9)) {
clearExpiredRecentPackets();
insert(r); // Insert or update the packet record in the history
}
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - Was Seen Recently: @exit s=%08x id=%08x (to=%08x) relby=%02x %02x %02x nxthop=%02x rxT=%d "
"found?%s seenRecently?%s wUpd?%s",
r.sender, r.id, p->to, r.relayed_by[0], r.relayed_by[1], r.relayed_by[2], r.next_hop, r.rxTimeMsec,
found ? "YES" : "NO ", seenRecently ? "YES" : "NO ", withUpdate ? "YES" : "NO ");
#endif
return seenRecently;
}
/**
* Iterate through all recent packets, and remove all older than FLOOD_EXPIRE_TIME
*/
void PacketHistory::clearExpiredRecentPackets()
/** Find a packet record in history.
* @return pointer to PacketRecord if found, NULL if not found */
PacketHistory::PacketRecord *PacketHistory::find(NodeNum sender, PacketId id)
{
LOG_DEBUG("recentPackets size=%ld", recentPackets.size());
if (sender == 0 || id == 0) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - find: s=%08x id=%08x sender/id=0->NOT FOUND", sender, id);
#endif
return NULL;
}
for (auto it = recentPackets.begin(); it != recentPackets.end();) {
if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) {
it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased
} else {
++it;
PacketRecord *it = NULL;
for (it = recentPackets; it < (recentPackets + recentPacketsCapacity); ++it) {
if (it->id == id && it->sender == sender) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - find: s=%08x id=%08x FOUND nh=%02x rby=%02x %02x %02x age=%d slot=%d/%d", it->sender,
it->id, it->next_hop, it->relayed_by[0], it->relayed_by[1], it->relayed_by[2], millis() - (it->rxTimeMsec),
it - recentPackets, recentPacketsCapacity);
#endif
// only the first match is returned, so be careful not to create duplicate entries
return it; // Return pointer to the found record
}
}
LOG_DEBUG("recentPackets size=%ld (after clearing expired packets)", recentPackets.size());
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - find: s=%08x id=%08x NOT FOUND", sender, id);
#endif
return NULL; // Not found
}
/** Insert/Replace oldest PacketRecord in recentPackets. */
void PacketHistory::insert(PacketRecord &r)
{
uint32_t now_millis = millis(); // Should not jump with time changes
uint32_t OldtrxTimeMsec = 0;
PacketRecord *tu = NULL; // Will insert here.
PacketRecord *it = NULL;
// Find a free, matching or oldest used slot in the recentPackets array
for (it = recentPackets; it < (recentPackets + recentPacketsCapacity); ++it) {
if (it->id == 0 && it->sender == 0 /*&& rxTimeMsec == 0*/) { // Record is empty
tu = it; // Remember the free slot
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Free slot@ %d/%d", tu - recentPackets, recentPacketsCapacity);
#endif
// We have that, Exit the loop
it = (recentPackets + recentPacketsCapacity);
} else if (it->id == r.id && it->sender == r.sender) { // Record matches the packet we want to insert
tu = it; // Remember the matching slot
OldtrxTimeMsec = now_millis - it->rxTimeMsec; // ..and save current entry's age
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Matched slot@ %d/%d age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
#endif
// We have that, Exit the loop
it = (recentPackets + recentPacketsCapacity);
} else {
if (it->rxTimeMsec == 0) {
LOG_WARN(
"Packet History - insert: Found packet s=%08x id=%08x with rxTimeMsec = 0, slot %d/%d. Should never happen!",
it->sender, it->id, it - recentPackets, recentPacketsCapacity);
}
if ((now_millis - it->rxTimeMsec) > OldtrxTimeMsec) { // 49.7 days rollover friendly
OldtrxTimeMsec = now_millis - it->rxTimeMsec;
tu = it; // remember the oldest packet
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Older slot@ %d/%d age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
#endif
}
// keep looking for oldest till entire array is checked
}
}
if (tu == NULL) {
LOG_ERROR("Packet History - insert: No free slot, no matched packet, no oldest to reuse. Something leaked."); // mx
// assert(false); // This should never happen, we should always have at least one packet to clear
return; // Return early if we can't update the history
}
#if VERBOSE_PACKET_HISTORY
if (tu->id == 0 && tu->sender == 0) {
LOG_DEBUG("Packet History - insert: slot@ %d/%d is NEW", tu - recentPackets, recentPacketsCapacity);
} else if (tu->id == r.id && tu->sender == r.sender) {
LOG_DEBUG("Packet History - insert: slot@ %d/%d MATCHED, age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
} else {
LOG_DEBUG("Packet History - insert: slot@ %d/%d REUSE OLDEST, age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
}
#endif
// If we are reusing a slot, we should warn if the packet is too recent
#if RECENT_WARN_AGE > 0
if (tu->rxTimeMsec && (OldtrxTimeMsec < RECENT_WARN_AGE)) {
if (!(tu->id == r.id && tu->sender == r.sender)) {
LOG_WARN("Packet History - insert: Reusing slot aged %ds < %ds RECENT_WARN_AGE", OldtrxTimeMsec / 1000,
RECENT_WARN_AGE / 1000);
} else {
// debug only
#if VERBOSE_PACKET_HISTORY
LOG_WARN("Packet History - insert: Reusing slot aged %.3fs < %ds with MATCHED PACKET - this is normal",
OldtrxTimeMsec / 1000., RECENT_WARN_AGE / 1000);
#endif
}
}
#if PACKET_HISTORY_TRACE_AGING
if (tu->rxTimeMsec != 0) {
LOG_INFO("Packet History - insert: Reusing slot aged %.3fs TRACE %s", OldtrxTimeMsec / 1000.,
(tu->id == r.id && tu->sender == r.sender) ? "MATCHED PACKET" : "OLDEST SLOT");
} else {
LOG_INFO("Packet History - insert: Using new slot @uptime %.3fs TRACE NEW", millis() / 1000.);
}
#endif
#endif
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d BEFORE",
tu - recentPackets, recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1],
tu->relayed_by[2], tu->rxTimeMsec);
#endif
if (r.rxTimeMsec == 0) {
LOG_WARN("Packet History - insert: I will not store packet with rxTimeMsec = 0.");
return; // Return early if we can't update the history
}
*tu = r; // store the packet
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d AFTER",
tu - recentPackets, recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1],
tu->relayed_by[2], tu->rxTimeMsec);
#endif
}
/* Check if a certain node was a relayer of a packet in the history given an ID and sender
* @return true if node was indeed a relayer, false if not */
bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender)
{
if (relayer == 0)
return false;
PacketRecord r = {.sender = sender, .id = id, .rxTimeMsec = 0, .next_hop = 0};
auto found = recentPackets.find(r);
if (found == recentPackets.end()) {
if (!initOk()) {
LOG_ERROR("PacketHistory - wasRelayer: NOT INITIALIZED!");
return false;
}
return wasRelayer(relayer, found);
if (relayer == 0) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was relayer: s=%08x id=%08x / rl=%02x=zero. NO", sender, id, relayer);
#endif
return false;
}
PacketRecord *found = find(sender, id);
if (found == NULL) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was relayer: s=%08x id=%08x / rl=%02x / PR not found. NO", sender, id, relayer);
#endif
return false;
}
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - was relayer: s=%08x id=%08x nh=%02x age=%d rls=%02x %02x %02x InHistory,check:%02x",
found->sender, found->id, found->next_hop, millis() - found->rxTimeMsec, found->relayed_by[0], found->relayed_by[1],
found->relayed_by[2], relayer);
#endif
return wasRelayer(relayer, *found);
}
/* Check if a certain node was a relayer of a packet in the history given iterator
* @return true if node was indeed a relayer, false if not */
bool PacketHistory::wasRelayer(const uint8_t relayer, std::unordered_set<PacketRecord, PacketRecordHashFunction>::iterator r)
bool PacketHistory::wasRelayer(const uint8_t relayer, PacketRecord &r)
{
for (uint8_t i = 0; i < NUM_RELAYERS; i++) {
if (r->relayed_by[i] == relayer) {
if (r.relayed_by[i] == relayer) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? YES", r.sender, r.id,
r.relayed_by[0], r.relayed_by[1], r.relayed_by[2], relayer);
#endif
return true;
}
}
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? NO", r.sender, r.id, r.relayed_by[0],
r.relayed_by[1], r.relayed_by[2], relayer);
#endif
return false;
}
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender
void PacketHistory::removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender)
{
PacketRecord r = {.sender = sender, .id = id, .rxTimeMsec = 0, .next_hop = 0};
auto found = recentPackets.find(r);
if (found == recentPackets.end()) {
if (!initOk()) {
LOG_ERROR("Packet History - remove Relayer: NOT INITIALIZED!");
return;
}
// Make a copy of the found record
r.next_hop = found->next_hop;
r.rxTimeMsec = found->rxTimeMsec;
// Only add the relayers that are not the one we want to remove
uint8_t j = 0;
for (uint8_t i = 0; i < NUM_RELAYERS; i++) {
if (found->relayed_by[i] != relayer) {
r.relayed_by[j] = found->relayed_by[i];
j++;
}
PacketRecord *found = find(sender, id);
if (found == NULL) {
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - remove Relayer s=%08x id=%08x (rl=%02x) NOT FOUND", sender, id, relayer);
#endif
return; // Nothing to remove
}
recentPackets.erase(found);
recentPackets.insert(r);
}
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - remove Relayer s=%08x id=%08x rby=%02x %02x %02x, rl:%02x BEFORE", found->sender, found->id,
found->relayed_by[0], found->relayed_by[1], found->relayed_by[2], relayer);
#endif
// nexthop and rxTimeMsec too stay in found entry
uint8_t j = 0;
uint8_t i = 0;
for (; i < NUM_RELAYERS; i++) {
if (found->relayed_by[i] != relayer) {
found->relayed_by[j] = found->relayed_by[i];
j++;
} else
found->relayed_by[i] = 0;
}
for (; j < NUM_RELAYERS; j++) { // Clear the rest of the relayed_by array
found->relayed_by[j] = 0;
}
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - remove Relayer s=%08x id=%08x rby=%02x %02x %02x rl:%02x AFTER - removed?%d", found->sender,
found->id, found->relayed_by[0], found->relayed_by[1], found->relayed_by[2], relayer, i != j);
#endif
}

View File

@@ -1,49 +1,47 @@
#pragma once
#include "NodeDB.h"
#include <unordered_set>
/// We clear our old flood record 10 minutes after we see the last of it
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#define FLOOD_EXPIRE_TIME (5 * 1000L) // Don't allow too many packets to accumulate when fuzzing.
#else
#define FLOOD_EXPIRE_TIME (10 * 60 * 1000L)
#endif
#define NUM_RELAYERS \
3 // Number of relayer we keep track of. Use 3 to be efficient with memory alignment of PacketRecord to 16 bytes
/**
* A record of a recent message broadcast
*/
struct PacketRecord {
NodeNum sender;
PacketId id;
uint32_t rxTimeMsec; // Unix time in msecs - the time we received it
uint8_t next_hop; // The next hop asked for this packet
uint8_t relayed_by[NUM_RELAYERS]; // Array of nodes that relayed this packet
bool operator==(const PacketRecord &p) const { return sender == p.sender && id == p.id; }
};
class PacketRecordHashFunction
{
public:
size_t operator()(const PacketRecord &p) const { return (std::hash<NodeNum>()(p.sender)) ^ (std::hash<PacketId>()(p.id)); }
};
/**
* This is a mixin that adds a record of past packets we have seen
*/
class PacketHistory
{
private:
std::unordered_set<PacketRecord, PacketRecordHashFunction> recentPackets;
struct PacketRecord { // A record of a recent message broadcast, no need to be visible outside this class.
NodeNum sender;
PacketId id;
uint32_t rxTimeMsec; // Unix time in msecs - the time we received it, 0 means empty
uint8_t next_hop; // The next hop asked for this packet
uint8_t relayed_by[NUM_RELAYERS]; // Array of nodes that relayed this packet
}; // 4B + 4B + 4B + 1B + 3B = 16B
void clearExpiredRecentPackets(); // clear all recentPackets older than FLOOD_EXPIRE_TIME
uint32_t recentPacketsCapacity =
0; // Can be set in constructor, no need to recompile. Used to allocate memory for mx_recentPackets.
PacketRecord *recentPackets = NULL; // Simple and fixed in size. Debloat.
/** Find a packet record in history.
* @param sender NodeNum
* @param id PacketId
* @return pointer to PacketRecord if found, NULL if not found */
PacketRecord *find(NodeNum sender, PacketId id);
/** Insert/Replace oldest PacketRecord in mx_recentPackets.
* @param r PacketRecord to insert or replace */
void insert(PacketRecord &r); // Insert or replace a packet record in the history
/* Check if a certain node was a relayer of a packet in the history given iterator
* @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, PacketRecord &r);
PacketHistory(const PacketHistory &); // non construction-copyable
PacketHistory &operator=(const PacketHistory &); // non copyable
public:
PacketHistory();
explicit PacketHistory(uint32_t size = -1); // Constructor with size parameter, default is PACKETHISTORY_MAX
~PacketHistory();
/**
* Update recentBroadcasts and return true if we have already seen this packet
@@ -59,10 +57,9 @@ class PacketHistory
* @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
/* Check if a certain node was a relayer of a packet in the history given iterator
* @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, std::unordered_set<PacketRecord, PacketRecordHashFunction>::iterator r);
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender
void removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
};
// To check if the PacketHistory was initialized correctly by constructor
bool initOk(void) { return recentPackets != NULL && recentPacketsCapacity != 0; }
};

View File

@@ -283,7 +283,9 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
/* Philippines 868mhz */
meshtastic_Config_LoRaConfig_RegionCode_PH_868 = 20,
/* Philippines 915mhz */
meshtastic_Config_LoRaConfig_RegionCode_PH_915 = 21
meshtastic_Config_LoRaConfig_RegionCode_PH_915 = 21,
/* Australia / New Zealand 433MHz */
meshtastic_Config_LoRaConfig_RegionCode_ANZ_433 = 22
} meshtastic_Config_LoRaConfig_RegionCode;
/* Standard predefined channel settings
@@ -679,8 +681,8 @@ extern "C" {
#define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1))
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_PH_915
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_PH_915+1))
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_ANZ_433
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_ANZ_433+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO

View File

@@ -46,6 +46,10 @@ uint8_t wifiDisconnectReason = 0;
// Stores our hostname
char ourHost[16];
// To replace blocking wifi connect delay with a non-blocking sleep
static unsigned long wifiReconnectStartMillis = 0;
static bool wifiReconnectPending = false;
bool APStartupComplete = 0;
unsigned long lastrun_ntp = 0;
@@ -160,17 +164,30 @@ static int32_t reconnectWiFi()
#endif
LOG_INFO("Reconnecting to WiFi access point %s", wifiName);
delay(5000);
// Start the non-blocking wait for 5 seconds
wifiReconnectStartMillis = millis();
wifiReconnectPending = true;
// Do not attempt to connect yet, wait for the next invocation
return 5000; // Schedule next check soon
}
if (!WiFi.isConnected()) {
// Check if we are ready to proceed with the WiFi connection after the 5s wait
if (wifiReconnectPending) {
if (millis() - wifiReconnectStartMillis >= 5000) {
if (!WiFi.isConnected()) {
#ifdef CONFIG_IDF_TARGET_ESP32C3
WiFi.mode(WIFI_MODE_NULL);
WiFi.useStaticBuffers(true);
WiFi.mode(WIFI_STA);
WiFi.mode(WIFI_MODE_NULL);
WiFi.useStaticBuffers(true);
WiFi.mode(WIFI_STA);
#endif
WiFi.begin(wifiName, wifiPsw);
WiFi.begin(wifiName, wifiPsw);
}
isReconnecting = false;
wifiReconnectPending = false;
} else {
// Still waiting for 5s to elapse
return 100; // Check again soon
}
isReconnecting = false;
}
#ifndef DISABLE_NTP
@@ -193,8 +210,6 @@ static int32_t reconnectWiFi()
if (config.network.wifi_enabled && !WiFi.isConnected()) {
#ifdef ARCH_RP2040 // (ESP32 handles this in WiFiEvent)
/* If APStartupComplete, but we're not connected, try again.
Shouldn't try again before APStartupComplete. */
needReconnect = APStartupComplete;
#endif
return 1000; // check once per second
@@ -486,4 +501,4 @@ uint8_t getWifiDisconnectReason()
{
return wifiDisconnectReason;
}
#endif
#endif // HAS_WIFI

View File

@@ -293,6 +293,12 @@ bool ExternalNotificationModule::getExternal(uint8_t index)
return externalCurrentState[index];
}
// Allow other firmware components to determine whether a notification is ongoing
bool ExternalNotificationModule::nagging()
{
return isNagging;
}
void ExternalNotificationModule::stopNow()
{
rtttl::stop();

View File

@@ -40,6 +40,8 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
void setMute(bool mute) { isMuted = mute; }
bool getMute() { return isMuted; }
bool nagging();
void stopNow();
void handleGetRingtone(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response);

View File

@@ -265,7 +265,6 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
}
LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i);
lastSentToMesh = millis();
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
@@ -276,13 +275,18 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
meshtastic_MeshPacket *PositionModule::allocReply()
{
if (config.device.role != meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND && lastSentToMesh &&
Throttle::isWithinTimespanMs(lastSentToMesh, 3 * 60 * 1000)) {
LOG_DEBUG("Skip Position reply since we sent it <3min ago");
if (config.device.role != meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND && lastSentReply &&
Throttle::isWithinTimespanMs(lastSentReply, 3 * 60 * 1000)) {
LOG_DEBUG("Skip Position reply since we sent a reply <3min ago");
ignoreRequest = true; // Mark it as ignored for MeshModule
return nullptr;
}
return allocPositionPacket();
meshtastic_MeshPacket *reply = allocPositionPacket();
if (reply) {
lastSentReply = millis(); // Track when we sent this reply
}
return reply;
}
meshtastic_MeshPacket *PositionModule::allocAtakPli()

View File

@@ -63,7 +63,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
void sendLostAndFoundText();
bool hasQualityTimesource();
bool hasGPS();
uint32_t lastSentToMesh = 0; // Last time we sent our position to the mesh
uint32_t lastSentReply = 0; // Last time we sent a position reply (used for reply throttling only)
const uint32_t minimumTimeThreshold =
Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);

View File

@@ -19,8 +19,8 @@
#include <OLEDDisplayUi.h>
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
// Sensors
// Sensors
#include "Sensor/CGRadSensSensor.h"
#include "Sensor/RCWL9620Sensor.h"
#include "Sensor/nullSensor.h"
@@ -101,6 +101,13 @@ SHTC3Sensor shtc3Sensor;
NullSensor shtc3Sensor;
#endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
#include "Sensor/RAK12035Sensor.h"
RAK12035Sensor rak12035Sensor;
#else
NullSensor rak12035Sensor;
#endif
#if __has_include(<Adafruit_VEML7700.h>)
#include "Sensor/VEML7700Sensor.h"
VEML7700Sensor veml7700Sensor;
@@ -173,6 +180,7 @@ NullSensor pct2075Sensor;
RCWL9620Sensor rcwl9620Sensor;
CGRadSensSensor cgRadSens;
#endif
#ifdef T1000X_SENSOR_EN
#include "Sensor/T1000xSensor.h"
@@ -182,6 +190,7 @@ T1000xSensor t1000xSensor;
#include "Sensor/IndicatorSensor.h"
IndicatorSensor indicatorSensor;
#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -288,6 +297,11 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = rak9154Sensor.runOnce();
#endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.runOnce();
}
#endif
#endif
}
// it's possible to have this module enabled, only for displaying values on the screen.
@@ -625,6 +639,14 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
valid = valid && rak9154Sensor.getMetrics(m);
hasSensor = true;
#endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \
RAK_4631 == \
1 // Not really needed, but may as well just skip at a lower level it if no library or not a RAK_4631
if (rak12035Sensor.hasSensor()) {
valid = valid && rak12035Sensor.getMetrics(m);
hasSensor = true;
}
#endif
#endif
return valid && hasSensor;
}
@@ -679,6 +701,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("Send: radiation=%fµR/h", m.variant.environment_metrics.radiation);
LOG_INFO("Send: soil_temperature=%f, soil_moisture=%u", m.variant.environment_metrics.soil_temperature,
m.variant.environment_metrics.soil_moisture);
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
@@ -850,8 +875,17 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \
RAK_4631 == \
1 // Not really needed, but may as well just skip it at a lower level if no library or not a RAK_4631
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#endif
#endif
return result;
}
#endif
#endif

View File

@@ -29,13 +29,15 @@ bool HostMetricsModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
if (t->which_variant == meshtastic_Telemetry_host_metrics_tag) {
#ifdef DEBUG_PORT
const char *sender = getSenderShortName(mp);
if (t->variant.host_metrics.has_user_string)
t->variant.host_metrics.user_string[sizeof(t->variant.host_metrics.user_string) - 1] = '\0';
LOG_INFO("(Received Host Metrics from %s): uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f, %s",
sender, t->variant.host_metrics.uptime_seconds, t->variant.host_metrics.diskfree1_bytes,
LOG_INFO("(Received Host Metrics from %s): uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f", sender,
t->variant.host_metrics.uptime_seconds, t->variant.host_metrics.diskfree1_bytes,
t->variant.host_metrics.freemem_bytes, static_cast<float>(t->variant.host_metrics.load1) / 100,
static_cast<float>(t->variant.host_metrics.load5) / 100,
static_cast<float>(t->variant.host_metrics.load15) / 100,
t->variant.host_metrics.has_user_string ? t->variant.host_metrics.user_string : "");
static_cast<float>(t->variant.host_metrics.load15) / 100);
// t->variant.host_metrics.has_user_string ? t->variant.host_metrics.user_string : "");
#endif
}
return false; // Let others look at this message also if they want
@@ -111,7 +113,8 @@ meshtastic_Telemetry HostMetricsModule::getHostMetrics()
if (settingsStrings[hostMetrics_user_command] != "") {
std::string userCommandResult = exec(settingsStrings[hostMetrics_user_command].c_str());
if (userCommandResult.length() > 1) {
strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), 200);
strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), sizeof(t.variant.host_metrics.user_string));
t.variant.host_metrics.user_string[sizeof(t.variant.host_metrics.user_string) - 1] = '\0';
t.variant.host_metrics.has_user_string = true;
}
}
@@ -121,12 +124,12 @@ meshtastic_Telemetry HostMetricsModule::getHostMetrics()
bool HostMetricsModule::sendMetrics()
{
meshtastic_Telemetry telemetry = getHostMetrics();
LOG_INFO("Send: uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f %s",
LOG_INFO("Send: uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f",
telemetry.variant.host_metrics.uptime_seconds, telemetry.variant.host_metrics.diskfree1_bytes,
telemetry.variant.host_metrics.freemem_bytes, static_cast<float>(telemetry.variant.host_metrics.load1) / 100,
static_cast<float>(telemetry.variant.host_metrics.load5) / 100,
static_cast<float>(telemetry.variant.host_metrics.load15) / 100,
telemetry.variant.host_metrics.has_user_string ? telemetry.variant.host_metrics.user_string : "");
static_cast<float>(telemetry.variant.host_metrics.load15) / 100);
// telemetry.variant.host_metrics.has_user_string ? telemetry.variant.host_metrics.user_string : "");
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
p->to = NODENUM_BROADCAST;
@@ -137,4 +140,4 @@ bool HostMetricsModule::sendMetrics()
service->sendToMesh(p, RX_SRC_LOCAL, true);
return true;
}
#endif
#endif

View File

@@ -0,0 +1,109 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RAK12035Sensor.h"
RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {}
int32_t RAK12035Sensor::runOnce()
{
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// TODO:: check for up to 2 additional sensors and start them if present.
sensor.set_sensor_addr(RAK120351_ADDR);
delay(100);
sensor.begin(nodeTelemetrySensorsMap[sensorType].first);
// Get sensor firmware version
uint8_t data = 0;
sensor.get_sensor_version(&data);
if (data != 0) {
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("RAK12035Sensor Init Succeed \nSensor1 Firmware version: %i, Sensor Name: %s", data, sensorName);
status = true;
sensor.sensor_sleep();
} else {
// If we reach here, it means the sensor did not initialize correctly.
LOG_INFO("Init sensor: %s", sensorName);
LOG_ERROR("RAK12035Sensor Init Failed");
status = false;
}
return initI2CSensor();
}
void RAK12035Sensor::setup()
{
// Set the calibration values
// Reading the saved calibration values from the sensor.
// TODO:: Check for and run calibration check for up to 2 additional sensors if present.
uint16_t zero_val = 0;
uint16_t hundred_val = 0;
uint16_t default_zero_val = 550;
uint16_t default_hundred_val = 420;
sensor.sensor_on();
delay(200);
sensor.get_dry_cal(&zero_val);
sensor.get_wet_cal(&hundred_val);
delay(200);
if (zero_val == 0 || zero_val <= hundred_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Dry Calibration: %d", default_zero_val);
sensor.set_dry_cal(default_zero_val);
sensor.get_dry_cal(&zero_val);
LOG_INFO("Dry calibration reset complete. New value is %d", zero_val);
}
if (hundred_val == 0 || hundred_val >= zero_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Wet Calibration: %d", default_hundred_val);
sensor.set_wet_cal(default_hundred_val);
sensor.get_wet_cal(&hundred_val);
LOG_INFO("Wet calibration reset complete. New value is %d", hundred_val);
}
sensor.sensor_sleep();
delay(200);
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
}
bool RAK12035Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
// TODO:: read and send metrics for up to 2 additional soil monitors if present.
// -- how to do this.. this could get a little complex..
// ie - 1> we combine them into an average and send that, 2> we send them as separate metrics
// ^-- these scenarios would require different handling of the metrics in the receiving end and maybe a setting in the
// device ui and an additional proto for that?
measurement->variant.environment_metrics.has_soil_temperature = true;
measurement->variant.environment_metrics.has_soil_moisture = true;
uint8_t moisture = 0;
uint16_t temp = 0;
bool success = false;
sensor.sensor_on();
delay(200);
success = sensor.get_sensor_moisture(&moisture);
delay(200);
success &= sensor.get_sensor_temperature(&temp);
delay(200);
sensor.sensor_sleep();
if (success == false) {
LOG_ERROR("Failed to read sensor data");
return false;
}
measurement->variant.environment_metrics.soil_temperature = ((float)temp / 10.0f);
measurement->variant.environment_metrics.soil_moisture = moisture;
return true;
}
#endif

View File

@@ -0,0 +1,28 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<RAK12035_SoilMoisture.h>) && defined(RAK_4631)
#ifndef _MT_RAK12035VBSENSOR_H
#define _MT_RAK12035VBSENSOR_H
#endif
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RAK12035_SoilMoisture.h"
#include "TelemetrySensor.h"
#include <Arduino.h>
class RAK12035Sensor : public TelemetrySensor
{
private:
RAK12035 sensor;
protected:
virtual void setup() override;
public:
RAK12035Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@@ -41,21 +41,36 @@ void RCWL9620Sensor::begin(TwoWire *wire, uint8_t addr, uint8_t sda, uint8_t scl
float RCWL9620Sensor::getDistance()
{
uint32_t data;
_wire->beginTransmission(_addr); // Transfer data to addr.
_wire->write(0x01);
_wire->endTransmission(); // Stop data transmission with the Ultrasonic
// Unit.
uint32_t data = 0;
uint8_t b1 = 0, b2 = 0, b3 = 0;
_wire->requestFrom(_addr,
(uint8_t)3); // Request 3 bytes from Ultrasonic Unit.
LOG_DEBUG("[RCWL9620] Start measure command");
_wire->beginTransmission(_addr);
_wire->write(0x01); // À tester aussi sans cette ligne si besoin
uint8_t result = _wire->endTransmission();
LOG_DEBUG("[RCWL9620] endTransmission result = %d", result);
delay(100); // délai pour laisser le capteur répondre
LOG_DEBUG("[RCWL9620] Read i2c data:");
_wire->requestFrom(_addr, (uint8_t)3);
if (_wire->available() < 3) {
LOG_DEBUG("[RCWL9620] less than 3 octets !");
return 0.0;
}
b1 = _wire->read();
b2 = _wire->read();
b3 = _wire->read();
data = ((uint32_t)b1 << 16) | ((uint32_t)b2 << 8) | b3;
float Distance = float(data) / 1000.0;
LOG_DEBUG("[RCWL9620] Bytes readed = %02X %02X %02X", b1, b2, b3);
LOG_DEBUG("[RCWL9620] data=%.2f, level=%.2f", (double)data, (double)Distance);
data = _wire->read();
data <<= 8;
data |= _wire->read();
data <<= 8;
data |= _wire->read();
float Distance = float(data) / 1000;
if (Distance > 4500.00) {
return 4500.00;
} else {
@@ -63,4 +78,4 @@ float RCWL9620Sensor::getDistance()
}
}
#endif
#endif

View File

@@ -308,6 +308,9 @@ void cpuDeepSleep(uint32_t msecToWake)
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
nrf_gpio_cfg_default(WB_I2C1_SCL);
nrf_gpio_cfg_default(WB_I2C1_SDA);
// nrf_gpio_cfg_default(WB_I2C2_SCL);
// nrf_gpio_cfg_default(WB_I2C2_SDA);
#endif
#endif
#ifdef MESHLINK

View File

@@ -89,6 +89,13 @@ extra_scripts =
${env.extra_scripts}
variants/diy/nrf52_promicro_diy_tcxo/custom_build_tasks.py ; Add to PIO's Project Tasks pane: preset builds for common displays
; Seeed Xiao BLE: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010448/16652893
[env:xiao_ble]
extends = env:seeed_xiao_nrf52840_kit
board_level = extra
build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DXIAO_BLE_LEGACY_PINOUT -DEBYTE_E22 -DEBYTE_E22_900M30S
build_unflags = -DGPS_L76K
; Seeed XIAO nRF52840 + XIAO Wio SX1262 DIY
[env:seeed-xiao-nrf52840-wio-sx1262]
board = xiao_ble_sense

View File

@@ -2,5 +2,5 @@
extends = esp32s3_base
board = lolin_s3_mini
board_level = extra
build_flags =
${esp32s3_base.build_flags} -D PRIVATE_HW -I variants/nugget_s3_lora
build_flags =
${esp32s3_base.build_flags} -D ARDUINO_USB_CDC_ON_BOOT=1 -D PRIVATE_HW -I variants/nugget_s3_lora

View File

@@ -1,5 +1,8 @@
#define I2C_SDA 34 // I2C pins for this board
#define I2C_SCL 38
#define I2C_SDA 35 // I2C pins for this board
#define I2C_SCL 36
#define USE_SSD1306
#define DISPLAY_FLIP_SCREEN
#define LED_PIN 15 // If defined we will blink this LED
@@ -8,7 +11,8 @@
#define NEOPIXEL_DATA 10 // gpio pin used to send data to the neopixels
#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
// Button A (44), B (43), R (12), U (13), L (11), D (18)
#define BUTTON_PIN 44 // If defined, this will be used for user button presses
#define BUTTON_NEED_PULLUP
#define USE_RF95

View File

@@ -18,6 +18,7 @@ lib_deps =
melopero/Melopero RV3028@^1.1.0
https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAK12035_SoilMoisture@^1.0.4
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
@@ -51,4 +52,4 @@ lib_deps =
upload_protocol = stlink
; eventually use platformio/tool-pyocd@^2.3600.0 instad
;upload_protocol = custom
;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE
;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE

View File

@@ -88,8 +88,13 @@ static const uint8_t A7 = PIN_A7;
#define ADC_RESOLUTION 14
// Other pins
#define WB_I2C1_SDA (13) // SENSOR_SLOT IO_SLOT
#define WB_I2C1_SCL (14) // SENSOR_SLOT IO_SLOT
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -143,8 +148,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (13)
#define PIN_WIRE_SCL (14)
#define PIN_WIRE_SDA (WB_I2C1_SDA)
#define PIN_WIRE_SCL (WB_I2C1_SCL)
// QSPI Pins
#define PIN_QSPI_SCK 3
@@ -227,6 +232,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base
@@ -280,4 +286,4 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -16,6 +16,7 @@ lib_deps =
melopero/Melopero RV3028@^1.1.0
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK12035_SoilMoisture@^1.0.4
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -188,6 +190,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base
@@ -231,4 +234,4 @@ static const uint8_t SCK = PIN_SPI_SCK;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -18,6 +18,7 @@ lib_deps =
melopero/Melopero RV3028@^1.1.0
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK12035_SoilMoisture@^1.0.4
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -69,7 +69,9 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
// #define PIN_NFC1 (9)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
// #define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -160,6 +162,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// NO GPS
#undef GPS_RX_PIN
@@ -202,4 +205,4 @@ static const uint8_t SCK = PIN_SPI_SCK;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -217,6 +219,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base
@@ -270,4 +273,4 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -22,6 +22,7 @@ lib_deps =
bodmer/TFT_eSPI
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK14014-FT6336U @ 1.0.1
beegee-tokyo/RAK12035_SoilMoisture@^1.0.4
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -176,11 +178,11 @@ static const uint8_t SCK = PIN_SPI_SCK;
// No reason not to have the RAK Wireless pin defs here too. This allows code from example RAK sketches to run without
// modification.
static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B
static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B
static const uint8_t WB_IO3 = 21; // SLOT_C
static const uint8_t WB_IO4 = 4; // SLOT_C
static const uint8_t WB_IO5 = 9; // SLOT_D
static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B
static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B
static const uint8_t WB_IO3 = 21; // SLOT_C
// static const uint8_t WB_IO4 = 4; // SLOT_C <- already defined above (ln. 94)
// static const uint8_t WB_IO5 = 9; // SLOT_D <- already defined above (ln. 93)
static const uint8_t WB_IO6 = 10; // SLOT_D
static const uint8_t WB_SW1 = 33; // IO_SLOT
static const uint8_t WB_A0 = 5; // IO_SLOT
@@ -314,4 +316,4 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -59,8 +59,8 @@ const uint32_t g_ADigitalPinMap[] = {
// D16 - Battery voltage ADC input
31, // D16 P0.31 VBAT_ADC
// GROVE
0, // D17 P0.00 GROVESDA
1, // D18 P0.01 GROVESCL
43, // D17 P0.00 GROVESDA
44, // D18 P0.01 GROVESCL
// FLASH
21, // D19 P0.21 (QSPI_SCK)

View File

@@ -74,10 +74,12 @@
// Communication Interfaces
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// I2C Configuration
#define HAS_WIRE 1
// #define HAS_WIRE 1
#define PIN_WIRE_SDA D14 // P0.09
#define PIN_WIRE_SCL D15 // P0.10
#define WIRE_INTERFACES_COUNT 1
#define WIRE_INTERFACES_COUNT 2
#define PIN_WIRE1_SDA D18
#define PIN_WIRE1_SCL D17
#define I2C_NO_RESCAN
static const uint8_t SDA = PIN_WIRE_SDA;

View File

@@ -2,7 +2,7 @@
[env:seeed_xiao_nrf52840_kit]
extends = nrf52840_base
board = xiao_ble_sense
build_flags = ${nrf52840_base.build_flags} -Ivariants/seeed_xiao_nrf52840_kit -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DSEEED_XIAO_NRF52840_KIT
build_flags = ${nrf52840_base.build_flags} -Ivariants/seeed_xiao_nrf52840_kit -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DSEEED_XIAO_NRF52840_KIT -DGPS_L76K
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/seeed_xiao_nrf52840_kit>
lib_deps =

View File

@@ -1,5 +1,5 @@
#ifndef _SEEED_XIAO_NRF52840_SENSE_H_
#define _SEEED_XIAO_NRF52840_SENSE_H_
#ifndef _SEEED_XIAO_NRF52840_KIT_H_
#define _SEEED_XIAO_NRF52840_KIT_H_
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
@@ -79,9 +79,8 @@ static const uint8_t A5 = PIN_A5;
*/
/*
* D0 is shared with PIN_GPS_STANDBY on the L76K GNSS Module.
* There are some technical solutions that can solve this problem, and we are
* currently exploring and researching them.
* D0 is shared with PIN_GPS_STANDBY on the L76K GNSS Module, so refer to
* GPS_L76K definition preventing this conflict
*/
// #define BUTTON_PIN D0
@@ -93,51 +92,60 @@ static const uint8_t A5 = PIN_A5;
#define PIN_SERIAL2_TX (-1)
/*
* SPI Interfaces
* Pinout for SX126x
*/
#define SPI_INTERFACES_COUNT 1
#define PIN_SPI_MISO (9)
#define PIN_SPI_MOSI (10)
#define PIN_SPI_SCK (8)
static const uint8_t SS = D4;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
#define USE_SX1262
// Pinout for SX126X
#ifdef XIAO_BLE_LEGACY_PINOUT
// Legacy xiao_ble variant pinout for third-party SX126x modules e.g. EBYTE E22
#define SX126X_CS D0
#define SX126X_DIO1 D1
#define SX126X_BUSY D2
#define SX126X_RESET D3
#define SX126X_RXEN D7
#elif defined(SEEED_XIAO_WIO_BTB)
// Wio-SX1262 for XIAO with 30-pin board-to-board connector
// https://files.seeedstudio.com/products/SenseCAP/Wio_SX1262/Schematic_Diagram_Wio-SX1262_for_XIAO.pdf
#define SX126X_CS D3
#define SX126X_DIO1 D0
#define SX126X_BUSY D1
#define SX126X_RESET D2
#define SX126X_RXEN D4
#else
// Wio-SX1262 for XIAO (standalone SKU 113010003 or nRF52840 kit SKU 102010710)
// https://files.seeedstudio.com/products/SenseCAP/Wio_SX1262/Wio-SX1262%20for%20XIAO%20V1.0_SCH.pdf
#define SX126X_CS D4
#define SX126X_DIO1 D1
#define SX126X_BUSY D3
#define SX126X_RESET D2
#define SX126X_RXEN D5
#endif
// Common pinouts for all SX126x pinouts above
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_RXEN D5 // This is used to control the RX side of the RF switch
#define SX126X_DIO2_AS_RF_SWITCH // DIO2 is used to control the TX side of the RF switch
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
/*
* Wire Interfaces
* SPI Interfaces
* Defined after pinout for SX1262x to factor in CS pinout variations
*/
#define I2C_NO_RESCAN // I2C is a bit finicky, don't scan too much
#define WIRE_INTERFACES_COUNT 1 // 2
#define SPI_INTERFACES_COUNT 1
// LSM6DS3TR on XIAO nRF52840 Series
#define PIN_WIRE_SDA (17)
#define PIN_WIRE_SCL (16)
#define PIN_SPI_MISO D9
#define PIN_SPI_MOSI D10
#define PIN_SPI_SCK D8
static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;
static const uint8_t SS = SX126X_CS;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* GPS
*/
// GPS L76KB
#define GPS_L76K
// GPS L76K
#ifdef GPS_L76K
#define PIN_GPS_RX D6
#define PIN_GPS_TX D7
@@ -146,6 +154,9 @@ static const uint8_t SCL = PIN_WIRE_SCL;
#define PIN_SERIAL1_RX PIN_GPS_TX
#define PIN_SERIAL1_TX PIN_GPS_RX
#define PIN_GPS_STANDBY D0
#else
#define PIN_SERIAL1_RX (-1)
#define PIN_SERIAL1_TX (-1)
#endif
/*
@@ -161,6 +172,39 @@ static const uint8_t SCL = PIN_WIRE_SCL;
#define BATTERY_SENSE_RESOLUTION_BITS (10)
/*
* Wire Interfaces
* Keep this section after potentially conflicting pin definitions
*/
#define I2C_NO_RESCAN // I2C is a bit finicky, don't scan too much
#define WIRE_INTERFACES_COUNT 1
#if !defined(XIAO_BLE_LEGACY_PINOUT) && !defined(GPS_L76K)
// If D6 and D7 are free, I2C is probably the most versatile assignment
#define PIN_WIRE_SDA D6
#define PIN_WIRE_SCL D7
#else
// Internal LSM6DS3TR on XIAO nRF52840 Series
#define PIN_WIRE_SDA (17)
#define PIN_WIRE_SCL (16)
#endif
static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;
/*
* Buttons
* Keep this section after potentially conflicting pin definitions
* because D0 has multiple possible conflicts with various XIAO modules:
* - PIN_GPS_STANDBY on the L76K GNSS Module
* - DIO1 on the Wio-SX1262 - 30-pin board-to-board connector version
* - SX1262X CS on XIAO BLE legacy pinout
*/
#if !defined(GPS_L76K) && !defined(SEEED_XIAO_WIO_BTB) && !defined(XIAO_BLE_OLD_PINOUT)
#define BUTTON_PIN D0
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -139,6 +139,7 @@ External serial flash WP25R1635FZUIL0
// Not really an E22 but TTGO seems to be trying to clone that
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define TCXO_OPTIONAL
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
@@ -214,7 +215,7 @@ External serial flash WP25R1635FZUIL0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (2.0F)
#define NO_EXT_GPIO 1
// #define NO_EXT_GPIO 1
#define HAS_RTC 1

View File

@@ -1,13 +0,0 @@
; Seeed Xiao BLE: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010448/16652893
[env:xiao_ble]
extends = nrf52840_base
board = xiao_ble_sense
board_level = extra
build_flags = ${nrf52840_base.build_flags} -Ivariants/xiao_ble -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -D EBYTE_E22 -DEBYTE_E22_900M30S -DPRIVATE_HW
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/xiao_ble>
lib_deps =
${nrf52840_base.lib_deps}
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -1,62 +0,0 @@
#include "variant.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// D0 .. D13
2, // D0 is P0.02 (A0)
3, // D1 is P0.03 (A1)
28, // D2 is P0.28 (A2)
29, // D3 is P0.29 (A3)
4, // D4 is P0.04 (A4,SDA)
5, // D5 is P0.05 (A5,SCL)
43, // D6 is P1.11 (TX)
44, // D7 is P1.12 (RX)
45, // D8 is P1.13 (SCK)
46, // D9 is P1.14 (MISO)
47, // D10 is P1.15 (MOSI)
// LEDs
26, // D11 is P0.26 (LED RED)
6, // D12 is P0.06 (LED BLUE)
30, // D13 is P0.30 (LED GREEN)
14, // D14 is P0.14 (READ_BAT)
// LSM6DS3TR
40, // D15 is P1.08 (6D_PWR)
27, // D16 is P0.27 (6D_I2C_SCL)
7, // D17 is P0.07 (6D_I2C_SDA)
11, // D18 is P0.11 (6D_INT1)
// MIC
42, // 17,//42, // D19 is P1.10 (MIC_PWR)
32, // 26,//32, // D20 is P1.00 (PDM_CLK)
16, // 25,//16, // D21 is P0.16 (PDM_DATA)
// BQ25100
13, // D22 is P0.13 (HICHG)
17, // D23 is P0.17 (~CHG)
//
21, // D24 is P0.21 (QSPI_SCK)
25, // D25 is P0.25 (QSPI_CSN)
20, // D26 is P0.20 (QSPI_SIO_0 DI)
24, // D27 is P0.24 (QSPI_SIO_1 DO)
22, // D28 is P0.22 (QSPI_SIO_2 WP)
23, // D29 is P0.23 (QSPI_SIO_3 HOLD)
// NFC
9, // D30 is P0.09 (NFC1)
10, // D31 is P0.10 (NFC2)
// VBAT
31, // D32 is P0.10 (VBAT)
};
void initVariant()
{
// Set BQ25101 ISET to 100mA instead of 50mA
pinMode(HICHG, OUTPUT);
digitalWrite(HICHG, LOW);
}

View File

@@ -1,212 +0,0 @@
#ifndef _SEEED_XIAO_NRF52840_SENSE_H_
#define _SEEED_XIAO_NRF52840_SENSE_H_
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
// #define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define PINS_COUNT (33)
#define NUM_DIGITAL_PINS (33)
#define NUM_ANALOG_INPUTS (8) // A6 is used for battery, A7 is analog reference
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define LED_RED 11
#define LED_BLUE 12
#define LED_GREEN 13
#define PIN_LED1 LED_GREEN
#define PIN_LED2 LED_BLUE
#define PIN_LED3 LED_RED
#define PIN_LED PIN_LED1
#define LED_PWR (PINS_COUNT)
#define LED_BUILTIN PIN_LED
#define LED_STATE_ON 1 // State when LED is lit
/*
* Buttons
*/
#define PIN_BUTTON1 (PINS_COUNT)
// Digital PINs
#define D0 (0ul)
#define D1 (1ul)
#define D2 (2ul)
#define D3 (3ul)
#define D4 (4ul)
#define D5 (5ul)
#define D6 (6ul)
#define D7 (7ul)
#define D8 (8ul)
#define D9 (9ul)
#define D10 (10ul)
/*
* Analog pins
*/
#define PIN_A0 (0)
#define PIN_A1 (1)
#define PIN_A2 (2)
#define PIN_A3 (3)
#define PIN_A4 (4)
#define PIN_A5 (5)
#define PIN_VBAT (32)
#define VBAT_ENABLE (14)
static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
#define ADC_RESOLUTION 12
// Other pins
#define PIN_NFC1 (30)
#define PIN_NFC2 (31)
/*
* Serial interfaces
*/
#define PIN_SERIAL1_RX (-1) // (7)
#define PIN_SERIAL1_TX (-1) // (6)
#define PIN_SERIAL2_RX (-1)
#define PIN_SERIAL2_TX (-1)
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 1
#define PIN_SPI_MISO (9)
#define PIN_SPI_MOSI (10)
#define PIN_SPI_SCK (8)
static const uint8_t SS = D0;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
// supported modules list
#define USE_SX1262
// common pinouts for SX126X modules
#define SX126X_CS D0
#define SX126X_DIO1 D1
#define SX126X_BUSY D2
#define SX126X_RESET D3
// ----------------------------------------------------------------
// E22 Tx/Rx control options:
// 1. Let the E22 control Tx and Rx automagically via DIO2.
// * The E22's TXEN and DIO2 pins are connected to each other, but not to the MCU.
// * The E22's RXEN pin *is* connected to the MCU.
// * E22_TXEN_CONNECTED_TO_DIO2 is defined so the logic in SX126XInterface.cpp handles this configuration correctly.
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_RXEN D7
// ------------------------------ OR ------------------------------
// 2. Control Tx and Rx manually.
// * The E22's TXEN and RXEN pins are both connected to the MCU.
// #define SX126X_TXEN D6
// #define SX126X_RXEN D7
// ----------------------------------------------------------------
#ifdef EBYTE_E22
// Internally the TTGO module hooks the SX126x-DIO2 in to control the TX/RX switch
// (which is the default for the sx1262interface code)
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#ifdef EBYTE_E22_900M30S
// 10dB PA gain and 30dB rated output; based on measurements from
// https://github.com/S5NC/EBYTE_ESP32-S3/blob/main/E22-900M30S%20power%20output%20testing.txt
#define TX_GAIN_LORA 7
#define SX126X_MAX_POWER 22
#endif
#ifdef EBYTE_E22_900M33S
// 25dB PA gain and 33dB rated output; based on TX Power Curve from E22-900M33S_UserManual_EN_v1.0.pdf
#define TX_GAIN_LORA 25
#define SX126X_MAX_POWER 8
#endif
#endif
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1 // 2
#define PIN_WIRE_SDA (4)
#define PIN_WIRE_SCL (5)
static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;
#define PIN_LSM6DS3TR_C_POWER (15)
#define PIN_LSM6DS3TR_C_INT1 (18)
// PDM Interfaces
// ---------------
#define PIN_PDM_PWR (19)
#define PIN_PDM_CLK (20)
#define PIN_PDM_DIN (21)
// QSPI Pins
#define PIN_QSPI_SCK (24)
#define PIN_QSPI_CS (25)
#define PIN_QSPI_IO0 (26)
#define PIN_QSPI_IO1 (27)
#define PIN_QSPI_IO2 (28)
#define PIN_QSPI_IO3 (29)
// On-board QSPI Flash
#define EXTERNAL_FLASH_DEVICES P25Q16H
#define EXTERNAL_FLASH_USE_QSPI
// Battery
#define ADC_CTRL VBAT_ENABLE // P0.14: VBAT voltage divider
#define ADC_CTRL_ENABLED LOW // ... sink
#define BATTERY_SENSE_RESOLUTION_BITS 10
#define CHARGE_LED 23 // P0_17 = 17 D23 YELLOW CHARGE LED
#define HICHG 22 // P0_13 = 13 D22 Charge-select pin for Lipo for 100 mA instead of default 50mA charge
// The battery sense is hooked to pin A0 (5)
#define BATTERY_PIN PIN_VBAT // PIN_A0
// ratio of voltage divider = 3.0 (R17=1M, R18=510k)
#define ADC_MULTIPLIER 3 // 3.0 + a bit for being optimistic
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@@ -1,15 +0,0 @@
#!/bin/bash
# adapted from the script linked in this very helpful article: https://enzolombardi.net/low-power-bluetooth-advertising-with-xiao-ble-and-platformio-e8e7d0da80d2
# source: https://gist.githubusercontent.com/turing-complete-labs/b3105ee653782183c54b4fdbe18f411f/raw/d86779ba7702775d3b79781da63d85442acd9de6/xiao_ble.sh
# download the core for arduino from seeedstudio. Softdevice 7.3.0, linker and variants folder are what we need
curl https://files.seeedstudio.com/arduino/core/nRF52840/Arduino_core_nRF52840.tar.bz2 -o arduino.core.1.0.0.tar.bz2
tar -xjf arduino.core.1.0.0.tar.bz2
rm arduino.core.1.0.0.tar.bz2
# copy the needed files
cp 1.0.0/cores/nRF5/linker/nrf52840_s140_v7.ld ~/.platformio/packages/framework-arduinoadafruitnrf52/cores/nRF5/linker
cp -r 1.0.0/cores/nRF5/nordic/softdevice/s140_nrf52_7.3.0_API ~/.platformio/packages/framework-arduinoadafruitnrf52/cores/nRF5/nordic/softdevice
rm -rf 1.0.0
echo done!

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 6
build = 12
build = 13