Compare commits

..

22 Commits

Author SHA1 Message Date
github-actions[bot]
6485f037ec [create-pull-request] automated change (#5133)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-10-23 21:21:49 -05:00
Ben Meadors
ca5f71f774 Add device unique id (#5092)
* Add device unique id

* Trunk

* WIP

* Esp32 implementation

* Trunk

* Check for ESP_EFUSE_OPTIONAL_UNIQUE_ID

* Comment print

* Trunk
2024-10-23 21:18:37 -05:00
panaceya
57667f1028 ADD: Configurable UPLINK_ENABLED and DOWNLINK_ENABLED in userPrefs.h (#5120) 2024-10-23 05:26:44 -05:00
Craig Bailey
3f1c86f953 Update meshtasticd.service (#5118)
Adding restart on service failure with 3 seconds between restart to stop fasst restart loops. Adding StartLimitBurst to limit it to 5 restarts in 200 seconds.
2024-10-22 16:22:10 -05:00
github-actions[bot]
4416ac57cf [create-pull-request] automated change (#5124)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-10-21 16:06:56 -05:00
Robert
a4705d2c19 add RFC 3927 IP address space to private IP checks (#5115)
Add the RFC 3927 IP address block (169.254.0.0/16), also referred to as IPIPA, to the private address checks for MQTT functionality.
2024-10-21 19:25:27 +11:00
aussieklutz
5ff8c904c8 MPR121 Touch IC Based Keypad Input Module (#5103)
Implements an input driver utilising the MPR121 Touch IC and is compatible with common MPR121 keypad PCB's.

- Implements a "candybar" phone style 12-key keypad
  - multiple taps to rotate through the character set
  - longpress for navigation keys
  - keymap to allow arbitrary routing of MPR121 pin to button configuration
  - extendable to other key functions
- Integrates with the existing kbI2cBase implementation
- Works with CannedMessageModule Freetext mode
- Can be used with common MPR121 boards, such as https://www.amazon.com/MPR121-Capacitive-Keyboard-Buttons-Sensitive/dp/B083R89CHB/ref=sr_1_6
- Of use for PCB based radios, where some form of low surface area low component freetext input is required, but also without consuming too many IO pins.
- Tested on a T3S3 connected to Wire1 (Second) interface.
  - Demonstration of functionality: https://youtu.be/UI6QP6nGvhY
2024-10-21 17:53:36 +11:00
GUVWAF
09c8642aa6 Fix rebroadcasting encrypted packets when KNOWN_ONLY or LOCAL_ONLY is used (#5109) 2024-10-20 17:04:45 -05:00
Thomas Göttgens
82bcd503a3 Merge pull request #5112 from meshtastic/preamble-length
Wide_Lora uses 12 symbols to be compatible with SX1280
2024-10-20 22:06:06 +02:00
Thomas Göttgens
18ca5b4449 Wide_Lora uses 12 symbols to be compatible with SX1280 2024-10-20 21:54:07 +02:00
Tom Fifield
2ba72c154a Fix GPS_DEBUG output (#5100)
After the recent change to move logging line breaks to a central
location, GPS_DEBUG is now emitting one character per line,
making the logs unusable.

Patch uses local strings and appends to collate and then print
in the right places.

Fixes https://github.com/meshtastic/firmware/issues/5099

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-10-20 04:46:25 -05:00
Ben Meadors
7e3931b05d Move 115200 baud GNSS probe earlier (#5101)
* Move 115200 baud GNSS probe earlier

* Even more optimized!
2024-10-19 20:05:52 -05:00
Ben Meadors
4575352d8c Initial NODENUM_BROADCAST_NO_LORA implementation with NeighborInfo module (#5087)
* Initial NODENUM_BROADCAST_NO_LORA implementation with NeighborInfo module

* isBroadcast

* Trunkt
2024-10-19 12:48:00 -05:00
Thomas Göttgens
b1b6bce6b7 Merge pull request #5095 from meshtastic/revert-5036-caveman99-patch-1
Revert "Permanently engage !CTRL"
2024-10-19 19:06:33 +02:00
Thomas Göttgens
304f26b909 Revert "Permanently engage !CTRL" 2024-10-19 19:01:58 +02:00
madeofstown
a27f9fcdbd Add -p flag (#5093)
Add the `-p` to the `mkdir` so it doesn't fail when the folder already exists

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-10-18 20:19:24 -05:00
Jonathan Bennett
dfeb33d46e Add DIO2_AS_RF_SWITCH to pinedio prefilled config. 2024-10-18 12:30:46 -05:00
github-actions[bot]
b8b6894d58 [create-pull-request] automated change (#5091)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-10-18 06:00:55 -05:00
Technologyman00
934be69663 Add buzzer feedback on GPS toggle (#5090)
Triple Press on buttons toggles GPS enable/disable.

This enhancement plays a triple-beep so that users of devices with buzzers can get audible feedback about whether they have turned the GPS off or on. This is especially valuable for screenless devices such as the T1000E where it may not be immediately obvious the GPS has been disabled.
2024-10-18 14:40:18 +11:00
Ben Meadors
ec9e562a77 Coerce minimum telemetry interval of 30 minutes on defaults and make new default interval one hour (#5086)
* Coerce minimum telemetry interval of 30 minutes on defaults and make new default interval one hour

* Smaller log messages
2024-10-17 13:33:52 -05:00
Johnathon Mohr
fbb6778415 Account for port specification with IP address for MQTT server. Some additional format validation. (#5084) 2024-10-17 05:05:35 -05:00
github-actions[bot]
2ea2b47690 [create-pull-request] automated change (#5085)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-10-16 19:51:11 -05:00
38 changed files with 804 additions and 56 deletions

View File

@@ -20,6 +20,7 @@ Lora:
# CS: 0
# IRQ: 10
# Busy: 11
# DIO2_AS_RF_SWITCH: true
# spidev: spidev0.1
# Module: RF95 # Adafruit RFM9x
@@ -154,4 +155,4 @@ Webserver:
General:
MaxNodes: 200
MaxMessageQueue: 100
MaxMessageQueue: 100

View File

@@ -1,12 +1,16 @@
[Unit]
Description=Meshtastic Native Daemon
After=network-online.target
StartLimitInterval=200
StartLimitBurst=5
[Service]
User=root
Group=root
Type=simple
ExecStart=/usr/sbin/meshtasticd
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
cp "release/meshtasticd_linux_$(uname -m)" /usr/sbin/meshtasticd
mkdir /etc/meshtasticd
mkdir -p /etc/meshtasticd
if [[ -f "/etc/meshtasticd/config.yaml" ]]; then
cp bin/config-dist.yaml /etc/meshtasticd/config-upgrade.yaml
else

View File

@@ -55,6 +55,18 @@ void playBeep()
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playGPSEnableBeep()
{
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playGPSDisableBeep()
{
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playStartMelody()
{
ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}};

View File

@@ -3,3 +3,5 @@
void playBeep();
void playStartMelody();
void playShutdownMelody();
void playGPSEnableBeep();
void playGPSDisableBeep();

View File

@@ -114,6 +114,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
#define BBQ10_KB_ADDR 0x1F
#define MPR121_KB_ADDR 0x5A
// -----------------------------------------------------------------------------
// SENSOR

View File

@@ -31,8 +31,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
{
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004};
return firstOfOrNONE(4, types);
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, MPR121KB};
return firstOfOrNONE(5, types);
}
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const

View File

@@ -61,7 +61,8 @@ class ScanI2C
STK8BAXX,
ICM20948,
MAX30102,
TPS65233
TPS65233,
MPR121KB
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@@ -243,6 +243,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found");
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found");
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found");
#ifdef HAS_NCP5623
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found");
@@ -408,7 +409,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#ifdef HAS_TPS65233
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233 BIAS-T found");
#endif
SCAN_SIMPLE_CASE(MLX90614_ADDR_DEF, MLX90614, "MLX90614 IR temp sensor found");
case MLX90614_ADDR_DEF:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1);
if (registerValue == 0x5a) {
type = MLX90614;
LOG_INFO("MLX90614 IR temp sensor found");
} else {
type = MPR121KB;
LOG_INFO("MPR121KB keyboard found");
}
break;
case ICM20948_ADDR: // same as BMX160_ADDR
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR

View File

@@ -7,6 +7,7 @@
#include "PowerMon.h"
#include "RTC.h"
#include "Throttle.h"
#include "buzz.h"
#include "meshUtils.h"
#include "main.h" // pmu_found
@@ -266,6 +267,9 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
uint32_t startTime = millis();
const char frame_errors[] = "More than 100 frame errors";
int sCounter = 0;
#ifdef GPS_DEBUG
std::string debugmsg = "";
#endif
for (int j = 2; j < 6; j++) {
buf[8] += buf[j];
@@ -291,20 +295,24 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
if (b == frame_errors[sCounter]) {
sCounter++;
if (sCounter == 26) {
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
return GNSS_RESPONSE_FRAME_ERRORS;
}
} else {
sCounter = 0;
}
#ifdef GPS_DEBUG
LOG_DEBUG("%02X", b);
debugmsg += vformat("%02X", b);
#endif
if (b == buf[ack]) {
ack++;
} else {
if (ack == 3 && b == 0x00) { // UBX-ACK-NAK message
#ifdef GPS_DEBUG
LOG_DEBUG("");
LOG_DEBUG(debugmsg.c_str());
#endif
LOG_WARN("Got NAK for class %02X message %02X", class_id, msg_id);
return GNSS_RESPONSE_NAK; // NAK received
@@ -314,7 +322,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
}
}
#ifdef GPS_DEBUG
LOG_DEBUG("");
LOG_DEBUG(debugmsg.c_str());
LOG_WARN("No response for class %02X message %02X", class_id, msg_id);
#endif
return GNSS_RESPONSE_NONE; // No response received within timeout
@@ -1623,6 +1631,9 @@ bool GPS::whileActive()
{
unsigned int charsInBuf = 0;
bool isValid = false;
#ifdef GPS_DEBUG
std::string debugmsg = "";
#endif
if (powerState != GPS_ACTIVE) {
clearBuffer();
return false;
@@ -1640,7 +1651,7 @@ bool GPS::whileActive()
int c = _serial_gps->read();
UBXscratch[charsInBuf] = c;
#ifdef GPS_DEBUG
LOG_DEBUG("%c", c);
debugmsg += vformat("%c", (c >= 32 && c <= 126) ? c : '.');
#endif
isValid |= reader.encode(c);
if (charsInBuf > sizeof(UBXscratch) - 10 || c == '\r') {
@@ -1652,6 +1663,9 @@ bool GPS::whileActive()
charsInBuf++;
}
}
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
return isValid;
}
void GPS::enable()
@@ -1680,6 +1694,7 @@ void GPS::toggleGpsMode()
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
LOG_INFO("User toggled GpsMode. Now DISABLED.");
playGPSDisableBeep();
#ifdef GNSS_AIROHA
if (powerState == GPS_ACTIVE) {
LOG_DEBUG("User power Off GPS");
@@ -1690,6 +1705,7 @@ void GPS::toggleGpsMode()
} else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
LOG_INFO("User toggled GpsMode. Now ENABLED");
playGPSEnableBeep();
enable();
}
}

View File

@@ -75,7 +75,7 @@ class GPS : private concurrency::OSThread
uint8_t fixType = 0; // fix type from GPGSA
#endif
private:
const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600};
const int serialSpeeds[6] = {9600, 115200, 38400, 4800, 57600, 9600};
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0;

View File

@@ -0,0 +1,430 @@
// Based on the BBQ10 Keyboard
#include "MPR121Keyboard.h"
#include "configuration.h"
#include <Arduino.h>
#define _MPR121_REG_KEY 0x5a
#define _MPR121_REG_TOUCH_STATUS 0x00
#define _MPR121_REG_ELECTRODE_FILTERED_DATA
#define _MPR121_REG_BASELINE_VALUE 0x1E
// Baseline filters
#define _MPR121_REG_MAX_HALF_DELTA_RISING 0x2B
#define _MPR121_REG_NOISE_HALF_DELTA_RISING 0x2C
#define _MPR121_REG_NOISE_COUNT_LIMIT_RISING 0x2D
#define _MPR121_REG_FILTER_DELAY_COUNT_RISING 0x2E
#define _MPR121_REG_MAX_HALF_DELTA_FALLING 0x2F
#define _MPR121_REG_NOISE_HALF_DELTA_FALLING 0x30
#define _MPR121_REG_NOISE_COUNT_LIMIT_FALLING 0x31
#define _MPR121_REG_FILTER_DELAY_COUNT_FALLING 0x32
#define _MPR121_REG_NOISE_HALF_DELTA_TOUCHED 0x33
#define _MPR121_REG_NOISE_COUNT_LIMIT_TOUCHED 0x34
#define _MPR121_REG_FILTER_DELAY_COUNT_TOUCHED 0x35
#define _MPR121_REG_TOUCH_THRESHOLD 0x41 // First input, +2 for subsequent
#define _MPR121_REG_RELEASE_THRESHOLD 0x42 // First input, +2 for subsequent
#define _MPR121_REG_DEBOUNCE 0x5B
#define _MPR121_REG_CONFIG1 0x5C
#define _MPR121_REG_CONFIG2 0x5D
#define _MPR121_REG_ELECTRODE_CONFIG 0x5E
#define _MPR121_REG_SOFT_RESET 0x80
#define _KEY_MASK 0x0FFF // Key mask for the first 12 bits
#define _NUM_KEYS 12
#define ECR_CALIBRATION_TRACK_FROM_ZERO (0 << 6)
#define ECR_CALIBRATION_LOCK (1 << 6)
#define ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER (2 << 6) // Recommended Typical Mode
#define ECR_CALIBRATION_TRACK_FROM_FULL_FILTER (3 << 6)
#define ECR_PROXIMITY_DETECTION_OFF (0 << 0) // Not using proximity detection
#define ECR_TOUCH_DETECTION_12CH (12 << 0) // Using all 12 channels
#define MPR121_NONE 0x00
#define MPR121_REBOOT 0x90
#define MPR121_LEFT 0xb4
#define MPR121_UP 0xb5
#define MPR121_DOWN 0xb6
#define MPR121_RIGHT 0xb7
#define MPR121_ESC 0x1b
#define MPR121_BSP 0x08
#define MPR121_SELECT 0x0d
#define MPR121_FN_ON 0xf1
#define MPR121_FN_OFF 0xf2
#define LONG_PRESS_THRESHOLD 2000
#define MULTI_TAP_THRESHOLD 2000
uint8_t TapMod[12] = {1, 2, 1, 13, 7, 7, 7, 7, 7, 9, 7, 9}; // Num chars per key, Modulus for rotating through characters
unsigned char MPR121_TapMap[12][13] = {{MPR121_BSP},
{'0', ' '},
{MPR121_SELECT},
{'1', '.', ',', '?', '!', ':', ';', '-', '_', '\\', '/', '(', ')'},
{'2', 'a', 'b', 'c', 'A', 'B', 'C'},
{'3', 'd', 'e', 'f', 'D', 'E', 'F'},
{'4', 'g', 'h', 'i', 'G', 'H', 'I'},
{'5', 'j', 'k', 'l', 'J', 'K', 'L'},
{'6', 'm', 'n', 'o', 'M', 'N', 'O'},
{'7', 'p', 'q', 'r', 's', 'P', 'Q', 'R', 'S'},
{'8', 't', 'u', 'v', 'T', 'U', 'V'},
{'9', 'w', 'x', 'y', 'z', 'W', 'X', 'Y', 'Z'}};
unsigned char MPR121_LongPressMap[12] = {MPR121_ESC, ' ', MPR121_NONE, MPR121_NONE, MPR121_UP, MPR121_NONE,
MPR121_LEFT, MPR121_NONE, MPR121_RIGHT, MPR121_NONE, MPR121_DOWN, MPR121_NONE};
// Translation map from left to right, top to bottom layout to a more convenient layout to manufacture, matching the
// https://www.amazon.com.au/Capacitive-Sensitive-Sensitivity-Replacement-Traditional/dp/B0CTJD5KW9/ref=pd_ci_mcx_mh_mcx_views_0_title?th=1
/*uint8_t MPR121_KeyMap[12] = {
9, 6, 3, 0,
10, 7, 4, 1,
11, 8, 5, 2
};*/
// Rotated Layout
uint8_t MPR121_KeyMap[12] = {2, 5, 8, 11, 1, 4, 7, 10, 0, 3, 6, 9};
MPR121Keyboard::MPR121Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr)
{
// LOG_DEBUG("MPR121 @ %02x\n", m_addr);
state = Init;
last_key = -1;
last_tap = 0L;
char_idx = 0;
queue = "";
}
void MPR121Keyboard::begin(uint8_t addr, TwoWire *wire)
{
m_addr = addr;
m_wire = wire;
m_wire->begin();
reset();
}
void MPR121Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
{
m_addr = addr;
m_wire = nullptr;
writeCallback = w;
readCallback = r;
reset();
}
void MPR121Keyboard::reset()
{
LOG_DEBUG("MPR121 Resetting...");
// Trigger a MPR121 Soft Reset
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(_MPR121_REG_SOFT_RESET);
m_wire->endTransmission();
}
if (writeCallback) {
uint8_t data = 0;
writeCallback(m_addr, _MPR121_REG_SOFT_RESET, &data, 0);
}
delay(100);
// Reset Electrode Configuration to 0x00, Stop Mode
writeRegister(_MPR121_REG_ELECTRODE_CONFIG, 0x00);
delay(100);
LOG_DEBUG("MPR121 Configuring");
// Set touch release thresholds
for (uint8_t i = 0; i < 12; i++) {
// Set touch threshold
writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 15);
delay(20);
// Set release threshold
writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 7);
delay(20);
}
// Configure filtering and baseline registers
writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x01);
delay(20);
writeRegister(_MPR121_REG_MAX_HALF_DELTA_FALLING, 0x01);
delay(20);
writeRegister(_MPR121_REG_NOISE_HALF_DELTA_RISING, 0x01);
delay(20);
writeRegister(_MPR121_REG_NOISE_HALF_DELTA_FALLING, 0x05);
delay(20);
writeRegister(_MPR121_REG_NOISE_HALF_DELTA_TOUCHED, 0x00);
delay(20);
writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x0e);
delay(20);
writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_FALLING, 0x01);
delay(20);
writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_TOUCHED, 0x00);
delay(20);
writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_RISING, 0x00);
delay(20);
writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_FALLING, 0x00);
delay(20);
writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_TOUCHED, 0x00);
delay(20);
// Set Debounce to 0x02
writeRegister(_MPR121_REG_DEBOUNCE, 0x00);
delay(20);
// Set Filter1 itterations and discharge current 6x and 16uA respectively (0x10)
writeRegister(_MPR121_REG_CONFIG1, 0x10);
delay(20);
// Set CDT to 0.5us, Filter2 itterations to 4x, and Sample interval = 0 (0x20)
writeRegister(_MPR121_REG_CONFIG2, 0x20);
delay(20);
// Enter run mode by Seting partial filter calibration tracking, disable proximity detection, enable 12 channels
writeRegister(_MPR121_REG_ELECTRODE_CONFIG,
ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH);
delay(100);
LOG_DEBUG("MPR121 Running");
state = Idle;
}
void MPR121Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const
{
pinMode(pin, INPUT_PULLUP);
::attachInterrupt(digitalPinToInterrupt(pin), func, RISING);
}
void MPR121Keyboard::detachInterrupt(uint8_t pin) const
{
::detachInterrupt(pin);
}
uint8_t MPR121Keyboard::status() const
{
return readRegister16(_MPR121_REG_KEY);
}
uint8_t MPR121Keyboard::keyCount() const
{
// Read the key register
uint16_t keyRegister = readRegister16(_MPR121_REG_KEY);
return keyCount(keyRegister);
}
uint8_t MPR121Keyboard::keyCount(uint16_t value) const
{
// Mask the first 12 bits
uint16_t buttonState = value & _KEY_MASK;
// Count how many bits are set to 1 (i.e., how many buttons are pressed)
uint8_t numButtonsPressed = 0;
for (uint8_t i = 0; i < 12; ++i) {
if (buttonState & (1 << i)) {
numButtonsPressed++;
}
}
return numButtonsPressed;
}
bool MPR121Keyboard::hasEvent()
{
return queue.length() > 0;
}
void MPR121Keyboard::queueEvent(char next)
{
if (next == MPR121_NONE) {
return;
}
queue.concat(next);
}
char MPR121Keyboard::dequeueEvent()
{
if (queue.length() < 1) {
return MPR121_NONE;
}
char next = queue.charAt(0);
queue.remove(0, 1);
return next;
}
void MPR121Keyboard::trigger()
{
// Intended to fire in response to an interrupt from the MPR121 or a longpress callback
// Only functional if not in Init state
if (state != Init) {
// Read the key register
uint16_t keyRegister = readRegister16(_MPR121_REG_KEY);
uint8_t keysPressed = keyCount(keyRegister);
if (keysPressed == 0) {
// No buttons pressed
if (state == Held)
released();
state = Idle;
return;
}
if (keysPressed == 1) {
// No buttons pressed
if (state == Held || state == HeldLong)
held(keyRegister);
if (state == Idle)
pressed(keyRegister);
return;
}
if (keysPressed > 1) {
// Multipress
state = Busy;
return;
}
} else {
reset();
}
}
void MPR121Keyboard::pressed(uint16_t keyRegister)
{
if (state == Init || state == Busy) {
return;
}
if (keyCount(keyRegister) != 1) {
LOG_DEBUG("Multipress");
return;
} else {
LOG_DEBUG("Pressed");
}
uint16_t buttonState = keyRegister & _KEY_MASK;
uint8_t next_pin = 0;
for (uint8_t i = 0; i < 12; ++i) {
if (buttonState & (1 << i)) {
next_pin = i;
}
}
uint8_t next_key = MPR121_KeyMap[next_pin];
LOG_DEBUG("MPR121 Pin: %i Key: %i", next_pin, next_key);
uint32_t now = millis();
int32_t tap_interval = now - last_tap;
if (tap_interval < 0) {
// long running, millis has overflowed.
last_tap = 0;
state = Busy;
return;
}
if (next_key != last_key || tap_interval > MULTI_TAP_THRESHOLD) {
char_idx = 0;
} else {
char_idx += 1;
}
last_key = next_key;
last_tap = now;
state = Held;
return;
}
void MPR121Keyboard::held(uint16_t keyRegister)
{
if (state == Init || state == Busy) {
return;
}
if (keyCount(keyRegister) != 1) {
return;
}
LOG_DEBUG("Held");
uint16_t buttonState = keyRegister & _KEY_MASK;
uint8_t next_key = 0;
for (uint8_t i = 0; i < 12; ++i) {
if (buttonState & (1 << i)) {
next_key = MPR121_KeyMap[i];
}
}
uint32_t now = millis();
int32_t held_interval = now - last_tap;
if (held_interval < 0 || next_key != last_key) {
// long running, millis has overflowed, or a key has been switched quickly...
last_tap = 0;
state = Busy;
return;
}
if (held_interval > LONG_PRESS_THRESHOLD) {
// Set state to heldlong, send a longpress, and reset the timer...
state = HeldLong; // heldlong will allow this function to still fire, but prevent a "release"
queueEvent(MPR121_LongPressMap[last_key]);
last_tap = now;
LOG_DEBUG("Long Press Key: %i Map: %i", last_key, MPR121_LongPressMap[last_key]);
}
return;
}
void MPR121Keyboard::released()
{
if (state != Held) {
return;
}
// would clear longpress callback... later.
if (last_key < 0 || last_key > _NUM_KEYS) { // reset to idle if last_key out of bounds
last_key = -1;
state = Idle;
return;
}
LOG_DEBUG("Released");
if (char_idx > 0 && TapMod[last_key] > 1) {
queueEvent(MPR121_BSP);
LOG_DEBUG("Multi Press, Backspace");
}
queueEvent(MPR121_TapMap[last_key][(char_idx % TapMod[last_key])]);
LOG_DEBUG("Key Press: %i Index:%i if %i Map: %i", last_key, char_idx, TapMod[last_key],
MPR121_TapMap[last_key][(char_idx % TapMod[last_key])]);
}
uint8_t MPR121Keyboard::readRegister8(uint8_t reg) const
{
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(reg);
m_wire->endTransmission();
m_wire->requestFrom(m_addr, (uint8_t)1);
if (m_wire->available() < 1)
return 0;
return m_wire->read();
}
if (readCallback) {
uint8_t data;
readCallback(m_addr, reg, &data, 1);
return data;
}
return 0;
}
uint16_t MPR121Keyboard::readRegister16(uint8_t reg) const
{
uint8_t data[2] = {0};
// uint8_t low = 0, high = 0;
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(reg);
m_wire->endTransmission();
m_wire->requestFrom(m_addr, (uint8_t)2);
if (m_wire->available() < 2)
return 0;
data[0] = m_wire->read();
data[1] = m_wire->read();
}
if (readCallback) {
readCallback(m_addr, reg, data, 2);
}
return (data[1] << 8) | data[0];
}
void MPR121Keyboard::writeRegister(uint8_t reg, uint8_t value)
{
uint8_t data[2];
data[0] = reg;
data[1] = value;
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(data, sizeof(uint8_t) * 2);
m_wire->endTransmission();
}
if (writeCallback) {
writeCallback(m_addr, data[0], &(data[1]), 1);
}
}

View File

@@ -0,0 +1,56 @@
// Based on the BBQ10 Keyboard
#include "concurrency/NotifiedWorkerThread.h"
#include "configuration.h"
#include <Wire.h>
#include <main.h>
class MPR121Keyboard
{
public:
typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
enum MPR121States { Init = 0, Idle, Held, HeldLong, Busy };
MPR121States state;
int8_t last_key;
uint32_t last_tap;
uint8_t char_idx;
String queue;
MPR121Keyboard();
void begin(uint8_t addr = MPR121_KB_ADDR, TwoWire *wire = &Wire);
void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = MPR121_KB_ADDR);
void reset(void);
void attachInterrupt(uint8_t pin, void (*func)(void)) const;
void detachInterrupt(uint8_t pin) const;
void trigger(void);
void pressed(uint16_t value);
void held(uint16_t value);
void released(void);
uint8_t status(void) const;
uint8_t keyCount(void) const;
uint8_t keyCount(uint16_t value) const;
bool hasEvent(void);
char dequeueEvent(void);
void queueEvent(char);
uint8_t readRegister8(uint8_t reg) const;
uint16_t readRegister16(uint8_t reg) const;
void writeRegister(uint8_t reg, uint8_t value);
private:
TwoWire *m_wire;
uint8_t m_addr;
i2c_com_fptr_t readCallback;
i2c_com_fptr_t writeCallback;
};

View File

@@ -12,8 +12,8 @@ void CardKbI2cImpl::init()
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO)
if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescanning for I2C keyboard");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR};
uint8_t i2caddr_asize = 3;
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR};
uint8_t i2caddr_asize = 4;
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if WIRE_INTERFACES_COUNT == 2
@@ -39,12 +39,17 @@ void CardKbI2cImpl::init()
// assign an arbitrary value to distinguish from other models
kb_model = 0x11;
break;
case ScanI2C::DeviceType::MPR121KB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x37;
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", kb_info.type);
kb_model = 0x00;
}
}
LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x\n", kb_info.type, kb_model, cardkb_found.address);
if (cardkb_found.address == 0x00) {
disable();
return;

View File

@@ -40,6 +40,9 @@ int32_t KbI2cBase::runOnce()
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1);
Q10keyboard.setBacklight(0);
}
if (cardkb_found.address == MPR121_KB_ADDR) {
MPRkeyboard.begin(MPR121_KB_ADDR, &Wire1);
}
break;
#endif
case ScanI2C::WIRE:
@@ -49,6 +52,9 @@ int32_t KbI2cBase::runOnce()
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire);
Q10keyboard.setBacklight(0);
}
if (cardkb_found.address == MPR121_KB_ADDR) {
MPRkeyboard.begin(MPR121_KB_ADDR, &Wire);
}
break;
case ScanI2C::NO_I2C:
default:
@@ -157,6 +163,69 @@ int32_t KbI2cBase::runOnce()
}
break;
}
case 0x37: { // MPR121
MPRkeyboard.trigger();
InputEvent e;
while (MPRkeyboard.hasEvent()) {
char nextEvent = MPRkeyboard.dequeueEvent();
e.inputEvent = ANYKEY;
e.kbchar = 0x00;
e.source = this->_originName;
switch (nextEvent) {
case 0x00: // MPR121_NONE
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.kbchar = 0x00;
break;
case 0x90: // MPR121_REBOOT
e.inputEvent = ANYKEY;
e.kbchar = INPUT_BROKER_MSG_REBOOT;
break;
case 0xb4: // MPR121_LEFT
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = 0x00;
break;
case 0xb5: // MPR121_UP
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
e.kbchar = 0x00;
break;
case 0xb6: // MPR121_DOWN
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
e.kbchar = 0x00;
break;
case 0xb7: // MPR121_RIGHT
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = 0x00;
break;
case 0x1b: // MPR121_ESC
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
e.kbchar = 0x1b;
break;
case 0x08: // MPR121_BSP
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = 0x08;
break;
case 0x0d: // MPR121_SELECT
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
e.kbchar = 0x0d;
break;
default:
if (nextEvent > 127) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.kbchar = 0x00;
break;
}
e.inputEvent = ANYKEY;
e.kbchar = nextEvent;
break;
}
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
LOG_DEBUG("MP121 Notifying: %i Char: %i", e.inputEvent, e.kbchar);
this->notifyObservers(&e);
}
}
break;
}
case 0x02: {
// RAK14004
uint8_t rDataBuf[8] = {0};

View File

@@ -2,6 +2,7 @@
#include "BBQ10Keyboard.h"
#include "InputBroker.h"
#include "MPR121Keyboard.h"
#include "Wire.h"
#include "concurrency/OSThread.h"
@@ -19,5 +20,6 @@ class KbI2cBase : public Observable<const InputEvent *>, public concurrency::OST
TwoWire *i2cBus = 0;
BBQ10Keyboard Q10keyboard;
MPR121Keyboard MPRkeyboard;
bool is_sym = false;
};
};

View File

@@ -523,6 +523,10 @@ void setup()
// assign an arbitrary value to distinguish from other models
kb_model = 0x11;
break;
case ScanI2C::DeviceType::MPR121KB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x37;
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", kb_info.type);

View File

@@ -117,13 +117,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
static const uint8_t defaultpsk0[] = USERPREFS_CHANNEL_0_PSK;
memcpy(channelSettings.psk.bytes, defaultpsk0, sizeof(defaultpsk0));
channelSettings.psk.size = sizeof(defaultpsk0);
#endif
#ifdef USERPREFS_CHANNEL_0_NAME
strcpy(channelSettings.name, USERPREFS_CHANNEL_0_NAME);
#endif
#ifdef USERPREFS_CHANNEL_0_PRECISION
channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_0_PRECISION;
#endif
#ifdef USERPREFS_CHANNEL_0_UPLINK_ENABLED
channelSettings.uplink_enabled = USERPREFS_CHANNEL_0_UPLINK_ENABLED;
#endif
#ifdef USERPREFS_CHANNEL_0_DOWNLINK_ENABLED
channelSettings.downlink_enabled = USERPREFS_CHANNEL_0_DOWNLINK_ENABLED;
#endif
break;
case 1:
@@ -131,13 +136,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
static const uint8_t defaultpsk1[] = USERPREFS_CHANNEL_1_PSK;
memcpy(channelSettings.psk.bytes, defaultpsk1, sizeof(defaultpsk1));
channelSettings.psk.size = sizeof(defaultpsk1);
#endif
#ifdef USERPREFS_CHANNEL_1_NAME
strcpy(channelSettings.name, USERPREFS_CHANNEL_1_NAME);
#endif
#ifdef USERPREFS_CHANNEL_1_PRECISION
channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_1_PRECISION;
#endif
#ifdef USERPREFS_CHANNEL_1_UPLINK_ENABLED
channelSettings.uplink_enabled = USERPREFS_CHANNEL_1_UPLINK_ENABLED;
#endif
#ifdef USERPREFS_CHANNEL_1_DOWNLINK_ENABLED
channelSettings.downlink_enabled = USERPREFS_CHANNEL_1_DOWNLINK_ENABLED;
#endif
break;
case 2:
@@ -145,13 +155,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
static const uint8_t defaultpsk2[] = USERPREFS_CHANNEL_2_PSK;
memcpy(channelSettings.psk.bytes, defaultpsk2, sizeof(defaultpsk2));
channelSettings.psk.size = sizeof(defaultpsk2);
#endif
#ifdef USERPREFS_CHANNEL_2_NAME
strcpy(channelSettings.name, USERPREFS_CHANNEL_2_NAME);
#endif
#ifdef USERPREFS_CHANNEL_2_PRECISION
channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_2_PRECISION;
#endif
#ifdef USERPREFS_CHANNEL_2_UPLINK_ENABLED
channelSettings.uplink_enabled = USERPREFS_CHANNEL_2_UPLINK_ENABLED;
#endif
#ifdef USERPREFS_CHANNEL_2_DOWNLINK_ENABLED
channelSettings.downlink_enabled = USERPREFS_CHANNEL_2_DOWNLINK_ENABLED;
#endif
break;
default:

View File

@@ -43,6 +43,15 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d
return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes);
}
uint32_t Default::getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue)
{
// If zero, intervals should be coalesced later by getConfiguredOrDefault... methods
if (configured == 0)
return configured;
return configured < minValue ? minValue : configured;
}
uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured)
{
#if USERPREFS_EVENT_MODE

View File

@@ -6,8 +6,9 @@
#define THIRTY_SECONDS_MS 30 * 1000
#define FIVE_SECONDS_MS 5 * 1000
#define min_default_telemetry_interval_secs 30 * 60
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60)
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 60 * 60)
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60)
#define default_wait_bluetooth_secs IF_ROUTER(1, 60)
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
@@ -35,6 +36,7 @@ class Default
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
private:
static float congestionScalingCoefficient(int numOnlineNodes)

View File

@@ -44,7 +44,7 @@ bool FloodingRouter::isRebroadcaster()
void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
{
bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && (p->decoded.request_id != 0);
if (isAckorReply && !isToUs(p) && p->to != NODENUM_BROADCAST) {
if (isAckorReply && !isToUs(p) && !isBroadcast(p->to)) {
// do not flood direct message that is ACKed or replied to
LOG_DEBUG("Rxd an ACK/reply not for me, cancel rebroadcast.");
Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM

View File

@@ -67,8 +67,10 @@ template <typename T> bool LR11x0Interface<T>::init()
power = LR1110_MAX_POWER;
if ((power > LR1120_MAX_POWER) &&
(config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // clamp again if wide freq range
(config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { // clamp again if wide freq range
power = LR1120_MAX_POWER;
preambleLength = 12; // 12 is the default for operation above 2GHz
}
limitPower();

View File

@@ -86,7 +86,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
auto ourNodeNum = nodeDB->getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || isToUs(&mp);
bool toUs = isBroadcast(mp.to) || isToUs(&mp);
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;

View File

@@ -57,4 +57,6 @@ bool isFromUs(const meshtastic_MeshPacket *p);
bool isToUs(const meshtastic_MeshPacket *p);
/* Some clients might not properly set priority, therefore we fix it here. */
void fixPriority(meshtastic_MeshPacket *p);
void fixPriority(meshtastic_MeshPacket *p);
bool isBroadcast(uint32_t dest);

View File

@@ -34,7 +34,11 @@
#endif
#include "modules/StoreForwardModule.h"
#include <Preferences.h>
#include <esp_efuse.h>
#include <esp_efuse_table.h>
#include <nvs_flash.h>
#include <soc/efuse_reg.h>
#include <soc/soc.h>
#endif
#ifdef ARCH_PORTDUINO
@@ -109,6 +113,43 @@ NodeDB::NodeDB()
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
int saveWhat = 0;
bool hasUniqueId = false;
// Get device unique id
#if defined(ARCH_ESP32) && defined(ESP_EFUSE_OPTIONAL_UNIQUE_ID)
uint32_t unique_id[4];
// ESP32 factory burns a unique id in efuse for S2+ series and evidently C3+ series
// This is used for HMACs in the esp-rainmaker AIOT platform and seems to be a good choice for us
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, unique_id, sizeof(unique_id) * 8);
if (err == ESP_OK) {
memcpy(myNodeInfo.device_id.bytes, unique_id, sizeof(unique_id));
myNodeInfo.device_id.size = 16;
hasUniqueId = true;
} else {
LOG_WARN("Failed to read unique id from efuse");
}
#elif defined(ARCH_NRF52)
// Nordic applies a FIPS compliant Random ID to each chip at the factory
// We concatenate the device address to the Random ID to create a unique ID for now
// This will likely utilize a crypto module in the future
uint64_t device_id_start = ((uint64_t)NRF_FICR->DEVICEID[1] << 32) | NRF_FICR->DEVICEID[0];
uint64_t device_id_end = ((uint64_t)NRF_FICR->DEVICEADDR[1] << 32) | NRF_FICR->DEVICEADDR[0];
memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start));
memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end));
myNodeInfo.device_id.size = 16;
hasUniqueId = true;
#else
// FIXME - implement for other platforms
#endif
// Uncomment below to print the device id
// if (hasUniqueId) {
// std::string deviceIdHex;
// for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) {
// char buf[3];
// snprintf(buf, sizeof(buf), "%02X", myNodeInfo.device_id.bytes[i]);
// deviceIdHex += buf;
// }
// LOG_DEBUG("Device ID (HEX): %s", deviceIdHex.c_str());
// }
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
@@ -171,6 +212,22 @@ NodeDB::NodeDB()
resetRadioConfig(); // If bogus settings got saved, then fix them
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
// If we are setup to broadcast on the default channel, ensure that the telemetry intervals are coerced to the minimum value
// of 30 minutes or more
if (channels.isDefaultChannel(channels.getPrimaryIndex())) {
LOG_DEBUG("Coercing telemetry to min of 30 minutes on defaults");
moduleConfig.telemetry.device_update_interval = Default::getConfiguredOrMinimumValue(
moduleConfig.telemetry.device_update_interval, min_default_telemetry_interval_secs);
moduleConfig.telemetry.environment_update_interval = Default::getConfiguredOrMinimumValue(
moduleConfig.telemetry.environment_update_interval, min_default_telemetry_interval_secs);
moduleConfig.telemetry.air_quality_interval = Default::getConfiguredOrMinimumValue(
moduleConfig.telemetry.air_quality_interval, min_default_telemetry_interval_secs);
moduleConfig.telemetry.power_update_interval = Default::getConfiguredOrMinimumValue(
moduleConfig.telemetry.power_update_interval, min_default_telemetry_interval_secs);
moduleConfig.telemetry.health_update_interval = Default::getConfiguredOrMinimumValue(
moduleConfig.telemetry.health_update_interval, min_default_telemetry_interval_secs);
}
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
saveWhat |= SEGMENT_DEVICESTATE;
if (configCRC != crc32Buffer(&config, sizeof(config)))
@@ -206,6 +263,11 @@ bool isToUs(const meshtastic_MeshPacket *p)
return p->to == nodeDB->getNodeNum();
}
bool isBroadcast(uint32_t dest)
{
return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA;
}
bool NodeDB::resetRadioConfig(bool factory_reset)
{
bool didFactoryReset = false;

View File

@@ -467,7 +467,10 @@ void RadioLibInterface::setStandby()
void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
{
printPacket("Starting low level send", txp);
if (disabled || !config.lora.tx_enabled) {
if (txp->to == NODENUM_BROADCAST_NO_LORA) {
LOG_DEBUG("Drop Tx packet because dest is broadcast no-lora");
packetPool.release(txp);
} else if (disabled || !config.lora.tx_enabled) {
LOG_WARN("Drop Tx packet because LoRa Tx disabled");
packetPool.release(txp);
} else {

View File

@@ -181,7 +181,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
} else {
// If we are sending a broadcast, we also treat it as if we just received it ourself
// this allows local apps (and PCs) to see broadcasts sourced locally
if (p->to == NODENUM_BROADCAST) {
if (isBroadcast(p->to)) {
handleReceived(p, src);
}
@@ -240,7 +240,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
// assert
// Never set the want_ack flag on broadcast packets sent over the air.
if (p->to == NODENUM_BROADCAST)
if (isBroadcast(p->to))
p->want_ack = false;
// Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over
@@ -328,7 +328,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
#if !(MESHTASTIC_EXCLUDE_PKI)
// Attempt PKI decryption first
if (p->channel == 0 && isToUs(p) && p->to > 0 && p->to != NODENUM_BROADCAST && nodeDB->getMeshNode(p->from) != nullptr &&
if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr &&
nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 &&
rawSize > MESHTASTIC_PKC_OVERHEAD) {
LOG_DEBUG("Attempting PKI decryption");
@@ -493,7 +493,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested
!(p->pki_encrypted != true && p->channel > 0) &&
// Check for valid keys and single node destination
config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr &&
config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr &&
// Check for a known public key for the destination
(node->user.public_key.size == 32) &&
// Some portnums either make no sense to send with PKC
@@ -615,7 +615,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
#if !MESHTASTIC_EXCLUDE_MQTT
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to
// us (because we would be able to decrypt it)
if (!decoded && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && p->to != NODENUM_BROADCAST && !isToUs(p))
if (!decoded && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && !isBroadcast(p->to) && !isToUs(p))
p_encrypted->pki_encrypted = true;
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
if ((decoded || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled && !isFromUs(p) && mqtt)

View File

@@ -75,7 +75,10 @@ typedef enum _meshtastic_Config_DeviceConfig_RebroadcastMode {
but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) */
meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY = 3,
/* Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role. */
meshtastic_Config_DeviceConfig_RebroadcastMode_NONE = 4
meshtastic_Config_DeviceConfig_RebroadcastMode_NONE = 4,
/* Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc.
Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing. */
meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY = 5
} meshtastic_Config_DeviceConfig_RebroadcastMode;
/* Bit field of boolean configuration options, indicating which optional
@@ -587,8 +590,8 @@ extern "C" {
#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK_TRACKER+1))
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_NONE
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_NONE+1))
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY+1))
#define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET
#define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED

View File

@@ -765,6 +765,7 @@ typedef struct _meshtastic_NodeInfo {
bool is_favorite;
} meshtastic_NodeInfo;
typedef PB_BYTES_ARRAY_T(16) meshtastic_MyNodeInfo_device_id_t;
/* Unique local debugging info for this node
Note: we don't include position or the user info, because that will come in the
Sent to the phone in response to WantNodes. */
@@ -778,6 +779,8 @@ typedef struct _meshtastic_MyNodeInfo {
/* The minimum app version that can talk to this device.
Phone/PC apps should compare this to their build number and if too low tell the user they must update their app */
uint32_t min_app_version;
/* Unique hardware identifier for this device */
meshtastic_MyNodeInfo_device_id_t device_id;
} meshtastic_MyNodeInfo;
/* Debug output from the device.
@@ -1112,7 +1115,7 @@ extern "C" {
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0}
#define meshtastic_MyNodeInfo_init_default {0, 0, 0}
#define meshtastic_MyNodeInfo_init_default {0, 0, 0, {0, {0}}}
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
@@ -1137,7 +1140,7 @@ extern "C" {
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0}
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0}
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0, {0, {0}}}
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
@@ -1244,6 +1247,7 @@ extern "C" {
#define meshtastic_MyNodeInfo_my_node_num_tag 1
#define meshtastic_MyNodeInfo_reboot_count_tag 8
#define meshtastic_MyNodeInfo_min_app_version_tag 11
#define meshtastic_MyNodeInfo_device_id_tag 12
#define meshtastic_LogRecord_message_tag 1
#define meshtastic_LogRecord_time_tag 2
#define meshtastic_LogRecord_source_tag 3
@@ -1446,7 +1450,8 @@ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_MyNodeInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, my_node_num, 1) \
X(a, STATIC, SINGULAR, UINT32, reboot_count, 8) \
X(a, STATIC, SINGULAR, UINT32, min_app_version, 11)
X(a, STATIC, SINGULAR, UINT32, min_app_version, 11) \
X(a, STATIC, SINGULAR, BYTES, device_id, 12)
#define meshtastic_MyNodeInfo_CALLBACK NULL
#define meshtastic_MyNodeInfo_DEFAULT NULL
@@ -1669,7 +1674,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_LogRecord_size 426
#define meshtastic_MeshPacket_size 367
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_MyNodeInfo_size 36
#define meshtastic_NeighborInfo_size 258
#define meshtastic_Neighbor_size 22
#define meshtastic_NodeInfo_size 317

View File

@@ -13,7 +13,7 @@
/* Canned message module configuration. */
typedef struct _meshtastic_RTTTLConfig {
/* Ringtone for PWM Buzzer in RTTTL Format. */
char ringtone[230];
char ringtone[231];
} meshtastic_RTTTLConfig;
@@ -41,7 +41,7 @@ extern const pb_msgdesc_t meshtastic_RTTTLConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_RTTTL_PB_H_MAX_SIZE meshtastic_RTTTLConfig_size
#define meshtastic_RTTTLConfig_size 232
#define meshtastic_RTTTLConfig_size 233
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -122,7 +122,7 @@ Will be used for broadcast.
int32_t NeighborInfoModule::runOnce()
{
if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) {
sendNeighborInfo(NODENUM_BROADCAST, false);
sendNeighborInfo(NODENUM_BROADCAST_NO_LORA, false);
}
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
}

View File

@@ -16,7 +16,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
bool hasChanged = nodeDB->updateUser(getFrom(&mp), p, mp.channel);
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
bool wasBroadcast = isBroadcast(mp.to);
// Show new nodes on LCD screen
if (wasBroadcast) {

View File

@@ -10,11 +10,7 @@ RoutingModule *routingModule;
bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Routing *r)
{
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
bool maybePKI =
mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && mp.to != NODENUM_BROADCAST;
bool maybePKI = mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && !isBroadcast(mp.to);
// Beginning of logic whether to drop the packet based on Rebroadcast mode
if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
(config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY ||
@@ -26,9 +22,12 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh
return false;
}
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
// FIXME - move this to a non promsicious PhoneAPI module?
// Note: we are careful not to send back packets that started with the phone back to the phone
if ((mp.to == NODENUM_BROADCAST || isToUs(&mp)) && (mp.from != 0)) {
if ((isBroadcast(mp.to) || isToUs(&mp)) && (mp.from != 0)) {
printPacket("Delivering rx packet", &mp);
service->handleFromRadio(&mp);
}

View File

@@ -706,22 +706,48 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json)
bool MQTT::isPrivateIpAddress(const char address[])
{
// Min. length like 10.0.0.0, max like 192.168.255.255
// Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21)
size_t length = strlen(address);
if (length < 8 || length > 15) {
if (length < 8 || length > 21) {
return false;
}
// Ensure the address contains only digits and dots.
// Ensure the address contains only digits and dots and maybe a colon.
// Some limited validation is done.
// Even if it's not a valid IP address, we will know it's not a domain.
bool hasColon = false;
int numDots = 0;
for (int i = 0; i < length; i++) {
if (!isdigit(address[i]) && address[i] != '.') {
if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
return false;
}
// Dots can't be the first character, immediately follow another dot,
// occur more than 3 times, or occur after a colon.
if (address[i] == '.') {
if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) {
return false;
}
}
// There can only be a single colon, and it can only occur after 3 dots
else if (address[i] == ':') {
if (hasColon || numDots < 3) {
return false;
}
hasColon = true;
}
}
// Final validation for IPv4 address and port format.
// Note that the values of octets haven't been tested, only the address format.
if (numDots != 3) {
return false;
}
// Check the easy ones first.
if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0) {
if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 ||
strncmp(address, "169.254", 7) == 0) {
return true;
}

View File

@@ -26,6 +26,8 @@
*/
// #define USERPREFS_CHANNEL_0_NAME "DEFCONnect"
// #define USERPREFS_CHANNEL_0_PRECISION 14
// #define USERPREFS_CHANNEL_0_UPLINK_ENABLED true
// #define USERPREFS_CHANNEL_0_DOWNLINK_ENABLED true
/*
#define USERPREFS_CHANNEL_1_PSK \
{ \
@@ -35,6 +37,8 @@
*/
// #define USERPREFS_CHANNEL_1_NAME "REPLACEME"
// #define USERPREFS_CHANNEL_1_PRECISION 14
// #define USERPREFS_CHANNEL_1_UPLINK_ENABLED true
// #define USERPREFS_CHANNEL_1_DOWNLINK_ENABLED true
/*
#define USERPREFS_CHANNEL_2_PSK \
{ \
@@ -44,6 +48,8 @@
*/
// #define USERPREFS_CHANNEL_2_NAME "REPLACEME"
// #define USERPREFS_CHANNEL_2_PRECISION 14
// #define USERPREFS_CHANNEL_2_UPLINK_ENABLED true
// #define USERPREFS_CHANNEL_2_DOWNLINK_ENABLED true
// #define USERPREFS_CONFIG_OWNER_LONG_NAME "My Long Name"
// #define USERPREFS_CONFIG_OWNER_SHORT_NAME "MLN"

View File

@@ -54,7 +54,7 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_BUSY
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL
#define SX126X_POWER_EN LORA_DIO4 // Antenna switch !CTRL via GPIO17
#define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL
#define SX126X_RXEN LORA_DIO4 // Antenna switch !CTRL via GPIO17
// #define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
#endif

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 5
build = 7
build = 8