Compare commits

..

63 Commits

Author SHA1 Message Date
todd-herbert
64531fa1ae Show compass on waypoint frame; clear when waypoint deleted (#4116)
* Clear expired or deleted waypoint frame

* Return 0 to CallbackObserver

* Add a missing comment

* Draw compass for waypoint frame

* Display our own waypoints
2024-06-24 02:04:46 -05:00
Warren Guy
23ac6b6514 fix INA3221 sensor (#4168)
- pass wire to begin()
- remove redundant setAddr() (already set in header)
2024-06-23 15:40:13 -05:00
Ben Meadors
f5098dc6d8 Explicitly set characteristic 2024-06-23 14:47:25 -05:00
Ben Meadors
2e0d96cece Esptool is better 2024-06-23 07:54:13 -05:00
GUVWAF
8078e03f5f Implement replies for all telemetry types based on variant tag (#4164)
* Implement replies for all telemetry types based on variant tag

* Remove check for `ignoreRequest`: modules can set this, don't need to check

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-06-23 07:13:59 -05:00
Ben Meadors
eb6bd3a06f Update NimBLE to 1.4.2 (#4163) 2024-06-22 08:49:55 -05:00
Ben Meadors
d32cdecc06 Pause BLE logging during want_config flow (#4162) 2024-06-22 07:00:48 -05:00
github-actions[bot]
f8db38cf99 [create-pull-request] automated change (#4157)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-06-21 19:02:16 -05:00
Ben Meadors
02d8715ca0 Standardize lat/lon position logs (#4156)
* Standardize lat/lon position logs

* Missed sone and condensed logs
2024-06-21 17:25:54 -05:00
Mike G
0dd363fa98 Use upload_protocol = esptool as with the other heltec devices instead of esp-builtin (#4151) 2024-06-20 20:01:36 -05:00
Ben Meadors
0bcc60d535 BLE based logging (#4146)
* WIP log characteristic

* Bluetooth logging plumbing

* Characteristic

* Callback

* Check for nullptr

* Esp32 bluetooth impl

* Formatting

* Add thread name and log level

* Add settings guard

* Remove comments

* Field name

* Fixes esp32

* Open it up

* Whoops

* Move va_end past our logic
2024-06-20 16:14:55 -05:00
Mike
2d39911f91 Fix protobuf structs handling (#4140)
* Fix protobuf structs handling

* Log instead of assert

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-06-20 16:14:34 -05:00
Thomas Göttgens
ddc406209b Merge pull request #4149 from geeksville/pr-fixserial
Make serial port on wio-sdk-wm1110 board work
2024-06-20 22:56:31 +02:00
Thomas Göttgens
f145b5f16f Merge pull request #3836 from ndoo/regulatory-gain
Add REGULATORY_GAIN configuration to remain within regulatory ERP limit
2024-06-20 22:53:18 +02:00
geeksville
e050888b26 Don't complain about wierd platformio python 2024-06-20 12:28:43 -07:00
geeksville
9266a53f4e Make serial port on wio-sdk-wm1110 board work
By disabling the (inaccessible) adafruit USB
2024-06-20 09:49:15 -07:00
Thomas Göttgens
9a80951d6f Merge pull request #4129 from ndoo/hru-3601
Add support for Heltec HRU-3601
2024-06-20 16:42:44 +02:00
Thomas Göttgens
f0a38a5cf0 Merge branch 'master' into regulatory-gain 2024-06-20 16:13:54 +02:00
Andrew Yong
1515c8e763 Add support for Heltec HRU-3601
Board is very similar to the Heltec HT-C62 based boards (Heltec HT62 variant) but due to wiring of SK6812 Neopixel LED to GPIO2 it becomes incompatible due to the regular HT-C62 dev board using a simple LED on the same GPIO. Depends on [protobufs#521](https://github.com/meshtastic/protobufs/pull/521).

Works:

* SK6812 Neopixel on GPIO2
* [GXCAS GXHTV3](https://www.lcsc.com/product-detail/Temperature-Sensors_GXCAS-GXHTV3C_C5441730.html) (SHTC3 compatible)

Won't fix:

* Battery reading - Board has no voltage divider on VBAT (board has a 1.25mm pitch "JST" style connector and a TP4054 charge IC)
* Main thread LED - Board has no LED on simple GPIO

Board schematic: [HRU3601.pdf](https://github.com/user-attachments/files/15874850/HRU3601.pdf)

Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-20 12:30:02 +08:00
Andrew Yong
3e9e0fdd49 Remove TTGO_T_ECHO gating for PIN_POWER_EN
Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-20 12:30:02 +08:00
Thomas Göttgens
ca560d64ea Merge pull request #4143 from meshtastic/fix-portduino
mask the I2C rescan for portduino
2024-06-19 22:33:18 +02:00
github-actions[bot]
c59cb3c292 [create-pull-request] automated change (#4145)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-06-19 14:48:46 -05:00
Thomas Göttgens
f79039fe57 mask the rescan for portduino 2024-06-19 21:46:29 +02:00
Thomas Göttgens
e780b9a798 Merge pull request #4142 from meshtastic/create-pull-request/patch
Changes by create-pull-request action
2024-06-19 21:32:37 +02:00
caveman99
3c4fa2101f [create-pull-request] automated change 2024-06-19 19:31:05 +00:00
Thomas Göttgens
8fa0911ec8 speed up OLED Display by transferring bigger chunks (#4138) 2024-06-18 15:59:47 -05:00
Thomas Göttgens
5fceab7f0f Merge pull request #3933 from HarukiToreda/master
Boot issue fix of Cardkb on ESP32
2024-06-17 23:24:42 +02:00
Thomas Göttgens
5cebe4a0a7 trunk fmt 2024-06-17 23:24:27 +02:00
Thomas Göttgens
275e393115 Merge branch 'master' into master 2024-06-17 22:33:54 +02:00
Manuel
e7181988b6 Merge pull request #4130 from meshtastic/fix-module-defines
fix for MESHTASTIC_EXCLUDE_INPUTBROKER
2024-06-17 22:17:38 +02:00
mverch67
e822525ce5 more semi colons >_< 2024-06-17 21:23:07 +02:00
mverch67
5e92136ed0 semi colon 2024-06-17 21:19:36 +02:00
mverch67
cd60ee80bd fix wrong include file exclusion 2024-06-17 21:17:25 +02:00
mverch67
9d8a5221a9 fix for MESHTASTIC_EXCLUDE_INPUTBROKER 2024-06-17 20:17:56 +02:00
Thomas Göttgens
30b14c57e7 Merge branch 'master' into master 2024-06-17 17:06:37 +02:00
Thomas Göttgens
7a25e0b69a don't close the wire when we didn't find anything. We might rescan later. 2024-06-17 17:03:32 +02:00
Thomas Göttgens
b6066a78c1 WIP 2024-06-17 15:09:38 +02:00
github-actions[bot]
5d2f7d1962 [create-pull-request] automated change (#4127)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-06-17 07:17:36 -05:00
Thomas Göttgens
00162b4ccf Merge branch 'master' of https://github.com/HarukiToreda/ESP32-CardKB-Fix 2024-06-17 12:01:26 +02:00
Thomas Göttgens
ce3be5b4e8 Merge branch 'master' into master 2024-06-17 12:00:16 +02:00
Thomas Göttgens
cf2a824cc1 Merge branch 'master' of https://github.com/HarukiToreda/ESP32-CardKB-Fix 2024-06-17 11:52:47 +02:00
Thomas Göttgens
a453d7f52c Iterate through uint array 2024-06-16 11:59:21 +02:00
Thomas Göttgens
ba14ffb8d3 change type to 8 bit uint 2024-06-16 11:59:21 +02:00
Thomas Göttgens
2eb3cfd5e0 change the main scan class so they scan only for wanted bits - UNTESTED 2024-06-16 11:59:21 +02:00
HarukiToreda
ce9e63a2cb Added fix for ESP32 2024-06-16 11:59:21 +02:00
Thomas Göttgens
dbb254ba7a change the main scan class so they scan only for wanted bits - UNTESTED 2024-06-16 11:59:21 +02:00
HarukiToreda
27bb3506d3 Added fix for ESP32 2024-06-16 11:59:21 +02:00
Thomas Göttgens
97e8b1fd18 Merge branch 'master' into regulatory-gain 2024-06-16 11:35:11 +02:00
Andrew Yong
53fc22178b Merge branch 'master' into regulatory-gain 2024-06-11 21:30:35 +08:00
Andrew Yong
08e1c2f681 Rename REGULATORY_GAIN to REGULATORY_GAIN_LORA to allow for other RF gain controls
For example, Wi-Fi or BLE gain control (#3962)

Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-06 14:42:30 +08:00
Andrew Yong
537814df58 xiao_ble: Add EBYTE E22-900M33S PA gain and limits
Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-06 14:42:30 +08:00
Andrew Yong
d1d49efc6e Implement REGULATORY_GAIN and SX126X_MAX_POWER in XIAO BLE EBYTE E22
Specify REGULATORY_GAIN and SX126X_MAX_POWER to prevent exceeding regulatory and hardware limits (i.e. overloading the PA input) respectively.

Also update the build flag to define EBYTE_E22_900M30S instead of just EBYTE_E22, since all the builds on the Discourse topic [New 1W DIY variant: Xiao nRF52840 + Ebyte E22-900M30S](https://meshtastic.discourse.group/t/new-1w-diy-variant-xiao-nrf52840-ebyte-e22-900m30s/7904) are using this module.

That should make it clearer as well that the variant header file should be tweaked if DIY builds are using stronger (E22-900M33S, not commonly available at this time) or weaker (E22-900M22S, not popular for DIY builds due to lack of differentiation from ordinary SX1262 modules).

Retain EBYTE_E22 flag alongside EBYTE_E22_900M30S build flag to prevent possible regressions in code paths generally intended for EBYTE E22 modules.

Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-06 14:42:30 +08:00
Andrew Yong
3cda598673 Add REGULATORY_GAIN configuration to remain within regulatory ERP limit
REGULATORY_GAIN is the total system gain in dBm to subtract from the configured Tx power, to remain within regulatory ERP limit for non-licensed operators.

This value should be set in variant.h and is PA gain + antenna gain (if system ships with an antenna).

This is similar to antenna_gain/NL80211_ATTR_WIPHY_ANTENNA_GAIN/NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN setting in Linux Regulatory/OpenWrt/mac80211/nl80211/iw.

Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-06 14:42:30 +08:00
Andrew Yong
5554cc46a7 Add REGULATORY_ prefix to LORA_REGIONCODE
Add REGULATORY_ prefix to LORA_REGIONCODE to prepare for more regulatory configuration options, and update comment block accordingly too.

Signed-off-by: Andrew Yong <me@ndoo.sg>
2024-06-06 14:42:30 +08:00
Thomas Göttgens
c46c3427f0 Iterate through uint array 2024-05-25 12:37:55 +02:00
Thomas Göttgens
dca8615eaa change type to 8 bit uint 2024-05-24 22:59:31 +02:00
Thomas Göttgens
79511aa61e Merge branch 'master' of https://github.com/HarukiToreda/ESP32-CardKB-Fix 2024-05-24 22:35:05 +02:00
Thomas Göttgens
80762518d5 Merge branch 'master' into master 2024-05-24 22:27:21 +02:00
HarukiToreda
d2390cb98a Merge branch 'master' of https://github.com/HarukiToreda/ESP32-CardKB-Fix 2024-05-19 16:11:26 -04:00
Thomas Göttgens
a37f309c03 change the main scan class so they scan only for wanted bits - UNTESTED 2024-05-19 19:33:19 +02:00
HarukiToreda
3a628047ef Added fix for ESP32 2024-05-19 19:33:19 +02:00
Thomas Göttgens
a5fdb663e2 change the main scan class so they scan only for wanted bits - UNTESTED 2024-05-19 19:32:08 +02:00
HarukiToreda
1ec0e750a3 Added fix for ESP32 2024-05-18 22:14:22 -04:00
69 changed files with 893 additions and 312 deletions

View File

@@ -44,7 +44,7 @@ lib_deps =
${networking_base.lib_deps}
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.1
h2zero/NimBLE-Arduino@^1.4.2
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f

View File

@@ -7,13 +7,6 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "WIO-BOOT",
"mcu": "nrf52840",
"variant": "Seeed_WIO_WM1110",
"bsp": {

3
extra_scripts/README.md Normal file
View File

@@ -0,0 +1,3 @@
# extra_scripts
This directory contains special [scripts](https://docs.platformio.org/en/latest/scripting/index.html) that are used to modify the platformio environment in rare cases.

View File

@@ -0,0 +1,20 @@
# trunk-ignore-all(flake8/F821)
# trunk-ignore-all(ruff/F821)
Import("env")
# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
print("Current CLI targets", COMMAND_LINE_TARGETS)
print("Current Build targets", BUILD_TARGETS)
print("CPP defs", env.get("CPPDEFINES"))
# Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this.
# So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR
# which might not be accepted. -@geeksville
env["CPPDEFINES"].remove("USBCON")
env["CPPDEFINES"].remove("USE_TINYUSB")
# Custom actions when building program/firmware
# env.AddPreAction("buildprog", callback...)

View File

@@ -80,7 +80,7 @@ monitor_speed = 115200
lib_deps =
jgromes/RadioLib@~6.6.0
https://github.com/meshtastic/esp8266-oled-ssd1306.git#dcacac5d2c7942376bc17f7079cced6a73cb659f ; ESP8266_SSD1306
https://github.com/meshtastic/esp8266-oled-ssd1306.git#2b40affbe7f7dc63b6c00fa88e7e12ed1f8e1719 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4

View File

@@ -10,4 +10,6 @@ const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd,
const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8,
0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c};
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
const uint8_t LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};

View File

@@ -11,10 +11,11 @@
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
#define FROMRADIO_UUID "2c55e69e-4993-11ed-b878-0242ac120002"
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
#define LOGRADIO_UUID "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
// NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[];
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[], LOGRADIO_UUID_16[];
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level);
@@ -27,4 +28,5 @@ class BluetoothApi
virtual void clearBonds();
virtual bool isConnected();
virtual int getRssi() = 0;
virtual void sendLog(const char *logMessage);
};

View File

@@ -124,7 +124,7 @@ class GPSStatus : public Status
if (isDirty) {
if (hasLock) {
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
LOG_DEBUG("New GPS pos@%x:3 lat=%f, lon=%f, alt=%d, pdop=%.2f, track=%.2f, speed=%.2f, sats=%d\n", p.timestamp,
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d\n", p.timestamp,
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
p.ground_speed * 1e-2, p.sats_in_view);
} else {

View File

@@ -3,6 +3,7 @@
#include "RTC.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include "main.h"
#include <assert.h>
#include <cstring>
#include <memory>
@@ -166,9 +167,43 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
}
#endif
va_end(arg);
isContinuationMessage = !hasNewline;
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
bool isBleConnected = false;
#ifdef ARCH_ESP32
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
#elif defined(ARCH_NRF52)
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
#endif
if (isBleConnected) {
char *message;
size_t initialLen;
size_t len;
initialLen = strlen(format);
message = new char[initialLen + 1];
len = vsnprintf(message, initialLen + 1, format, arg);
if (len > initialLen) {
delete[] message;
message = new char[len + 1];
vsnprintf(message, len + 1, format, arg);
}
auto thread = concurrency::OSThread::currentThread;
#ifdef ARCH_ESP32
if (thread)
nimbleBluetooth->sendLog(mt_sprintf("%s | [%s] %s", logLevel, thread->ThreadName.c_str(), message).c_str());
else
nimbleBluetooth->sendLog(mt_sprintf("%s | %s", logLevel, message).c_str());
#elif defined(ARCH_NRF52)
if (thread)
nrf52Bluetooth->sendLog(mt_sprintf("%s | [%s] %s", logLevel, thread->ThreadName.c_str(), message).c_str());
else
nrf52Bluetooth->sendLog(mt_sprintf("%s | %s", logLevel, message).c_str());
#endif
delete[] message;
}
}
va_end(arg);
#ifdef HAS_FREE_RTOS
xSemaphoreGive(inDebugPrint);
#else

View File

@@ -75,11 +75,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
// -----------------------------------------------------------------------------
// Regulatory overrides for producing regional builds
// Regulatory overrides
// -----------------------------------------------------------------------------
// Define if region should override user saved region
// #define LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
// Override user saved region, for producing region-locked builds
// #define REGULATORY_LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
// Total system gain in dBm to subtract from Tx power to remain within regulatory ERP limit for non-licensed operators
// This value should be set in variant.h and is PA gain + antenna gain (if system ships with an antenna)
#ifndef REGULATORY_GAIN_LORA
#define REGULATORY_GAIN_LORA 0
#endif
// -----------------------------------------------------------------------------
// Feature toggles

View File

@@ -6,6 +6,7 @@ const ScanI2C::FoundDevice ScanI2C::DEVICE_NONE = ScanI2C::FoundDevice(ScanI2C::
ScanI2C::ScanI2C() = default;
void ScanI2C::scanPort(ScanI2C::I2CPort port) {}
void ScanI2C::scanPort(ScanI2C::I2CPort port, uint8_t *address, uint8_t asize) {}
void ScanI2C::setSuppressScreen()
{

View File

@@ -88,6 +88,7 @@ class ScanI2C
ScanI2C();
virtual void scanPort(ScanI2C::I2CPort);
virtual void scanPort(ScanI2C::I2CPort, uint8_t *, uint8_t);
/*
* A bit of a hack, this tells the scanner not to tell later systems there is a screen to avoid enabling it.

View File

@@ -14,6 +14,15 @@
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34
#endif
bool in_array(uint8_t *array, int size, uint8_t lookfor)
{
int i;
for (i = 0; i < size; i++)
if (lookfor == array[i])
return true;
return false;
}
ScanI2C::FoundDevice ScanI2CTwoWire::find(ScanI2C::DeviceType type) const
{
concurrency::LockGuard guard((concurrency::Lock *)&lock);
@@ -135,11 +144,11 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
type = T; \
break;
void ScanI2CTwoWire::scanPort(I2CPort port)
void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
{
concurrency::LockGuard guard((concurrency::Lock *)&lock);
LOG_DEBUG("Scanning for i2c devices on port %d\n", port);
LOG_DEBUG("Scanning for I2C devices on port %d\n", port);
uint8_t err;
@@ -163,6 +172,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
#endif
for (addr.address = 1; addr.address < 127; addr.address++) {
if (asize != 0) {
if (!in_array(address, asize, addr.address))
continue;
LOG_DEBUG("Scanning address 0x%x\n", addr.address);
}
i2cBus->beginTransmission(addr.address);
#ifdef ARCH_PORTDUINO
if (i2cBus->read() != -1)
@@ -356,7 +370,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
}
} else if (err == 4) {
LOG_ERROR("Unknown error at address 0x%x\n", addr);
LOG_ERROR("Unknown error at address 0x%x\n", addr.address);
}
// Check if a type was found for the enumerated device - save, if so
@@ -367,6 +381,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
}
}
void ScanI2CTwoWire::scanPort(I2CPort port)
{
scanPort(port, nullptr, 0);
}
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
{
if (address.port == ScanI2C::I2CPort::WIRE) {

View File

@@ -16,6 +16,8 @@ class ScanI2CTwoWire : public ScanI2C
public:
void scanPort(ScanI2C::I2CPort) override;
void scanPort(ScanI2C::I2CPort, uint8_t *, uint8_t) override;
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
@@ -53,4 +55,4 @@ class ScanI2CTwoWire : public ScanI2C
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const;
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
};
};

View File

@@ -778,6 +778,22 @@ GPS::~GPS()
notifyGPSSleepObserver.observe(&notifyGPSSleep);
}
const char *GPS::powerStateToString()
{
switch (powerState) {
case GPS_OFF:
return "OFF";
case GPS_IDLE:
return "IDLE";
case GPS_STANDBY:
return "STANDBY";
case GPS_ACTIVE:
return "ACTIVE";
default:
return "UNKNOWN";
}
}
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
{
// Record the current powerState
@@ -792,7 +808,7 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
else
powerState = GPS_OFF;
LOG_DEBUG("GPS::powerState=%d\n", powerState);
LOG_DEBUG("GPS::powerState=%s\n", powerStateToString());
// If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out.
if (!on && powerState == GPS_IDLE)

View File

@@ -289,6 +289,8 @@ class GPS : private concurrency::OSThread
// delay counter to allow more sats before fixed position stops GPS thread
uint8_t fixeddelayCtr = 0;
const char *powerStateToString();
protected:
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
};

View File

@@ -43,6 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "meshUtils.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -59,6 +60,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "platform/portduino/PortduinoGlue.h"
#endif
/// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7)
using namespace meshtastic; /** @todo remove */
namespace graphics
@@ -446,6 +450,37 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
// Determine whether the waypoint frame should be drawn (waypoint deleted? expired?)
static bool shouldDrawWaypoint(const meshtastic_MeshPacket *packet)
{
#if !MESHTASTIC_EXCLUDE_WAYPOINT
// If no waypoint to show
if (!devicestate.has_rx_waypoint)
return false;
// Decode the message, to find the expiration time (is waypoint still valid)
// This handles "deletion" as well as expiration
meshtastic_Waypoint wp;
memset(&wp, 0, sizeof(wp));
if (pb_decode_from_bytes(packet->decoded.payload.bytes, packet->decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
// Valid waypoint
if (wp.expire > getTime())
return devicestate.has_rx_waypoint = true;
// Expired, or deleted
else
return devicestate.has_rx_waypoint = false;
}
// If decoding failed
LOG_ERROR("Failed to decode waypoint\n");
devicestate.has_rx_waypoint = false;
return false;
#else
return false;
#endif
}
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
{
@@ -1091,43 +1126,6 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
#endif
}
/// Draw the last waypoint we received
static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
static char tempBuf[237];
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
}
uint32_t seconds = sinceReceived(&mp);
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
if (config.display.heading_bold) {
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
}
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
display->setColor(WHITE);
meshtastic_Waypoint scratch;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
snprintf(tempBuf, sizeof(tempBuf), "Received waypoint: %s", scratch.name);
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
}
}
/// Draw a series of fields in a column, wrapping to multiple columns if needed
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
@@ -1453,8 +1451,35 @@ static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t com
drawLine(display, N1, N4);
}
/// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7)
// Get a string representation of the time passed since something happened
static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
{
// Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(agoSecs, &timestampHours, &timestampMinutes, &daysAgo);
if (agoSecs < 120) // last 2 mins?
snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
// -- if suitable for timestamp --
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
else if (useTimestamp && daysAgo == 0) // Today
snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
else if (useTimestamp && daysAgo == 1) // Yesterday
snprintf(timeStr, maxLength, "Seen yesterday");
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
// -- if using time delta instead --
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
else
snprintf(timeStr, maxLength, "unknown age");
}
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -1494,34 +1519,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
}
uint32_t agoSecs = sinceLastSeen(node);
static char lastStr[20];
// Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(agoSecs, &timestampHours, &timestampMinutes, &daysAgo);
if (agoSecs < 120) // last 2 mins?
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
// -- if suitable for timestamp --
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
else if (useTimestamp && daysAgo == 0) // Today
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
else if (useTimestamp && daysAgo == 1) // Yesterday
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
// -- if using time delta instead --
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
else
snprintf(lastStr, sizeof(lastStr), "unknown age");
getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
static char distStr[20];
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
@@ -1596,6 +1595,112 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
drawColumns(display, x, y, fields);
}
/// Draw the last waypoint we received
static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Prepare to draw
display->setFont(FONT_SMALL);
display->setTextAlignment(TEXT_ALIGN_LEFT);
// Handle inverted display
// Unsure of expected behavior: for now, copy drawNodeInfo
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
// Decode the waypoint
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
meshtastic_Waypoint wp;
memset(&wp, 0, sizeof(wp));
if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
// This *should* be caught by shouldDrawWaypoint, but we'll short-circuit here just in case
display->drawStringMaxWidth(0 + x, 0 + y, x + display->getWidth(), "Couldn't decode waypoint");
devicestate.has_rx_waypoint = false;
return;
}
// Get timestamp info. Will pass as a field to drawColumns
static char lastStr[20];
getTimeAgoStr(sinceReceived(&mp), lastStr, sizeof(lastStr));
// Will contain distance information, passed as a field to drawColumns
static char distStr[20];
// Get our node, to use our own position
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
// Text fields to draw (left of compass)
// Last element must be NULL. This signals the end of the char*[] to drawColumns
const char *fields[] = {"Waypoint", lastStr, wp.name, distStr, NULL};
// Co-ordinates for the center of the compass/circle
int16_t compassX = 0, compassY = 0;
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
compassY = y + SCREEN_HEIGHT / 2;
} else {
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
}
// If our node has a position:
if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
const meshtastic_PositionLite &op = ourNode->position;
float myHeading;
if (screen->hasHeading())
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
else
myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
drawCompassNorth(display, compassX, compassY, myHeading);
// Distance to Waypoint
float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
if (d < (2 * MILES_TO_FEET))
snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
else
snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET);
} else {
if (d < 2000)
snprintf(distStr, sizeof(distStr), "%.0f m", d);
else
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
}
// Compass bearing to waypoint
float bearingToOther =
GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i));
// If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
if (!config.display.compass_north_top)
bearingToOther -= myHeading;
drawNodeHeading(display, compassX, compassY, bearingToOther);
}
// If our node doesn't have position
else {
// ? in the compass
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
// ? in the distance field
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
strncpy(distStr, "? mi", sizeof(distStr));
else
strncpy(distStr, "? km", sizeof(distStr));
}
// Undo color-inversion, if set prior to drawing header
// Unsure of expected behavior? For now: copy drawNodeInfo
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->setColor(BLACK);
}
// Draw compass circle
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
// Must be after distStr is populated
drawColumns(display, x, y, fields);
}
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
@@ -1806,6 +1911,8 @@ void Screen::setup()
textMessageObserver.observe(textMessageModule);
if (inputBroker)
inputObserver.observe(inputBroker);
if (waypointModule)
waypointObserver.observe(waypointModule);
// Modules can notify screen about refresh
MeshModule::observeUIEvents(&uiFrameEventObserver);
@@ -2133,8 +2240,9 @@ void Screen::setFrames()
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
}
// If we have a waypoint - show it next, unless it's a phone message and we aren't using any special modules
if (devicestate.has_rx_waypoint && shouldDrawMessage(&devicestate.rx_waypoint)) {
// If we have a waypoint (not expired, not deleted)
if (devicestate.has_rx_waypoint && shouldDrawWaypoint(&devicestate.rx_waypoint)) {
normalFrames[numframes++] = drawWaypointFrame;
}
@@ -2736,6 +2844,13 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0;
}
int Screen::handleWaypoint(const meshtastic_MeshPacket *arg)
{
// TODO: move to appropriate frame when redrawing
setFrames();
return 0;
}
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}

View File

@@ -126,6 +126,8 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const meshtastic_MeshPacket *> waypointObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleWaypoint);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
CallbackObserver<Screen, const InputEvent *> inputObserver =
@@ -336,6 +338,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
int handleWaypoint(const meshtastic_MeshPacket *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);

View File

@@ -1,7 +1,7 @@
#include "InputBroker.h"
#include "PowerFSM.h" // needed for event trigger
InputBroker *inputBroker;
InputBroker *inputBroker = nullptr;
InputBroker::InputBroker(){};

View File

@@ -1,5 +1,7 @@
#include "cardKbI2cImpl.h"
#include "InputBroker.h"
#include "detect/ScanI2CTwoWire.h"
#include "main.h"
CardKbI2cImpl *cardKbI2cImpl;
@@ -7,10 +9,52 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
void CardKbI2cImpl::init()
{
#ifndef ARCH_PORTDUINO
if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescanning for I2C keyboard\n");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR};
uint8_t i2caddr_asize = 3;
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if defined(I2C_SDA1)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
#endif
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
auto kb_info = i2cScanner->firstKeyboard();
if (kb_info.type != ScanI2C::DeviceType::NONE) {
cardkb_found = kb_info.address;
switch (kb_info.type) {
case ScanI2C::DeviceType::RAK14004:
kb_model = 0x02;
break;
case ScanI2C::DeviceType::CARDKB:
kb_model = 0x00;
break;
case ScanI2C::DeviceType::TDECKKB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x10;
break;
case ScanI2C::DeviceType::BBQ10KB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x11;
break;
default:
// use this as default since it's also just zero
LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type);
kb_model = 0x00;
}
}
if (cardkb_found.address == 0x00) {
disable();
return;
}
}
#else
if (cardkb_found.address == 0x00) {
disable();
return;
}
#endif
inputBroker->registerSource(this);
}

View File

@@ -1,6 +1,5 @@
#pragma once
#include "kbI2cBase.h"
#include "main.h"
/**
* @brief The idea behind this class to have static methods for the event handlers.

View File

@@ -1,6 +1,7 @@
#include "kbI2cBase.h"
#include "configuration.h"
#include "detect/ScanI2C.h"
#include "detect/ScanI2CTwoWire.h"
extern ScanI2C::DeviceAddress cardkb_found;
extern uint8_t kb_model;
@@ -29,11 +30,6 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len
int32_t KbI2cBase::runOnce()
{
if (cardkb_found.address == 0x00) {
// Input device is not detected.
return INT32_MAX;
}
if (!i2cBus) {
switch (cardkb_found.port) {
case ScanI2C::WIRE1:

View File

@@ -41,13 +41,13 @@
#endif
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "nimble/NimbleBluetooth.h"
NimbleBluetooth *nimbleBluetooth;
NimbleBluetooth *nimbleBluetooth = nullptr;
#endif
#endif
#ifdef ARCH_NRF52
#include "NRF52Bluetooth.h"
NRF52Bluetooth *nrf52Bluetooth;
NRF52Bluetooth *nrf52Bluetooth = nullptr;
#endif
#if HAS_WIFI
@@ -94,23 +94,23 @@ NRF52Bluetooth *nrf52Bluetooth;
#include "ButtonThread.h"
#endif
#include "AmbientLightingThread.h"
#include "PowerFSMThread.h"
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "AccelerometerThread.h"
#include "AmbientLightingThread.h"
AccelerometerThread *accelerometerThread;
AccelerometerThread *accelerometerThread = nullptr;
#endif
#ifdef HAS_I2S
#include "AudioThread.h"
AudioThread *audioThread;
AudioThread *audioThread = nullptr;
#endif
using namespace concurrency;
// We always create a screen object, but we only init it if we find the hardware
graphics::Screen *screen;
graphics::Screen *screen = nullptr;
// Global power status
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
@@ -154,6 +154,7 @@ bool isVibrating = false;
bool eink_found = true;
uint32_t serialSinceMsec;
bool pauseBluetoothLogging = false;
bool pmu_found;
@@ -172,7 +173,7 @@ const char *getDeviceName()
static char name[20];
snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]);
// if the shortname exists and is NOT the new default of ab3c, use it for BLE name.
if ((owner.short_name != NULL) && (strcmp(owner.short_name, name) != 0)) {
if (strcmp(owner.short_name, name) != 0) {
snprintf(name, sizeof(name), "%s_%02x%02x", owner.short_name, dmac[4], dmac[5]);
} else {
snprintf(name, sizeof(name), "Meshtastic_%02x%02x", dmac[4], dmac[5]);
@@ -241,7 +242,7 @@ void setup()
initDeepSleep();
// power on peripherals
#if defined(TTGO_T_ECHO) && defined(PIN_POWER_EN)
#if defined(PIN_POWER_EN)
pinMode(PIN_POWER_EN, OUTPUT);
digitalWrite(PIN_POWER_EN, HIGH);
// digitalWrite(PIN_POWER_EN1, INPUT);
@@ -421,10 +422,6 @@ void setup()
auto i2cCount = i2cScanner->countDevices();
if (i2cCount == 0) {
LOG_INFO("No I2C devices found\n");
Wire.end();
#ifdef I2C_SDA1
Wire1.end();
#endif
} else {
LOG_INFO("%i I2C devices found\n", i2cCount);
}

View File

@@ -85,6 +85,8 @@ extern uint32_t serialSinceMsec;
// This will suppress the current delay and instead try to run ASAP.
extern bool runASAP;
extern bool pauseBluetoothLogging;
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode();
meshtastic_DeviceMetadata getDeviceMetadata();

View File

@@ -20,7 +20,7 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p)
bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg, because we've already seen it", p);
printPacket("Ignoring incoming msg we've already seen", p);
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {

View File

@@ -373,8 +373,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
pos.time = getValidTime(RTCQualityFromNet);
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
LOG_DEBUG("onGPSChanged() pos@%x, time=%u, lat=%d, lon=%d, alt=%d\n", pos.timestamp, pos.time, pos.latitude_i,
pos.longitude_i, pos.altitude);
LOG_DEBUG("onGPSChanged() pos@%x time=%u lat=%d lon=%d alt=%d\n", pos.timestamp, pos.time, pos.latitude_i, pos.longitude_i,
pos.altitude);
// Update our current position in the local DB
nodeDB->updatePosition(nodeDB->getNodeNum(), pos, RX_SRC_LOCAL);

View File

@@ -141,11 +141,6 @@ NodeDB::NodeDB()
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
saveWhat |= SEGMENT_CHANNELS;
if (!devicestate.node_remote_hardware_pins) {
meshtastic_NodeRemoteHardwarePin empty[12] = {meshtastic_RemoteHardwarePin_init_default};
memcpy(devicestate.node_remote_hardware_pins, empty, sizeof(empty));
}
if (config.position.gps_enabled) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
config.position.gps_enabled = 0;
@@ -826,8 +821,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
if (src == RX_SRC_LOCAL) {
// Local packet, fully authoritative
LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
p.longitude_i, p.altitude);
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d\n", p.timestamp, p.time, p.latitude_i, p.longitude_i,
p.altitude);
setLocalPosition(p);
info->position = TypeConversions::ConvertToPositionLite(p);
@@ -842,7 +837,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
// recorded based on the packet rxTime
//
// FIXME perhaps handle RX_SRC_USER separately?
LOG_INFO("updatePosition REMOTE node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
// First, back up fields that we want to protect from overwrite
uint32_t tmp_time = info->position.time;

View File

@@ -155,8 +155,8 @@ class NodeDB
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
return;
}
LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%u, timestamp=%u\n", position.latitude_i,
position.longitude_i, position.time, position.timestamp);
LOG_DEBUG("Setting local position: lat=%i lon=%i time=%u timestamp=%u\n", position.latitude_i, position.longitude_i,
position.time, position.timestamp);
localPosition = position;
}

View File

@@ -46,6 +46,7 @@ void PhoneAPI::handleStartConfig()
// even if we were already connected - restart our state machine
state = STATE_SEND_MY_INFO;
pauseBluetoothLogging = true;
LOG_INFO("Starting API client config\n");
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
@@ -352,9 +353,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.config_complete_id = config_nonce;
config_nonce = 0;
state = STATE_SEND_PACKETS;
pauseBluetoothLogging = false;
break;
case STATE_SEND_PACKETS:
pauseBluetoothLogging = false;
// Do we have a message from the mesh or packet from the local device?
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
if (queueStatusPacketForPhone) {
@@ -398,6 +401,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
void PhoneAPI::handleDisconnect()
{
pauseBluetoothLogging = false;
LOG_INFO("PhoneAPI disconnect\n");
}

View File

@@ -98,8 +98,6 @@ class PhoneAPI
bool isConnected() { return state != STATE_SEND_NOTHING; }
void setInitialState() { state = STATE_SEND_MY_INFO; }
protected:
/// Our fromradio packet while it is being assembled
meshtastic_FromRadio fromRadioScratch = {};

View File

@@ -154,8 +154,8 @@ static uint8_t bytes[MAX_RHPACKETLEN];
void initRegion()
{
const RegionInfo *r = regions;
#ifdef LORA_REGIONCODE
for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != LORA_REGIONCODE; r++)
#ifdef REGULATORY_LORA_REGIONCODE
for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != REGULATORY_LORA_REGIONCODE; r++)
;
LOG_INFO("Wanted region %d, regulatory override to %s\n", config.lora.region, r->name);
#else
@@ -478,8 +478,8 @@ void RadioInterface::applyModemConfig()
power = loraConfig.tx_power;
if ((power == 0) || ((power > myRegion->powerLimit) && !devicestate.owner.is_licensed))
power = myRegion->powerLimit;
if ((power == 0) || ((power + REGULATORY_GAIN_LORA > myRegion->powerLimit) && !devicestate.owner.is_licensed))
power = myRegion->powerLimit - REGULATORY_GAIN_LORA;
if (power == 0)
power = 17; // Default to this power level if we don't have a valid regional power limit (powerLimit of myRegion defaults

View File

@@ -372,6 +372,9 @@ typedef struct _meshtastic_Config_PowerConfig {
uint32_t min_wake_secs;
/* I2C address of INA_2XX to use for reading device battery voltage */
uint8_t device_battery_ina_address;
/* If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled.
Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. */
uint64_t powermon_enables;
} meshtastic_Config_PowerConfig;
typedef struct _meshtastic_Config_NetworkConfig_IpV4Config {
@@ -511,6 +514,8 @@ typedef struct _meshtastic_Config_BluetoothConfig {
meshtastic_Config_BluetoothConfig_PairingMode mode;
/* Specified PIN for PairingMode.FixedPin */
uint32_t fixed_pin;
/* Enables device (serial style logs) over Bluetooth */
bool device_logging_enabled;
} meshtastic_Config_BluetoothConfig;
typedef struct _meshtastic_Config {
@@ -610,21 +615,21 @@ extern "C" {
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_Config_DeviceConfig_role_tag 1
@@ -660,6 +665,7 @@ extern "C" {
#define meshtastic_Config_PowerConfig_ls_secs_tag 7
#define meshtastic_Config_PowerConfig_min_wake_secs_tag 8
#define meshtastic_Config_PowerConfig_device_battery_ina_address_tag 9
#define meshtastic_Config_PowerConfig_powermon_enables_tag 32
#define meshtastic_Config_NetworkConfig_IpV4Config_ip_tag 1
#define meshtastic_Config_NetworkConfig_IpV4Config_gateway_tag 2
#define meshtastic_Config_NetworkConfig_IpV4Config_subnet_tag 3
@@ -702,6 +708,7 @@ extern "C" {
#define meshtastic_Config_BluetoothConfig_enabled_tag 1
#define meshtastic_Config_BluetoothConfig_mode_tag 2
#define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3
#define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4
#define meshtastic_Config_device_tag 1
#define meshtastic_Config_position_tag 2
#define meshtastic_Config_power_tag 3
@@ -770,7 +777,8 @@ X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \
X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \
X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9)
X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9) \
X(a, STATIC, SINGULAR, UINT64, powermon_enables, 32)
#define meshtastic_Config_PowerConfig_CALLBACK NULL
#define meshtastic_Config_PowerConfig_DEFAULT NULL
@@ -833,7 +841,8 @@ X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
#define meshtastic_Config_BluetoothConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, UENUM, mode, 2) \
X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3)
X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) \
X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4)
#define meshtastic_Config_BluetoothConfig_CALLBACK NULL
#define meshtastic_Config_BluetoothConfig_DEFAULT NULL
@@ -860,14 +869,14 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
#define meshtastic_Config_BluetoothConfig_size 10
#define meshtastic_Config_BluetoothConfig_size 12
#define meshtastic_Config_DeviceConfig_size 100
#define meshtastic_Config_DisplayConfig_size 30
#define meshtastic_Config_LoRaConfig_size 80
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_PositionConfig_size 62
#define meshtastic_Config_PowerConfig_size 40
#define meshtastic_Config_PowerConfig_size 52
#define meshtastic_Config_size 199
#ifdef __cplusplus

View File

@@ -308,7 +308,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
#define meshtastic_ChannelFile_size 718
#define meshtastic_NodeInfoLite_size 166
#define meshtastic_OEMStore_size 3370
#define meshtastic_OEMStore_size 3384
#define meshtastic_PositionLite_size 28
#ifdef __cplusplus

View File

@@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 539
#define meshtastic_LocalConfig_size 553
#define meshtastic_LocalModuleConfig_size 685
#ifdef __cplusplus

View File

@@ -69,6 +69,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_WIO_WM1110 = 21,
/* RAK2560 Solar base station based on RAK4630 */
meshtastic_HardwareModel_RAK2560 = 22,
/* Heltec HRU-3601: https://heltec.org/project/hru-3601/ */
meshtastic_HardwareModel_HELTEC_HRU_3601 = 23,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */

View File

@@ -0,0 +1,13 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.8 */
#include "meshtastic/powermon.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)

View File

@@ -0,0 +1,85 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.8 */
#ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
typedef enum _meshtastic_PowerMon_State {
meshtastic_PowerMon_State_None = 0,
meshtastic_PowerMon_State_CPU_DeepSleep = 1,
meshtastic_PowerMon_State_CPU_LightSleep = 2,
/* The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only
occasionally. In cases where that rail has multiple devices on it we usually want to have logging on
the state of that rail as an independent record.
For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen.
The log messages will be short and complete (see PowerMon.Event in the protobufs for details).
something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states.
(We use a bitmask for states so that if a log message gets lost it won't be fatal) */
meshtastic_PowerMon_State_Vext1_On = 4,
meshtastic_PowerMon_State_Lora_RXOn = 8,
meshtastic_PowerMon_State_Lora_TXOn = 16,
meshtastic_PowerMon_State_Lora_RXActive = 32,
meshtastic_PowerMon_State_BT_On = 64,
meshtastic_PowerMon_State_LED_On = 128,
meshtastic_PowerMon_State_Screen_On = 256,
meshtastic_PowerMon_State_Screen_Drawing = 512,
meshtastic_PowerMon_State_Wifi_On = 1024,
/* GPS is actively trying to find our location
See GPSPowerState for more details */
meshtastic_PowerMon_State_GPS_Active = 2048
} meshtastic_PowerMon_State;
/* Struct definitions */
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
typedef struct _meshtastic_PowerMon {
char dummy_field;
} meshtastic_PowerMon;
#ifdef __cplusplus
extern "C" {
#endif
/* Helper constants for enums */
#define _meshtastic_PowerMon_State_MIN meshtastic_PowerMon_State_None
#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
/* Initializer values for message structs */
#define meshtastic_PowerMon_init_default {0}
#define meshtastic_PowerMon_init_zero {0}
/* Field tags (for use in manual encoding/decoding) */
/* Struct field encoding specification for nanopb */
#define meshtastic_PowerMon_FIELDLIST(X, a) \
#define meshtastic_PowerMon_CALLBACK NULL
#define meshtastic_PowerMon_DEFAULT NULL
extern const pb_msgdesc_t meshtastic_PowerMon_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerMon_size
#define meshtastic_PowerMon_size 0
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -299,8 +299,8 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp,
{
// Skip if it's disabled or no pins are exposed
if (!r->get_module_config_response.payload_variant.remote_hardware.enabled ||
!r->get_module_config_response.payload_variant.remote_hardware.available_pins) {
LOG_DEBUG("Remote hardware module disabled or no vailable_pins. Skipping...\n");
r->get_module_config_response.payload_variant.remote_hardware.available_pins_count == 0) {
LOG_DEBUG("Remote hardware module disabled or no available_pins. Skipping...\n");
return;
}
for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) {

View File

@@ -42,6 +42,7 @@
#include "modules/Telemetry/DeviceTelemetry.h"
#endif
#if HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "main.h"
#include "modules/Telemetry/AirQualityTelemetry.h"
#include "modules/Telemetry/EnvironmentTelemetry.h"
#endif

View File

@@ -73,7 +73,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
}
// Log packet size and data fields
LOG_DEBUG("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
"time=%d\n",
getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae,
p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp,
@@ -219,7 +219,7 @@ meshtastic_MeshPacket *PositionModule::allocReply()
LOG_INFO("Providing time to mesh %u\n", p.time);
}
LOG_INFO("Position reply: time=%i, latI=%i, lonI=%i\n", p.time, p.latitude_i, p.longitude_i);
LOG_INFO("Position reply: time=%i lat=%i lon=%i\n", p.time, p.latitude_i, p.longitude_i);
// 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)

View File

@@ -85,53 +85,90 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
return false; // Let others look at this message also if they want
}
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
{
if (!aqi.read(&data)) {
LOG_WARN("Skipping send measurements. Could not read AQIn\n");
return false;
}
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
m.variant.air_quality_metrics.pm25_standard = data.pm25_standard;
m.variant.air_quality_metrics.pm100_standard = data.pm100_standard;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
m.variant.air_quality_metrics.pm10_environmental = data.pm10_env;
m.variant.air_quality_metrics.pm25_environmental = data.pm25_env;
m.variant.air_quality_metrics.pm100_environmental = data.pm100_env;
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
m.variant.air_quality_metrics.pm100_standard);
m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard,
m->variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental,
m.variant.air_quality_metrics.pm100_environmental);
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental,
m->variant.air_quality_metrics.pm100_environmental);
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Sending packet to phone\n");
service.sendToPhone(p);
} else {
LOG_INFO("Sending packet to mesh\n");
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding AirQualityTelemetry module!\n");
return NULL;
}
// Check for a request for air quality metrics
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
LOG_INFO("Air quality telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Sending packet to phone\n");
service.sendToPhone(p);
} else {
LOG_INFO("Sending packet to mesh\n");
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
return false;
}
#endif

View File

@@ -26,6 +26,11 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Air Quality data
@return true if it contains valid data
*/
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/

View File

@@ -52,14 +52,27 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
{
if (ignoreRequest) {
return NULL;
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding DeviceTelemetry module!\n");
return NULL;
}
// Check for a request for device metrics
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
LOG_INFO("Device telemetry replying to request\n");
meshtastic_Telemetry telemetry = getDeviceTelemetry();
return allocDataProtobuf(telemetry);
}
}
LOG_INFO("Device telemetry replying to request\n");
meshtastic_Telemetry telemetry = getDeviceTelemetry();
return allocDataProtobuf(telemetry);
return NULL;
}
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
@@ -104,4 +117,4 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
}

View File

@@ -270,98 +270,129 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
return false; // Let others look at this message also if they want
}
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = true;
bool hasSensor = false;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
if (dfRobotLarkSensor.hasSensor()) {
valid = valid && dfRobotLarkSensor.getMetrics(&m);
valid = valid && dfRobotLarkSensor.getMetrics(m);
hasSensor = true;
}
if (sht31Sensor.hasSensor()) {
valid = valid && sht31Sensor.getMetrics(&m);
valid = valid && sht31Sensor.getMetrics(m);
hasSensor = true;
}
if (lps22hbSensor.hasSensor()) {
valid = valid && lps22hbSensor.getMetrics(&m);
valid = valid && lps22hbSensor.getMetrics(m);
hasSensor = true;
}
if (shtc3Sensor.hasSensor()) {
valid = valid && shtc3Sensor.getMetrics(&m);
valid = valid && shtc3Sensor.getMetrics(m);
hasSensor = true;
}
if (bmp085Sensor.hasSensor()) {
valid = valid && bmp085Sensor.getMetrics(&m);
valid = valid && bmp085Sensor.getMetrics(m);
hasSensor = true;
}
if (bmp280Sensor.hasSensor()) {
valid = valid && bmp280Sensor.getMetrics(&m);
valid = valid && bmp280Sensor.getMetrics(m);
hasSensor = true;
}
if (bme280Sensor.hasSensor()) {
valid = valid && bme280Sensor.getMetrics(&m);
valid = valid && bme280Sensor.getMetrics(m);
hasSensor = true;
}
if (bme680Sensor.hasSensor()) {
valid = valid && bme680Sensor.getMetrics(&m);
valid = valid && bme680Sensor.getMetrics(m);
hasSensor = true;
}
if (mcp9808Sensor.hasSensor()) {
valid = valid && mcp9808Sensor.getMetrics(&m);
valid = valid && mcp9808Sensor.getMetrics(m);
hasSensor = true;
}
if (ina219Sensor.hasSensor()) {
valid = valid && ina219Sensor.getMetrics(&m);
valid = valid && ina219Sensor.getMetrics(m);
hasSensor = true;
}
if (ina260Sensor.hasSensor()) {
valid = valid && ina260Sensor.getMetrics(&m);
valid = valid && ina260Sensor.getMetrics(m);
hasSensor = true;
}
if (veml7700Sensor.hasSensor()) {
valid = valid && veml7700Sensor.getMetrics(&m);
valid = valid && veml7700Sensor.getMetrics(m);
hasSensor = true;
}
if (tsl2591Sensor.hasSensor()) {
valid = valid && tsl2591Sensor.getMetrics(&m);
valid = valid && tsl2591Sensor.getMetrics(m);
hasSensor = true;
}
if (opt3001Sensor.hasSensor()) {
valid = valid && opt3001Sensor.getMetrics(&m);
valid = valid && opt3001Sensor.getMetrics(m);
hasSensor = true;
}
if (mlx90632Sensor.hasSensor()) {
valid = valid && mlx90632Sensor.getMetrics(&m);
valid = valid && mlx90632Sensor.getMetrics(m);
hasSensor = true;
}
if (rcwl9620Sensor.hasSensor()) {
valid = valid && rcwl9620Sensor.getMetrics(&m);
valid = valid && rcwl9620Sensor.getMetrics(m);
hasSensor = true;
}
if (nau7802Sensor.hasSensor()) {
valid = valid && nau7802Sensor.getMetrics(&m);
valid = valid && nau7802Sensor.getMetrics(m);
hasSensor = true;
}
if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(&m);
valid = valid && aht10Sensor.getMetrics(m);
hasSensor = true;
} else {
// prefer bmp280 temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
aht10Sensor.getMetrics(&m_ahtx);
m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
}
}
valid = valid && hasSensor;
return valid && hasSensor;
}
if (valid) {
meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding EnvironmentTelemetry module!\n");
return NULL;
}
// Check for a request for environment metrics
if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getEnvironmentTelemetry(&m)) {
LOG_INFO("Environment telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getEnvironmentTelemetry(&m)) {
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
@@ -399,8 +430,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000);
}
}
return true;
}
return valid;
return false;
}
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,

View File

@@ -32,6 +32,11 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Environment telemetry data
@return true if it contains valid data
*/
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/

View File

@@ -163,29 +163,63 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
return false; // Let others look at this message also if they want
}
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
{
bool valid = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
m->variant.power_metrics.ch1_voltage = 0;
m->variant.power_metrics.ch1_current = 0;
m->variant.power_metrics.ch2_voltage = 0;
m->variant.power_metrics.ch2_current = 0;
m->variant.power_metrics.ch3_voltage = 0;
m->variant.power_metrics.ch3_current = 0;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(m);
#endif
return valid;
}
meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding PowerTelemetry module!\n");
return NULL;
}
// Check for a request for power metrics
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getPowerTelemetry(&m)) {
LOG_INFO("Power telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = false;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
m.variant.power_metrics.ch1_voltage = 0;
m.variant.power_metrics.ch1_current = 0;
m.variant.power_metrics.ch2_voltage = 0;
m.variant.power_metrics.ch2_current = 0;
m.variant.power_metrics.ch3_voltage = 0;
m.variant.power_metrics.ch3_current = 0;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(&m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(&m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(&m);
#endif
if (valid) {
if (getPowerTelemetry(&m)) {
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f\n",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
@@ -218,8 +252,9 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000);
}
}
return true;
}
return valid;
return false;
}
#endif

View File

@@ -33,6 +33,11 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Power telemetry data
@return true if it contains valid data
*/
bool getPowerTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/

View File

@@ -16,8 +16,7 @@ int32_t INA3221Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
ina3221.begin();
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {

View File

@@ -1,7 +1,10 @@
#include "OPT3001Sensor.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "OPT3001Sensor.h"
#include "TelemetrySensor.h"
#include <ClosedCube_OPT3001.h>
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
@@ -41,4 +44,6 @@ bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux);
return true;
}
}
#endif

View File

@@ -1,3 +1,8 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <ClosedCube_OPT3001.h>
@@ -14,4 +19,6 @@ class OPT3001Sensor : public TelemetrySensor
OPT3001Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
};
#endif

View File

@@ -66,10 +66,6 @@ bool PaxcounterModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m
meshtastic_MeshPacket *PaxcounterModule::allocReply()
{
if (ignoreRequest) {
return NULL;
}
meshtastic_Paxcount pl = meshtastic_Paxcount_init_default;
pl.wifi = count_from_libpax.wifi_count;
pl.ble = count_from_libpax.ble_count;
@@ -131,4 +127,4 @@ void PaxcounterModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
}
#endif // HAS_SCREEN
#endif
#endif

View File

@@ -482,7 +482,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
auto &ch = channels.getByIndex(chIndex);
if (&mp_decoded.decoded && strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n");
return;
}
if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
(mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) {
LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n");

View File

@@ -12,6 +12,7 @@
NimBLECharacteristic *fromNumCharacteristic;
NimBLECharacteristic *BatteryCharacteristic;
NimBLECharacteristic *logRadioCharacteristic;
NimBLEServer *bleServer;
static bool passkeyShowing;
@@ -58,7 +59,6 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
{
virtual void onRead(NimBLECharacteristic *pCharacteristic)
{
LOG_INFO("From Radio onread\n");
uint8_t fromRadioBytes[meshtastic_FromRadio_size];
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
@@ -180,6 +180,8 @@ void NimbleBluetooth::setupService()
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
logRadioCharacteristic =
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
} else {
ToRadioCharacteristic = bleService->createCharacteristic(
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
@@ -188,6 +190,10 @@ void NimbleBluetooth::setupService()
fromNumCharacteristic =
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
logRadioCharacteristic = bleService->createCharacteristic(
LOGRADIO_UUID,
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
logRadioCharacteristic->setValue("Init");
}
bluetoothPhoneAPI = new BluetoothPhoneAPI();
@@ -236,6 +242,14 @@ void NimbleBluetooth::clearBonds()
NimBLEDevice::deleteAllBonds();
}
void NimbleBluetooth::sendLog(const char *logMessage)
{
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
return;
}
logRadioCharacteristic->notify(reinterpret_cast<const uint8_t *>(logMessage), strlen(logMessage), true);
}
void clearNVS()
{
NimBLEDevice::deleteAllBonds();

View File

@@ -11,6 +11,7 @@ class NimbleBluetooth : BluetoothApi
bool isActive();
bool isConnected();
int getRssi();
void sendLog(const char *logMessage);
private:
void setupService();

View File

@@ -101,6 +101,8 @@
#define HW_VENDOR meshtastic_HardwareModel_STATION_G1
#elif defined(DR_DEV)
#define HW_VENDOR meshtastic_HardwareModel_DR_DEV
#elif defined(HELTEC_HRU_3601)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HRU_3601
#elif defined(HELTEC_V3)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V3
#elif defined(HELTEC_WSL_V3)

View File

@@ -7,16 +7,16 @@
#include "mesh/mesh-pb-constants.h"
#include <bluefruit.h>
#include <utility/bonding.h>
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
static BLECharacteristic logRadio = BLECharacteristic(BLEUuid(LOGRADIO_UUID_16));
static BLEDis bledis; // DIS (Device Information Service) helper class instance
static BLEBas blebas; // BAS (Battery Service) helper class instance
static BLEDfu bledfu; // DFU software update helper service
static BLEDfu bledfu; // DFU software update helper service
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
// process at once
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
@@ -50,16 +50,14 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
void onConnect(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection *connection = Bluefruit.Connection(conn_handle);
connectionHandle = conn_handle;
char central_name[32] = {0};
connection->getPeerName(central_name, sizeof(central_name));
LOG_INFO("BLE Connected to %s\n", central_name);
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
@@ -70,15 +68,13 @@ void onDisconnect(uint16_t conn_handle, uint8_t reason)
// FIXME - we currently assume only one active connection
LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason);
}
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
{
// Display the raw request packet
LOG_INFO("CCCD Updated: %u\n", cccd_value);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr->uuid == fromNum.uuid) {
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
if (chr->notifyEnabled(conn_hdl)) {
LOG_INFO("fromNum 'Notify' enabled\n");
} else {
@@ -86,21 +82,17 @@ void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
}
}
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
// IncludeService UUID
// Bluefruit.ScanResponse.addService(meshBleService);
Bluefruit.ScanResponse.addTxPower();
Bluefruit.ScanResponse.addName();
// Include Name
// Bluefruit.Advertising.addName();
Bluefruit.Advertising.addService(meshBleService);
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
@@ -115,7 +107,6 @@ void startAdv(void)
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
}
// Just ack that the caller is allowed to read
static void authorizeRead(uint16_t conn_hdl)
{
@@ -123,7 +114,6 @@ static void authorizeRead(uint16_t conn_hdl)
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
}
/**
* client is starting read, pull the bytes from our API class
*/
@@ -132,7 +122,6 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
if (request->offset == 0) {
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
// or make empty if the queue is empty
fromRadio.write(fromRadioBytes, numBytes);
@@ -141,37 +130,22 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
}
authorizeRead(conn_hdl);
}
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
bluetoothPhoneAPI->handleToRadio(data, len);
}
/**
* client is starting read, pull the bytes from our API class
*/
void onFromNumAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
{
LOG_INFO("fromNumAuthorizeCb\n");
authorizeRead(conn_hdl);
}
void setupMeshService(void)
{
bluetoothPhoneAPI = new BluetoothPhoneAPI();
meshBleService.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.. Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was 'begin()'ed!
auto secMode =
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
fromNum.setFixedLen(
@@ -201,10 +175,15 @@ void setupMeshService(void)
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
toRadio.setWriteCallback(onToRadioWrite, false);
toRadio.begin();
logRadio.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
logRadio.setMaxLen(512);
logRadio.setCccdWriteCallback(onCccd);
logRadio.write32(0);
logRadio.begin();
}
static uint32_t configuredPasskey;
void NRF52Bluetooth::shutdown()
{
// Shutdown bluetooth for minimum power draw
@@ -214,29 +193,23 @@ void NRF52Bluetooth::shutdown()
}
Bluefruit.Advertising.stop();
}
void NRF52Bluetooth::startDisabled()
{
// Setup Bluetooth
nrf52Bluetooth->setup();
// Shutdown bluetooth for minimum power draw
Bluefruit.Advertising.stop();
Bluefruit.setTxPower(-40); // Minimum power
LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n");
}
bool NRF52Bluetooth::isConnected()
{
return Bluefruit.connected(connectionHandle);
}
int NRF52Bluetooth::getRssi()
{
return 0; // FIXME figure out where to source this
}
void NRF52Bluetooth::setup()
{
// Initialise the Bluefruit module
@@ -244,12 +217,10 @@ void NRF52Bluetooth::setup()
Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
// Clear existing data.
Bluefruit.Advertising.stop();
Bluefruit.Advertising.clearData();
Bluefruit.ScanResponse.clearData();
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
? config.bluetooth.fixed_pin
@@ -268,37 +239,29 @@ void NRF52Bluetooth::setup()
}
// Set the advertised device name (keep it short!)
Bluefruit.setName(getDeviceName());
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(onConnect);
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
bledfu.begin(); // Install the DFU helper
// Configure and Start the Device Information Service
LOG_INFO("Configuring the Device Information Service\n");
bledis.setModel(optstr(HW_VERSION));
bledis.setFirmwareRev(optstr(APP_VERSION));
bledis.begin();
// Start the BLE Battery Service and set it to 100%
LOG_INFO("Configuring the Battery Service\n");
blebas.begin();
blebas.write(0); // Unknown battery level for now
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
LOG_INFO("Configuring the Mesh bluetooth service\n");
setupMeshService();
// Setup the advertising packet(s)
LOG_INFO("Setting up the advertising payload(s)\n");
startAdv();
LOG_INFO("Advertising\n");
}
void NRF52Bluetooth::resumeAdvertising()
{
Bluefruit.Advertising.restartOnDisconnect(true);
@@ -306,34 +269,28 @@ void NRF52Bluetooth::resumeAdvertising()
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0);
}
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level)
{
blebas.write(level);
}
void NRF52Bluetooth::clearBonds()
{
LOG_INFO("Clearing bluetooth bonds!\n");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
}
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
{
LOG_INFO("BLE connection secured\n");
}
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
{
LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
screen->startBluetoothPinScreen(configuredPasskey);
if (match_request) {
uint32_t start_time = millis();
while (millis() < start_time + 30000) {
@@ -344,13 +301,18 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request);
return true;
}
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
{
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
LOG_INFO("BLE pairing success\n");
else
LOG_INFO("BLE pairing failed\n");
screen->stopBluetoothPinScreen();
}
void NRF52Bluetooth::sendLog(const char *logMessage)
{
if (!isConnected() || strlen(logMessage) > 512)
return;
logRadio.notify(logMessage);
}

View File

@@ -13,10 +13,10 @@ class NRF52Bluetooth : BluetoothApi
void clearBonds();
bool isConnected();
int getRssi();
void sendLog(const char *logMessage);
private:
static void onConnectionSecured(uint16_t conn_handle);
void convertToUint8(uint8_t target[4], uint32_t source);
static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status);
};

View File

@@ -231,12 +231,10 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
nodeDB->saveToDisk();
#ifdef TTGO_T_ECHO
#ifdef PIN_POWER_EN
pinMode(PIN_POWER_EN, INPUT); // power off peripherals
// pinMode(PIN_POWER_EN1, INPUT_PULLDOWN);
#endif
#endif
#if HAS_GPS
// Kill GPS power completely (even if previously we just had it in sleep mode)
if (gps)

View File

@@ -0,0 +1,25 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#include <variant.h>
static const uint8_t TX = UART_TX;
static const uint8_t RX = UART_RX;
static const uint8_t SDA = I2C_SDA;
static const uint8_t SCL = I2C_SCL;
static const uint8_t SS = 8;
static const uint8_t MOSI = 7;
static const uint8_t MISO = 6;
static const uint8_t SCK = 10;
static const uint8_t A0 = 0;
static const uint8_t A1 = 1;
static const uint8_t A2 = 2;
static const uint8_t A3 = 3;
static const uint8_t A4 = 4;
static const uint8_t A5 = 5;
#endif /* Pins_Arduino_h */

View File

@@ -0,0 +1,9 @@
[env:heltec-hru-3601]
extends = esp32c3_base
board = adafruit_qtpy_esp32c3
build_flags =
${esp32_base.build_flags}
-D HELTEC_HRU_3601
-I variants/heltec_hru_3601
lib_deps = ${esp32c3_base.lib_deps}
adafruit/Adafruit NeoPixel @ ^1.12.0

View File

@@ -0,0 +1,40 @@
#define BUTTON_PIN 9
#define HAS_SCREEN 0
#define HAS_GPS 0
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define USE_SX1262
#define LORA_SCK 10
#define LORA_MISO 6
#define LORA_MOSI 7
#define LORA_CS 8
#define LORA_DIO0 RADIOLIB_NC
#define LORA_RESET 5
#define LORA_DIO1 3
#define LORA_DIO2 RADIOLIB_NC
#define LORA_BUSY 4
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_BUSY
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Vext_Ctrl pin controls 3.3V LDO (U2) which provides power to I2C peripheral
#define PIN_POWER_EN 1
// Board has I2C connected to UART0 pins, and no other hardware serial port
#define UART_TX -1
#define UART_RX -1
// Board has I2C connected to U0RXD and U0TXD
#define I2C_SDA 21
#define I2C_SCL 20
// Board has RGB LED on GPIO2
#define HAS_NEOPIXEL // Enable the use of neopixels
#define NEOPIXEL_COUNT 1 // How many neopixels are connected
#define NEOPIXEL_DATA 2 // gpio pin used to send data to the neopixels
#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use

View File

@@ -1,7 +1,7 @@
[env:heltec-wireless-tracker]
extends = esp32s3_base
board = heltec_wireless_tracker
upload_protocol = esp-builtin
upload_protocol = esptool
build_flags =
${esp32s3_base.build_flags} -I variants/heltec_wireless_tracker
@@ -11,4 +11,4 @@ build_flags =
lib_deps =
${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@^1.1.8
lovyan03/LovyanGFX@^1.1.8

View File

@@ -2,7 +2,7 @@
extends = esp32s3_base
board = tlora-t3s3-v1
board_check = true
upload_protocol = esp-builtin
upload_protocol = esptool
build_flags =
${esp32_base.build_flags} -D TLORA_T3S3_V1 -I variants/tlora_t3s3_v1

View File

@@ -2,6 +2,10 @@
[env:wio-sdk-wm1110]
extends = nrf52840_base
board = wio-sdk-wm1110
# Remove adafruit USB serial from the build (it is incompatible with using the ch340 serial chip on this board)
build_unflags = ${nrf52840_base:build_unflags} -DUSBCON -DUSE_TINYUSB
board_level = extra
; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e
build_flags = ${nrf52840_base.build_flags} -Ivariants/wio-sdk-wm1110 -DWIO_WM1110
@@ -12,4 +16,4 @@ 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
upload_protocol = jlink

View File

@@ -31,6 +31,13 @@
#include "WVariant.h"
#ifdef USE_TINYUSB
#error TinyUSB must be disabled by platformio before using this variant
#endif
// We use the hardware serial port for the serial console
#define Serial Serial1
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

View File

@@ -3,7 +3,7 @@
extends = nrf52840_base
board = xiao_ble_sense
board_level = extra
build_flags = ${nrf52840_base.build_flags} -Ivariants/xiao_ble -Ivariants/xiao_ble/softdevice -Ivariants/xiao_ble/softdevice/nrf52 -D EBYTE_E22 -DPRIVATE_HW
build_flags = ${nrf52840_base.build_flags} -Ivariants/xiao_ble -Ivariants/xiao_ble/softdevice -Ivariants/xiao_ble/softdevice/nrf52 -D EBYTE_E22 -DEBYTE_E22_900M30S -DPRIVATE_HW
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
board_build.ldscript = variants/xiao_ble/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/xiao_ble>

View File

@@ -142,6 +142,16 @@ static const uint8_t SCK = PIN_SPI_SCK;
// (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 PA output table from Ebyte Robin <sales06@ebyte.com>
#define REGULATORY_GAIN_LORA 10
#define SX126X_MAX_POWER 20
#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 REGULATORY_GAIN_LORA 25
#define SX126X_MAX_POWER 8
#endif
#endif
/*

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 3
build = 13
build = 14