Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54b0683e20 | ||
|
|
8e4ea08b98 | ||
|
|
f6c9b9aab0 | ||
|
|
449b95fe0f | ||
|
|
7a3b4395f2 | ||
|
|
0abd202b82 | ||
|
|
8049d77522 | ||
|
|
0ee360295b | ||
|
|
1c0182f329 | ||
|
|
472b14c4e4 | ||
|
|
3851fbfec3 | ||
|
|
8b22448285 | ||
|
|
73e79797b2 | ||
|
|
a96f83fd01 | ||
|
|
0f761d930b | ||
|
|
4d4906772f | ||
|
|
711abb56f3 | ||
|
|
82aefd1af1 | ||
|
|
0f9eb86830 | ||
|
|
e2aa44ec54 | ||
|
|
0832330327 | ||
|
|
af3739fd63 | ||
|
|
0148a89ddb | ||
|
|
6199faacf1 | ||
|
|
fc5556b8e6 | ||
|
|
502c5af524 | ||
|
|
05707079bd | ||
|
|
1c05633fcd | ||
|
|
fce419b335 |
@@ -333,6 +333,7 @@ jobs:
|
||||
prerelease: true
|
||||
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||
tag_name: v${{ needs.version.outputs.long }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
body: ${{ steps.release_notes.outputs.notes }}
|
||||
|
||||
- name: Download source deb
|
||||
|
||||
+4
-4
@@ -4,11 +4,11 @@ cli:
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.9.0
|
||||
ref: v1.10.0
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.528
|
||||
- checkov@3.2.529
|
||||
- renovate@43.150.0
|
||||
- prettier@3.8.3
|
||||
- trufflehog@3.95.3
|
||||
@@ -16,7 +16,7 @@ lint:
|
||||
- bandit@1.9.4
|
||||
- trivy@0.70.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.15.12
|
||||
- ruff@0.15.14
|
||||
- isort@8.0.1
|
||||
- markdownlint@0.48.0
|
||||
- oxipng@10.1.1
|
||||
@@ -26,7 +26,7 @@ lint:
|
||||
- hadolint@2.14.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.11.0
|
||||
- black@26.3.1
|
||||
- black@26.5.1
|
||||
- git-diff-check
|
||||
- gitleaks@8.30.1
|
||||
- clang-format@16.0.3
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.25" date="2026-05-23">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.25</url>
|
||||
</release>
|
||||
<release version="2.7.24" date="2026-05-08">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.24</url>
|
||||
</release>
|
||||
|
||||
Vendored
+6
@@ -1,3 +1,9 @@
|
||||
meshtasticd (2.7.25.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.25
|
||||
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 23 May 2026 01:16:20 +0000
|
||||
|
||||
meshtasticd (2.7.24.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.24
|
||||
|
||||
+2
-2
@@ -121,12 +121,12 @@ lib_deps =
|
||||
[radiolib_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=github-tags depName=RadioLib packageName=jgromes/RadioLib
|
||||
https://github.com/jgromes/RadioLib/archive/refs/tags/7.6.0.zip
|
||||
https://github.com/jgromes/RadioLib/archive/7.7.0.zip
|
||||
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/4bf593a82100b911ff816dddf7158ffdee2114cd.zip
|
||||
https://github.com/meshtastic/device-ui/archive/34e96d298e78ddf28b7c7e0e82b7bea503abafc3.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
+1
-1
Submodule protobufs updated: b302d92332...59cb394dcf
@@ -85,8 +85,9 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const
|
||||
|
||||
return o_probe;
|
||||
}
|
||||
|
||||
uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation ®isterLocation,
|
||||
ScanI2CTwoWire::ResponseWidth responseWidth, bool zeropad = false) const
|
||||
ScanI2CTwoWire::ResponseWidth responseWidth, bool zeropad) const
|
||||
{
|
||||
uint16_t value = 0x00;
|
||||
TwoWire *i2cBus = fetchI2CBus(registerLocation.i2cAddress);
|
||||
@@ -175,6 +176,62 @@ String readSEN5xProductName(TwoWire *i2cBus, uint8_t address)
|
||||
return String(productName);
|
||||
}
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
static uint8_t crcSHT2X(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : crc << 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool detectSHT21SerialNumber(TwoWire *i2cBus, uint8_t address)
|
||||
{
|
||||
uint8_t serialA[8] = {0};
|
||||
uint8_t serialB[6] = {0};
|
||||
|
||||
i2cBus->beginTransmission(address);
|
||||
i2cBus->write(0xFA);
|
||||
i2cBus->write(0x0F);
|
||||
|
||||
if (i2cBus->endTransmission() != 0)
|
||||
return false;
|
||||
|
||||
if (i2cBus->requestFrom(address, (uint8_t)sizeof(serialA)) != sizeof(serialA))
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(serialA); i++) {
|
||||
if (!i2cBus->available())
|
||||
return false;
|
||||
serialA[i] = i2cBus->read();
|
||||
}
|
||||
|
||||
i2cBus->beginTransmission(address);
|
||||
i2cBus->write(0xFC);
|
||||
i2cBus->write(0xC9);
|
||||
|
||||
if (i2cBus->endTransmission() != 0)
|
||||
return false;
|
||||
|
||||
if (i2cBus->requestFrom(address, (uint8_t)sizeof(serialB)) != sizeof(serialB))
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(serialB); i++) {
|
||||
if (!i2cBus->available())
|
||||
return false;
|
||||
serialB[i] = i2cBus->read();
|
||||
}
|
||||
|
||||
return crcSHT2X(&serialA[0], 1) == serialA[1] && crcSHT2X(&serialA[2], 1) == serialA[3] &&
|
||||
crcSHT2X(&serialA[4], 1) == serialA[5] && crcSHT2X(&serialA[6], 1) == serialA[7] &&
|
||||
crcSHT2X(&serialB[0], 2) == serialB[2] && crcSHT2X(&serialB[3], 2) == serialB[5];
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SCAN_SIMPLE_CASE(ADDR, T, ...) \
|
||||
case ADDR: \
|
||||
logFoundDevice(__VA_ARGS__); \
|
||||
|
||||
@@ -53,7 +53,7 @@ class ScanI2CTwoWire : public ScanI2C
|
||||
|
||||
concurrency::Lock lock;
|
||||
|
||||
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const;
|
||||
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool = false) const;
|
||||
|
||||
bool i2cCommandResponseLength(DeviceAddress addr, uint16_t command, uint8_t expectedLength) const;
|
||||
|
||||
|
||||
@@ -353,6 +353,11 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
#elif defined(USE_SSD1306)
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#if defined(OLED_Y_OFFSET_PAGES)
|
||||
// Panels whose active window does not start at GDDRAM row 0 (e.g. 72x40
|
||||
// modules on pages 3..7) need a fixed vertical page shift on every write.
|
||||
static_cast<SSD1306Wire *>(dispdev)->setYOffset(OLED_Y_OFFSET_PAGES);
|
||||
#endif
|
||||
#elif defined(USE_SPISSD1306)
|
||||
dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48);
|
||||
if (!dispdev->init()) {
|
||||
@@ -834,7 +839,7 @@ int32_t Screen::runOnce()
|
||||
|
||||
#ifndef DISABLE_WELCOME_UNSET
|
||||
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
menuHandler::LoraRegionPicker();
|
||||
#else
|
||||
menuHandler::OnboardMessage();
|
||||
@@ -1058,7 +1063,7 @@ void Screen::setFrames(FrameFocus focus)
|
||||
#if defined(DISPLAY_CLOCK_FRAME)
|
||||
if (!hiddenFrames.clock) {
|
||||
fsi.positions.clock = numframes;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||
#else
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
@@ -1511,7 +1516,7 @@ void Screen::showFrame(FrameDirection direction)
|
||||
|
||||
void Screen::setFastFramerate()
|
||||
{
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
dispdev->clear();
|
||||
dispdev->display();
|
||||
#endif
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||
#elif defined(M5STACK_UNITC6L)
|
||||
#elif defined(OLED_TINY)
|
||||
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||
#define FONT_MEDIUM FONT_SMALL_LOCAL // Height: 13
|
||||
#define FONT_LARGE FONT_SMALL_LOCAL // Height: 13
|
||||
|
||||
@@ -27,6 +27,12 @@ ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenw
|
||||
return ScreenResolution::UltraLow;
|
||||
}
|
||||
|
||||
#ifdef DISPLAY_FORCE_SMALL_FONTS
|
||||
if (screenwidth <= 160 && screenheight <= 80) {
|
||||
return ScreenResolution::Low;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Standard OLED screens
|
||||
if (screenwidth > 128 && screenheight <= 64) {
|
||||
return ScreenResolution::Low;
|
||||
@@ -155,7 +161,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
||||
|
||||
int batteryX = 1;
|
||||
int batteryY = HEADER_OFFSET_Y + 1;
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Battery Icons ===
|
||||
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
||||
batteryX += 1;
|
||||
|
||||
@@ -142,8 +142,9 @@ void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offset
|
||||
if (keyboardStartY < 0)
|
||||
keyboardStartY = 0;
|
||||
} else {
|
||||
// Default (non-wide, non-64px) behavior: use key height heuristic and place at bottom
|
||||
cellH = KEY_HEIGHT;
|
||||
// Default (non-wide, non-64px) e.g. SH1107 128x128:
|
||||
// cellH = FONT_HEIGHT_SMALL - 2 so rows are tighter while still hosting the font
|
||||
cellH = std::max((int)KEY_HEIGHT, FONT_HEIGHT_SMALL - 2);
|
||||
int keyboardHeight = KEYBOARD_ROWS * cellH;
|
||||
keyboardStartY = screenH - keyboardHeight;
|
||||
if (keyboardStartY < 0)
|
||||
@@ -446,11 +447,8 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
|
||||
if (textX < x)
|
||||
textX = x; // guard
|
||||
} else {
|
||||
if (display->getHeight() <= 64 && (key.character >= '0' && key.character <= '9')) {
|
||||
textX = x + (width - textWidth + 1) / 2;
|
||||
} else {
|
||||
textX = x + (width - textWidth) / 2;
|
||||
}
|
||||
// Use ceil rounding for all screens (consistent with 128x64 behavior for numbers)
|
||||
textX = x + (width - textWidth + 1) / 2;
|
||||
}
|
||||
int contentTop = y;
|
||||
int contentH = height;
|
||||
@@ -746,4 +744,4 @@ bool VirtualKeyboard::isTimedOut() const
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -183,9 +183,13 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
|
||||
|
||||
if (!scaleInitialized) {
|
||||
#ifdef DISPLAY_FORCE_SMALL_FONTS
|
||||
float screenwidth_target_ratio = 0.70f; // Target 70% of display width (adjustable)
|
||||
#else
|
||||
float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
|
||||
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
|
||||
float step = 0.05f; // Step increment per iteration
|
||||
#endif
|
||||
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
|
||||
float step = 0.05f; // Step increment per iteration
|
||||
|
||||
float target_width = display->getWidth() * screenwidth_target_ratio;
|
||||
float target_height =
|
||||
|
||||
@@ -449,7 +449,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
||||
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Fifth Row: Channel Utilization ===
|
||||
const char *chUtil = "ChUtil:";
|
||||
char chUtilPercentage[10];
|
||||
@@ -569,7 +569,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
||||
// Label
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->drawString(labelX, getTextPositions(display)[line], label);
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// Bar
|
||||
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
||||
display->setColor(WHITE);
|
||||
|
||||
@@ -491,7 +491,7 @@ void menuHandler::TZPicker()
|
||||
|
||||
void menuHandler::clockMenu()
|
||||
{
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static const char *optionsArray[] = {"Back", "Time Format", "Timezone"};
|
||||
#else
|
||||
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
||||
|
||||
@@ -21,7 +21,7 @@ extern bool haveGlyphs(const char *str);
|
||||
// Global screen instance
|
||||
extern graphics::Screen *screen;
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static uint32_t lastSwitchTime = 0;
|
||||
#endif
|
||||
namespace graphics
|
||||
@@ -670,7 +670,7 @@ void drawDynamicListScreen_Nodes(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
if (now - lastSwitchTime >= 3000) {
|
||||
display->display();
|
||||
@@ -706,7 +706,7 @@ void drawDynamicListScreen_Location(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
if (now - lastSwitchTime >= 3000) {
|
||||
display->display();
|
||||
@@ -771,7 +771,7 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
double lat = DegD(ourNode->position.latitude_i);
|
||||
double lon = DegD(ourNode->position.longitude_i);
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
uint32_t now = millis();
|
||||
if (now - lastSwitchTime >= 2000) {
|
||||
|
||||
@@ -580,7 +580,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
}
|
||||
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
||||
boxHeight += (currentResolution == ScreenResolution::High) ? 2 : 1;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
if (visibleTotalLines == 1) {
|
||||
boxTop += 25;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
// External variables
|
||||
extern graphics::Screen *screen;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static uint32_t lastSwitchTime = 0;
|
||||
#endif
|
||||
namespace graphics
|
||||
@@ -304,7 +304,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
||||
return;
|
||||
display->clear();
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
uint32_t now = millis();
|
||||
if (now - lastSwitchTime >= 10000) // 10000 ms = 10 秒
|
||||
{
|
||||
@@ -518,7 +518,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
if (seenStr[0]) {
|
||||
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
||||
}
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === 4. Uptime (only show if metric is present) ===
|
||||
char uptimeStr[32] = "";
|
||||
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
||||
@@ -795,7 +795,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
line += 1;
|
||||
|
||||
// === Node Identity ===
|
||||
@@ -1092,7 +1092,7 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
||||
// needs to be drawn relative to x and y
|
||||
|
||||
// draw centered icon left to right and centered above the one line of app text
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->drawXbm(x + (SCREEN_WIDTH - 50) / 2, y + (SCREEN_HEIGHT - 28) / 2, icon_width, icon_height, icon_bits);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
@@ -1243,7 +1243,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
||||
}
|
||||
display->drawString(x, getTextPositions(display)[line++], altitudeLine);
|
||||
}
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Draw Compass if heading is valid ===
|
||||
if (validHeading) {
|
||||
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
||||
|
||||
@@ -318,7 +318,7 @@ const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
||||
#define connection_icon_height 5
|
||||
const uint8_t connection_icon[] = {0x36, 0x41, 0x5D, 0x41, 0x36};
|
||||
|
||||
#ifdef M5STACK_UNITC6L
|
||||
#ifdef OLED_TINY
|
||||
#include "img/icon_small.xbm"
|
||||
#else
|
||||
#include "img/icon.xbm"
|
||||
|
||||
@@ -741,6 +741,11 @@ void setup()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef OLED_GEOMETRY_OVERRIDE
|
||||
// Per-variant geometry (e.g. 72x40 micro-OLEDs). Takes precedence over the
|
||||
// default GEOMETRY_128_64 set at the top of setup().
|
||||
screen_geometry = OLED_GEOMETRY_OVERRIDE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C
|
||||
|
||||
+3
-2
@@ -836,7 +836,8 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.has_store_forward = true;
|
||||
moduleConfig.has_telemetry = true;
|
||||
moduleConfig.has_external_notification = true;
|
||||
#if defined(PIN_BUZZER) || defined(PIN_VIBRATION) || defined(LED_NOTIFICATION) || defined(PCA_LED_NOTIFICATION)
|
||||
#if defined(PIN_BUZZER) || defined(PIN_VIBRATION) || defined(LED_NOTIFICATION) || defined(PCA_LED_NOTIFICATION) || \
|
||||
defined(NEOPIXEL_STATUS_NOTIFICATION_PIN)
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
#endif
|
||||
#if defined(PIN_BUZZER)
|
||||
@@ -857,7 +858,7 @@ void NodeDB::installDefaultModuleConfig()
|
||||
#endif
|
||||
#if defined(PIN_VIBRATION)
|
||||
moduleConfig.external_notification.nag_timeout = 2;
|
||||
#elif defined(PIN_BUZZER) || defined(LED_NOTIFICATION)
|
||||
#elif defined(PIN_BUZZER) || defined(LED_NOTIFICATION) || defined(NEOPIXEL_STATUS_NOTIFICATION_PIN)
|
||||
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "PositionPrecision.h"
|
||||
#include "Channels.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(const meshtastic_Channel &channel)
|
||||
{
|
||||
if (channel.settings.has_module_settings) {
|
||||
return channel.settings.module_settings.position_precision;
|
||||
}
|
||||
// No module settings: fail closed. A PRIMARY channel used to default to 32
|
||||
// here, leaking an exact position on a sharing-disabled channel. See #10509.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex)
|
||||
{
|
||||
return getPositionPrecisionForChannel(channels.getByIndex(channelIndex));
|
||||
}
|
||||
|
||||
static int32_t truncateCoordinate(int32_t coordinate, uint32_t precision)
|
||||
{
|
||||
uint32_t coordinateBits = static_cast<uint32_t>(coordinate);
|
||||
uint32_t truncated = coordinateBits & (UINT32_MAX << (32 - precision));
|
||||
|
||||
// Use the middle of the possible location, not the low edge of the bucket.
|
||||
truncated += (1UL << (31 - precision));
|
||||
|
||||
return static_cast<int32_t>(truncated);
|
||||
}
|
||||
|
||||
void applyPositionPrecision(meshtastic_Position &position, uint32_t precision)
|
||||
{
|
||||
if (precision == 0) {
|
||||
uint32_t time = position.time;
|
||||
position = meshtastic_Position_init_default;
|
||||
position.time = time;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t effectivePrecision = precision > 32 ? 32 : precision;
|
||||
position.precision_bits = effectivePrecision;
|
||||
|
||||
if (effectivePrecision < 32) {
|
||||
position.latitude_i = truncateCoordinate(position.latitude_i, effectivePrecision);
|
||||
position.longitude_i = truncateCoordinate(position.longitude_i, effectivePrecision);
|
||||
}
|
||||
}
|
||||
|
||||
bool applyPositionPrecision(meshtastic_MeshPacket &packet, uint32_t precision)
|
||||
{
|
||||
if (packet.which_payload_variant != meshtastic_MeshPacket_decoded_tag ||
|
||||
packet.decoded.portnum != meshtastic_PortNum_POSITION_APP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
meshtastic_Position position = meshtastic_Position_init_default;
|
||||
if (!pb_decode_from_bytes(packet.decoded.payload.bytes, packet.decoded.payload.size, &meshtastic_Position_msg, &position)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
applyPositionPrecision(position, precision);
|
||||
packet.decoded.payload.size = pb_encode_to_bytes(packet.decoded.payload.bytes, sizeof(packet.decoded.payload.bytes),
|
||||
&meshtastic_Position_msg, &position);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool applyPositionPrecisionForChannel(meshtastic_MeshPacket &packet, uint8_t channelIndex)
|
||||
{
|
||||
if (packet.which_payload_variant != meshtastic_MeshPacket_decoded_tag ||
|
||||
packet.decoded.portnum != meshtastic_PortNum_POSITION_APP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return applyPositionPrecision(packet, getPositionPrecisionForChannel(channelIndex));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "meshtastic/channel.pb.h"
|
||||
#include "meshtastic/mesh.pb.h"
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(const meshtastic_Channel &channel);
|
||||
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex);
|
||||
void applyPositionPrecision(meshtastic_Position &position, uint32_t precision);
|
||||
bool applyPositionPrecision(meshtastic_MeshPacket &packet, uint32_t precision);
|
||||
bool applyPositionPrecisionForChannel(meshtastic_MeshPacket &packet, uint8_t channelIndex);
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PositionPrecision.h"
|
||||
#include "RTC.h"
|
||||
|
||||
#include "configuration.h"
|
||||
@@ -350,6 +351,15 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
}
|
||||
|
||||
fixPriority(p); // Before encryption, fix the priority if it's unset
|
||||
// Position precision is an originator-only privacy policy. Relays keep
|
||||
// p->from as the original sender, so do not rewrite their POSITION_APP payload.
|
||||
if (isFromUs(p)) {
|
||||
if (!applyPositionPrecisionForChannel(*p, p->channel)) {
|
||||
LOG_ERROR("Dropping malformed position packet before send");
|
||||
packetPool.release(p);
|
||||
return meshtastic_Routing_Error_BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
// If the packet is not yet encrypted, do so now
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
|
||||
@@ -52,8 +52,8 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
|
||||
#ifdef SX126X_POWER_EN // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly
|
||||
// used and not part of the 'default' set of pin definitions.
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#if HAS_LORA_FEM
|
||||
@@ -65,8 +65,8 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
#endif
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
digitalWrite(RF95_FAN_EN, HIGH);
|
||||
pinMode(RF95_FAN_EN, OUTPUT);
|
||||
digitalWrite(RF95_FAN_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
|
||||
@@ -15,6 +15,9 @@ PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent,
|
||||
PB_BIND(meshtastic_AdminMessage_OTAEvent, meshtastic_AdminMessage_OTAEvent, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_LockdownAuth, meshtastic_LockdownAuth, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -130,6 +130,41 @@ typedef struct _meshtastic_AdminMessage_OTAEvent {
|
||||
meshtastic_AdminMessage_OTAEvent_ota_hash_t ota_hash;
|
||||
} meshtastic_AdminMessage_OTAEvent;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_LockdownAuth_passphrase_t;
|
||||
/* Lockdown passphrase delivery payload.
|
||||
|
||||
One message handles three operations distinguished by content:
|
||||
- Provision (first-time): passphrase set, lock_now=false. Firmware
|
||||
generates DEK, wraps with passphrase-derived KEK, persists.
|
||||
- Unlock: passphrase set, lock_now=false. Firmware verifies
|
||||
passphrase against stored DEK, unlocks storage, authorizes the
|
||||
connection that delivered this packet.
|
||||
- Lock now: lock_now=true, passphrase ignored. Firmware revokes
|
||||
all client auth and reboots into the locked state.
|
||||
|
||||
Firmware decides between provision and unlock based on its own state
|
||||
(whether a DEK file already exists). Clients do not need to track
|
||||
which case applies. */
|
||||
typedef struct _meshtastic_LockdownAuth {
|
||||
/* Passphrase bytes (1-32). Empty when lock_now is true.
|
||||
Capped to 32 to match the proto cap on related security fields. */
|
||||
meshtastic_LockdownAuth_passphrase_t passphrase;
|
||||
/* Optional override of the boot-count token TTL granted on success.
|
||||
0 = use firmware default (TOKEN_DEFAULT_BOOTS).
|
||||
On reboot the firmware decrements this; when it reaches 0 the
|
||||
device boots fully locked and requires a fresh passphrase. */
|
||||
uint32_t boots_remaining;
|
||||
/* Optional wall-clock expiry for the unlock token, as absolute
|
||||
Unix-epoch seconds. 0 = no time limit (only the boot-count TTL
|
||||
applies). On boot, if the device RTC is set and now > this value,
|
||||
the token is treated as expired. */
|
||||
uint32_t valid_until_epoch;
|
||||
/* If true, ignore passphrase fields, immediately revoke all
|
||||
connection-level admin authorization, and reboot the device into
|
||||
the locked state. Always honoured regardless of current lock state. */
|
||||
bool lock_now;
|
||||
} meshtastic_LockdownAuth;
|
||||
|
||||
/* Parameters for setting up Meshtastic for ameteur radio usage */
|
||||
typedef struct _meshtastic_HamParameters {
|
||||
/* Amateur radio call sign, eg. KD2ABC */
|
||||
@@ -384,6 +419,15 @@ typedef struct _meshtastic_AdminMessage {
|
||||
meshtastic_AdminMessage_OTAEvent ota_request;
|
||||
/* Parameters and sensor configuration */
|
||||
meshtastic_SensorConfig sensor_config;
|
||||
/* Lockdown passphrase delivery / unlock / lock-now command for hardened
|
||||
firmware builds (see MESHTASTIC_LOCKDOWN). Used to provision the
|
||||
passphrase on first boot, unlock encrypted storage on subsequent
|
||||
reboots, re-verify on already-unlocked devices to authorize a new
|
||||
client connection, or immediately re-lock the device.
|
||||
|
||||
Replaces the earlier scheme that repurposed SecurityConfig.private_key
|
||||
to carry passphrase bytes; that hack is retired. */
|
||||
meshtastic_LockdownAuth lockdown_auth;
|
||||
};
|
||||
/* The node generates this key and sends it with any get_x_response packets.
|
||||
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
|
||||
@@ -429,6 +473,7 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
#define meshtastic_KeyVerificationAdmin_message_type_ENUMTYPE meshtastic_KeyVerificationAdmin_MessageType
|
||||
|
||||
|
||||
@@ -441,6 +486,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_default {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_LockdownAuth_init_default {{0, {0}}, 0, 0, 0}
|
||||
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||
#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0}
|
||||
@@ -453,6 +499,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_zero {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_LockdownAuth_init_zero {{0, {0}}, 0, 0, 0}
|
||||
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||
#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0}
|
||||
@@ -470,6 +517,10 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_InputEvent_touch_y_tag 4
|
||||
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_tag 1
|
||||
#define meshtastic_AdminMessage_OTAEvent_ota_hash_tag 2
|
||||
#define meshtastic_LockdownAuth_passphrase_tag 1
|
||||
#define meshtastic_LockdownAuth_boots_remaining_tag 2
|
||||
#define meshtastic_LockdownAuth_valid_until_epoch_tag 3
|
||||
#define meshtastic_LockdownAuth_lock_now_tag 4
|
||||
#define meshtastic_HamParameters_call_sign_tag 1
|
||||
#define meshtastic_HamParameters_tx_power_tag 2
|
||||
#define meshtastic_HamParameters_frequency_tag 3
|
||||
@@ -560,6 +611,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_nodedb_reset_tag 100
|
||||
#define meshtastic_AdminMessage_ota_request_tag 102
|
||||
#define meshtastic_AdminMessage_sensor_config_tag 103
|
||||
#define meshtastic_AdminMessage_lockdown_auth_tag 104
|
||||
#define meshtastic_AdminMessage_session_passkey_tag 101
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
@@ -621,7 +673,8 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \
|
||||
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config), 103)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config), 103) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lockdown_auth,lockdown_auth), 104)
|
||||
#define meshtastic_AdminMessage_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_DEFAULT NULL
|
||||
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
|
||||
@@ -644,6 +697,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config)
|
||||
#define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin
|
||||
#define meshtastic_AdminMessage_payload_variant_ota_request_MSGTYPE meshtastic_AdminMessage_OTAEvent
|
||||
#define meshtastic_AdminMessage_payload_variant_sensor_config_MSGTYPE meshtastic_SensorConfig
|
||||
#define meshtastic_AdminMessage_payload_variant_lockdown_auth_MSGTYPE meshtastic_LockdownAuth
|
||||
|
||||
#define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, event_code, 1) \
|
||||
@@ -659,6 +713,14 @@ X(a, STATIC, SINGULAR, BYTES, ota_hash, 2)
|
||||
#define meshtastic_AdminMessage_OTAEvent_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_OTAEvent_DEFAULT NULL
|
||||
|
||||
#define meshtastic_LockdownAuth_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BYTES, passphrase, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, boots_remaining, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, valid_until_epoch, 3) \
|
||||
X(a, STATIC, SINGULAR, BOOL, lock_now, 4)
|
||||
#define meshtastic_LockdownAuth_CALLBACK NULL
|
||||
#define meshtastic_LockdownAuth_DEFAULT NULL
|
||||
|
||||
#define meshtastic_HamParameters_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 2) \
|
||||
@@ -737,6 +799,7 @@ X(a, STATIC, OPTIONAL, UINT32, set_accuracy, 1)
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_OTAEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LockdownAuth_msg;
|
||||
extern const pb_msgdesc_t meshtastic_HamParameters_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
|
||||
extern const pb_msgdesc_t meshtastic_SharedContact_msg;
|
||||
@@ -751,6 +814,7 @@ extern const pb_msgdesc_t meshtastic_SHTXX_config_msg;
|
||||
#define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg
|
||||
#define meshtastic_AdminMessage_InputEvent_fields &meshtastic_AdminMessage_InputEvent_msg
|
||||
#define meshtastic_AdminMessage_OTAEvent_fields &meshtastic_AdminMessage_OTAEvent_msg
|
||||
#define meshtastic_LockdownAuth_fields &meshtastic_LockdownAuth_msg
|
||||
#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg
|
||||
#define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg
|
||||
@@ -768,6 +832,7 @@ extern const pb_msgdesc_t meshtastic_SHTXX_config_msg;
|
||||
#define meshtastic_AdminMessage_size 511
|
||||
#define meshtastic_HamParameters_size 31
|
||||
#define meshtastic_KeyVerificationAdmin_size 25
|
||||
#define meshtastic_LockdownAuth_size 48
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_size 496
|
||||
#define meshtastic_SCD30_config_size 27
|
||||
#define meshtastic_SCD4X_config_size 29
|
||||
|
||||
@@ -198,7 +198,9 @@ typedef enum _meshtastic_Config_DisplayConfig_OledType {
|
||||
/* Can not be auto detected but set by proto. Used for 128x64 screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107 = 3,
|
||||
/* Can not be auto detected but set by proto. Used for 128x128 screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128 = 4
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128 = 4,
|
||||
/* Can not be auto detected but set by proto. Used for 64x128 rotated screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED = 5
|
||||
} meshtastic_Config_DisplayConfig_OledType;
|
||||
|
||||
typedef enum _meshtastic_Config_DisplayConfig_DisplayMode {
|
||||
@@ -720,8 +722,8 @@ extern "C" {
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayUnits_ARRAYSIZE ((meshtastic_Config_DisplayConfig_DisplayUnits)(meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL+1))
|
||||
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MIN meshtastic_Config_DisplayConfig_OledType_OLED_AUTO
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MAX meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_ARRAYSIZE ((meshtastic_Config_DisplayConfig_OledType)(meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128+1))
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MAX meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_ARRAYSIZE ((meshtastic_Config_DisplayConfig_OledType)(meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED+1))
|
||||
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_MIN meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_MAX meshtastic_Config_DisplayConfig_DisplayMode_COLOR
|
||||
|
||||
@@ -57,6 +57,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
||||
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_LockdownStatus, meshtastic_LockdownStatus, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2)
|
||||
|
||||
|
||||
@@ -134,6 +137,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -321,6 +321,10 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_HELTEC_MESH_NODE_T1 = 133,
|
||||
/* B&Q Consulting Station G3: TBD */
|
||||
meshtastic_HardwareModel_STATION_G3 = 134,
|
||||
/* Lilygo T-Impulse-Plus */
|
||||
meshtastic_HardwareModel_T_IMPULSE_PLUS = 135,
|
||||
/* Lilygo T-Echo Card */
|
||||
meshtastic_HardwareModel_T_ECHO_CARD = 136,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -638,6 +642,25 @@ typedef enum _meshtastic_LogRecord_Level {
|
||||
meshtastic_LogRecord_Level_TRACE = 5
|
||||
} meshtastic_LogRecord_Level;
|
||||
|
||||
typedef enum _meshtastic_LockdownStatus_State {
|
||||
/* Default; should not be sent. */
|
||||
meshtastic_LockdownStatus_State_STATE_UNSPECIFIED = 0,
|
||||
/* No passphrase has ever been provisioned on this device.
|
||||
Client should prompt the operator to set one. */
|
||||
meshtastic_LockdownStatus_State_NEEDS_PROVISION = 1,
|
||||
/* Storage is locked or this client has not authenticated yet.
|
||||
lock_reason carries a machine-readable detail string.
|
||||
Client should present (or auto-replay) a passphrase via
|
||||
AdminMessage.lockdown_auth. */
|
||||
meshtastic_LockdownStatus_State_LOCKED = 2,
|
||||
/* Passphrase accepted; client is now authorized for this connection.
|
||||
boots_remaining and valid_until_epoch describe the active session
|
||||
token's TTL. */
|
||||
meshtastic_LockdownStatus_State_UNLOCKED = 3,
|
||||
/* Passphrase rejected. backoff_seconds is non-zero when rate-limited. */
|
||||
meshtastic_LockdownStatus_State_UNLOCK_FAILED = 4
|
||||
} meshtastic_LockdownStatus_State;
|
||||
|
||||
/* Struct definitions */
|
||||
/* A GPS Position */
|
||||
typedef struct _meshtastic_Position {
|
||||
@@ -1148,6 +1171,38 @@ typedef struct _meshtastic_QueueStatus {
|
||||
uint32_t mesh_packet_id;
|
||||
} meshtastic_QueueStatus;
|
||||
|
||||
/* Lockdown state report from firmware to client (for hardened builds
|
||||
with MESHTASTIC_LOCKDOWN). Sent immediately after config_complete_id
|
||||
to inform a freshly-connected unauthorized client what it must do,
|
||||
and again in response to each LockdownAuth admin command. */
|
||||
typedef struct _meshtastic_LockdownStatus {
|
||||
/* Current lockdown state being reported. */
|
||||
meshtastic_LockdownStatus_State state;
|
||||
/* For LOCKED: machine-readable reason. Known values:
|
||||
"needs_auth" — storage already unlocked, client must auth
|
||||
"token_missing" — no boot token on flash
|
||||
"token_expired" — boot token wall-clock TTL elapsed
|
||||
"token_boots_zero" — boot token boot-count TTL exhausted
|
||||
"token_hmac_fail" — token tampered or wrong device
|
||||
"token_dek_fail" — token DEK decrypt failed
|
||||
"token_wrong_size" — token file corrupted
|
||||
"token_bad_magic" — token file corrupted
|
||||
"not_provisioned" — should generally use NEEDS_PROVISION state instead
|
||||
Other values may be added; clients should treat unknown values as
|
||||
"locked, ask for passphrase". */
|
||||
char lock_reason[32];
|
||||
/* For UNLOCKED: remaining boots on the issued session token.
|
||||
Decrements by 1 on each subsequent boot. */
|
||||
uint32_t boots_remaining;
|
||||
/* For UNLOCKED: wall-clock expiry of the issued session token,
|
||||
absolute Unix-epoch seconds. 0 = no time limit. */
|
||||
uint32_t valid_until_epoch;
|
||||
/* For UNLOCK_FAILED: seconds the client must wait before another
|
||||
passphrase attempt will be accepted. 0 = wrong passphrase, no
|
||||
backoff (immediate retry allowed but advisable to prompt user). */
|
||||
uint32_t backoff_seconds;
|
||||
} meshtastic_LockdownStatus;
|
||||
|
||||
typedef struct _meshtastic_KeyVerificationNumberInform {
|
||||
uint64_t nonce;
|
||||
char remote_longname[40];
|
||||
@@ -1321,6 +1376,12 @@ typedef struct _meshtastic_FromRadio {
|
||||
meshtastic_ClientNotification clientNotification;
|
||||
/* Persistent data for device-ui */
|
||||
meshtastic_DeviceUIConfig deviceuiConfig;
|
||||
/* Lockdown state notification for hardened firmware builds.
|
||||
Sent post-config (so unauthorized clients learn they must
|
||||
provision/unlock) and after each LockdownAuth admin command
|
||||
to report success or failure. Replaces the earlier scheme of
|
||||
encoding state as magic-string prefixes inside ClientNotification. */
|
||||
meshtastic_LockdownStatus lockdown_status;
|
||||
};
|
||||
} meshtastic_FromRadio;
|
||||
|
||||
@@ -1462,6 +1523,10 @@ extern "C" {
|
||||
#define _meshtastic_LogRecord_Level_MAX meshtastic_LogRecord_Level_CRITICAL
|
||||
#define _meshtastic_LogRecord_Level_ARRAYSIZE ((meshtastic_LogRecord_Level)(meshtastic_LogRecord_Level_CRITICAL+1))
|
||||
|
||||
#define _meshtastic_LockdownStatus_State_MIN meshtastic_LockdownStatus_State_STATE_UNSPECIFIED
|
||||
#define _meshtastic_LockdownStatus_State_MAX meshtastic_LockdownStatus_State_UNLOCK_FAILED
|
||||
#define _meshtastic_LockdownStatus_State_ARRAYSIZE ((meshtastic_LockdownStatus_State)(meshtastic_LockdownStatus_State_UNLOCK_FAILED+1))
|
||||
|
||||
#define meshtastic_Position_location_source_ENUMTYPE meshtastic_Position_LocSource
|
||||
#define meshtastic_Position_altitude_source_ENUMTYPE meshtastic_Position_AltSource
|
||||
|
||||
@@ -1492,6 +1557,8 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
#define meshtastic_LockdownStatus_state_ENUMTYPE meshtastic_LockdownStatus_State
|
||||
|
||||
#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level
|
||||
|
||||
|
||||
@@ -1532,6 +1599,7 @@ extern "C" {
|
||||
#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}}
|
||||
#define meshtastic_LockdownStatus_init_default {_meshtastic_LockdownStatus_State_MIN, "", 0, 0, 0}
|
||||
#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, "", 0, {meshtastic_KeyVerificationNumberInform_init_default}}
|
||||
#define meshtastic_KeyVerificationNumberInform_init_default {0, "", 0}
|
||||
#define meshtastic_KeyVerificationNumberRequest_init_default {0, ""}
|
||||
@@ -1566,6 +1634,7 @@ extern "C" {
|
||||
#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}}
|
||||
#define meshtastic_LockdownStatus_init_zero {_meshtastic_LockdownStatus_State_MIN, "", 0, 0, 0}
|
||||
#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, "", 0, {meshtastic_KeyVerificationNumberInform_init_zero}}
|
||||
#define meshtastic_KeyVerificationNumberInform_init_zero {0, "", 0}
|
||||
#define meshtastic_KeyVerificationNumberRequest_init_zero {0, ""}
|
||||
@@ -1718,6 +1787,11 @@ extern "C" {
|
||||
#define meshtastic_QueueStatus_free_tag 2
|
||||
#define meshtastic_QueueStatus_maxlen_tag 3
|
||||
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
|
||||
#define meshtastic_LockdownStatus_state_tag 1
|
||||
#define meshtastic_LockdownStatus_lock_reason_tag 2
|
||||
#define meshtastic_LockdownStatus_boots_remaining_tag 3
|
||||
#define meshtastic_LockdownStatus_valid_until_epoch_tag 4
|
||||
#define meshtastic_LockdownStatus_backoff_seconds_tag 5
|
||||
#define meshtastic_KeyVerificationNumberInform_nonce_tag 1
|
||||
#define meshtastic_KeyVerificationNumberInform_remote_longname_tag 2
|
||||
#define meshtastic_KeyVerificationNumberInform_security_number_tag 3
|
||||
@@ -1777,6 +1851,7 @@ extern "C" {
|
||||
#define meshtastic_FromRadio_fileInfo_tag 15
|
||||
#define meshtastic_FromRadio_clientNotification_tag 16
|
||||
#define meshtastic_FromRadio_deviceuiConfig_tag 17
|
||||
#define meshtastic_FromRadio_lockdown_status_tag 18
|
||||
#define meshtastic_Heartbeat_nonce_tag 1
|
||||
#define meshtastic_ToRadio_packet_tag 1
|
||||
#define meshtastic_ToRadio_want_config_id_tag 3
|
||||
@@ -2017,7 +2092,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfig), 17)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfig), 17) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lockdown_status,lockdown_status), 18)
|
||||
#define meshtastic_FromRadio_CALLBACK NULL
|
||||
#define meshtastic_FromRadio_DEFAULT NULL
|
||||
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
|
||||
@@ -2034,6 +2110,16 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfi
|
||||
#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
|
||||
#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification
|
||||
#define meshtastic_FromRadio_payload_variant_deviceuiConfig_MSGTYPE meshtastic_DeviceUIConfig
|
||||
#define meshtastic_FromRadio_payload_variant_lockdown_status_MSGTYPE meshtastic_LockdownStatus
|
||||
|
||||
#define meshtastic_LockdownStatus_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, state, 1) \
|
||||
X(a, STATIC, SINGULAR, STRING, lock_reason, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, boots_remaining, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, valid_until_epoch, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, backoff_seconds, 5)
|
||||
#define meshtastic_LockdownStatus_CALLBACK NULL
|
||||
#define meshtastic_LockdownStatus_DEFAULT NULL
|
||||
|
||||
#define meshtastic_ClientNotification_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \
|
||||
@@ -2194,6 +2280,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LogRecord_msg;
|
||||
extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_FromRadio_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LockdownStatus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ClientNotification_msg;
|
||||
extern const pb_msgdesc_t meshtastic_KeyVerificationNumberInform_msg;
|
||||
extern const pb_msgdesc_t meshtastic_KeyVerificationNumberRequest_msg;
|
||||
@@ -2230,6 +2317,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
|
||||
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
|
||||
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
|
||||
#define meshtastic_LockdownStatus_fields &meshtastic_LockdownStatus_msg
|
||||
#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg
|
||||
#define meshtastic_KeyVerificationNumberInform_fields &meshtastic_KeyVerificationNumberInform_msg
|
||||
#define meshtastic_KeyVerificationNumberRequest_fields &meshtastic_KeyVerificationNumberRequest_msg
|
||||
@@ -2265,6 +2353,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_KeyVerificationNumberInform_size 58
|
||||
#define meshtastic_KeyVerificationNumberRequest_size 52
|
||||
#define meshtastic_KeyVerification_size 79
|
||||
#define meshtastic_LockdownStatus_size 53
|
||||
#define meshtastic_LogRecord_size 426
|
||||
#define meshtastic_LowEntropyKey_size 0
|
||||
#define meshtastic_MeshPacket_size 381
|
||||
|
||||
@@ -144,7 +144,7 @@ typedef struct _meshtastic_ModuleConfig_MQTTConfig {
|
||||
(the default official mqtt.meshtastic.org server can handle encrypted packets)
|
||||
Decrypted packets may be useful for external systems that want to consume meshtastic packets */
|
||||
bool encryption_enabled;
|
||||
/* Whether to send / consume json packets on MQTT */
|
||||
/* Deprecated: JSON packet support on MQTT was removed, and this field is ignored. */
|
||||
bool json_enabled;
|
||||
/* If true, we attempt to establish a secure connection using TLS */
|
||||
bool tls_enabled;
|
||||
|
||||
@@ -987,11 +987,11 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
|
||||
case meshtastic_ModuleConfig_neighbor_info_tag:
|
||||
LOG_INFO("Set module config: Neighbor Info");
|
||||
moduleConfig.has_neighbor_info = true;
|
||||
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
|
||||
if (moduleConfig.neighbor_info.update_interval < min_neighbor_info_broadcast_secs) {
|
||||
LOG_DEBUG("Tried to set update_interval too low, setting to %d", default_neighbor_info_broadcast_secs);
|
||||
moduleConfig.neighbor_info.update_interval = default_neighbor_info_broadcast_secs;
|
||||
}
|
||||
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_detection_sensor_tag:
|
||||
LOG_INFO("Set module config: Detection Sensor");
|
||||
|
||||
@@ -206,6 +206,10 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
||||
#ifdef PCA_LED_NOTIFICATION
|
||||
io.digitalWrite(PCA_LED_NOTIFICATION, on);
|
||||
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
notificationPixel.setPixelColor(0, on ? NEOPIXEL_STATUS_NOTIFICATION_COLOR : 0);
|
||||
notificationPixel.show();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -324,6 +328,12 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
LOG_INFO("Use Pin %i in digital mode", output);
|
||||
pinMode(output, OUTPUT);
|
||||
}
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
LOG_INFO("Use WS2812 on GPIO %d as notification LED", NEOPIXEL_STATUS_NOTIFICATION_PIN);
|
||||
notificationPixel.begin();
|
||||
notificationPixel.clear();
|
||||
notificationPixel.show();
|
||||
#endif
|
||||
setExternalState(0, false);
|
||||
externalTurnedOn[0] = 0;
|
||||
if (moduleConfig.external_notification.output_vibra) {
|
||||
|
||||
@@ -10,6 +10,19 @@
|
||||
extern AmbientLightingThread *ambientLightingThread;
|
||||
#endif
|
||||
|
||||
// Drive a single WS2812 as the notification LED (M1/M2-style LED_NOTIFICATION
|
||||
// but addressable). A variant defines NEOPIXEL_STATUS_NOTIFICATION_PIN to
|
||||
// enable. Colour defaults to green but can be overridden.
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#ifndef NEOPIXEL_STATUS_TYPE
|
||||
#define NEOPIXEL_STATUS_TYPE (NEO_GRB + NEO_KHZ800)
|
||||
#endif
|
||||
#ifndef NEOPIXEL_STATUS_NOTIFICATION_COLOR
|
||||
#define NEOPIXEL_STATUS_NOTIFICATION_COLOR 0x00FF00 // green
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
#include <NonBlockingRtttl.h>
|
||||
#else
|
||||
@@ -38,6 +51,10 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
|
||||
CallbackObserver<ExternalNotificationModule, const InputEvent *>(this, &ExternalNotificationModule::handleInputEvent);
|
||||
uint32_t output = 0;
|
||||
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
Adafruit_NeoPixel notificationPixel = Adafruit_NeoPixel(1, NEOPIXEL_STATUS_NOTIFICATION_PIN, NEOPIXEL_STATUS_TYPE);
|
||||
#endif
|
||||
|
||||
public:
|
||||
ExternalNotificationModule();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PositionPrecision.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "TransmitHistory.h"
|
||||
@@ -107,13 +108,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
}
|
||||
|
||||
nodeDB->updatePosition(getFrom(&mp), p);
|
||||
if (channels.getByIndex(mp.channel).settings.has_module_settings) {
|
||||
precision = channels.getByIndex(mp.channel).settings.module_settings.position_precision;
|
||||
} else if (channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
||||
precision = 32;
|
||||
} else {
|
||||
precision = 0;
|
||||
}
|
||||
precision = getPositionPrecisionForChannel(mp.channel);
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
@@ -121,15 +116,12 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_Position *p)
|
||||
{
|
||||
// Phone position packets need to be truncated to the channel precision
|
||||
if (isFromUs(&mp) && (precision < 32 && precision > 0)) {
|
||||
LOG_DEBUG("Truncate phone position to channel precision %i", precision);
|
||||
p->latitude_i = p->latitude_i & (UINT32_MAX << (32 - precision));
|
||||
p->longitude_i = p->longitude_i & (UINT32_MAX << (32 - precision));
|
||||
|
||||
// We want the imprecise position to be the middle of the possible location, not
|
||||
p->latitude_i += (1 << (31 - precision));
|
||||
p->longitude_i += (1 << (31 - precision));
|
||||
|
||||
if (isFromUs(&mp)) {
|
||||
if (precision == 0)
|
||||
LOG_DEBUG("Strip phone position due to channel precision 0");
|
||||
else if (precision < 32)
|
||||
LOG_DEBUG("Truncate phone position to channel precision %i", precision);
|
||||
applyPositionPrecision(*p, precision);
|
||||
mp.decoded.payload.size =
|
||||
pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes), &meshtastic_Position_msg, p);
|
||||
}
|
||||
@@ -205,20 +197,11 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
|
||||
|
||||
// lat/lon are unconditionally included - IF AVAILABLE!
|
||||
LOG_DEBUG("Send location with precision %i", precision);
|
||||
if (precision < 32 && precision > 0) {
|
||||
p.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - precision));
|
||||
p.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - precision));
|
||||
|
||||
// We want the imprecise position to be the middle of the possible location, not
|
||||
p.latitude_i += (1 << (31 - precision));
|
||||
p.longitude_i += (1 << (31 - precision));
|
||||
} else {
|
||||
p.latitude_i = localPosition.latitude_i;
|
||||
p.longitude_i = localPosition.longitude_i;
|
||||
}
|
||||
p.precision_bits = precision;
|
||||
p.latitude_i = localPosition.latitude_i;
|
||||
p.longitude_i = localPosition.longitude_i;
|
||||
p.has_latitude_i = true;
|
||||
p.has_longitude_i = true;
|
||||
applyPositionPrecision(p, precision);
|
||||
// Always use NTP / GPS time if available
|
||||
if (getValidTime(RTCQualityNTP) > 0) {
|
||||
p.time = getValidTime(RTCQualityNTP);
|
||||
@@ -349,8 +332,7 @@ void PositionModule::sendOurPosition()
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
LOG_INFO("Send pos@%x:6 to mesh (wantReplies=%d)", localPosition.timestamp, requestReplies);
|
||||
for (uint8_t channelNum = 0; channelNum < 8; channelNum++) {
|
||||
if (channels.getByIndex(channelNum).settings.has_module_settings &&
|
||||
channels.getByIndex(channelNum).settings.module_settings.position_precision != 0) {
|
||||
if (getPositionPrecisionForChannel(channelNum) != 0) {
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies, channelNum);
|
||||
return;
|
||||
}
|
||||
@@ -368,10 +350,8 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service->cancelSending(prevPacketId);
|
||||
|
||||
// Set's the class precision value for this particular packet
|
||||
if (channels.getByIndex(channel).settings.has_module_settings) {
|
||||
precision = channels.getByIndex(channel).settings.module_settings.position_precision;
|
||||
}
|
||||
// Set the class precision value for this particular packet.
|
||||
precision = getPositionPrecisionForChannel(channel);
|
||||
|
||||
meshtastic_MeshPacket *p = allocPositionPacket();
|
||||
if (p == nullptr) {
|
||||
|
||||
@@ -17,8 +17,29 @@ StatusLEDModule::StatusLEDModule() : concurrency::OSThread("StatusLEDModule")
|
||||
if (inputBroker)
|
||||
inputObserver.observe(inputBroker);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_POWER_PIN
|
||||
powerPixel.begin();
|
||||
powerPixel.clear();
|
||||
powerPixel.show();
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_PAIRING_PIN
|
||||
pairingPixel.begin();
|
||||
pairingPixel.clear();
|
||||
pairingPixel.show();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper: write a 1-pixel NeoPixel strand to `color` when stateOn, else clear.
|
||||
// Kept as a static inline here (rather than a member) so it compiles out
|
||||
// completely when no NeoPixel status pins are defined.
|
||||
#if defined(NEOPIXEL_STATUS_POWER_PIN) || defined(NEOPIXEL_STATUS_PAIRING_PIN)
|
||||
static inline void writeStatusPixel(Adafruit_NeoPixel &pixel, uint32_t color, bool stateOn)
|
||||
{
|
||||
pixel.setPixelColor(0, stateOn ? color : 0);
|
||||
pixel.show();
|
||||
}
|
||||
#endif
|
||||
|
||||
int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
{
|
||||
switch (arg->getStatusType()) {
|
||||
@@ -176,6 +197,12 @@ int32_t StatusLEDModule::runOnce()
|
||||
#ifdef LED_PAIRING
|
||||
digitalWrite(LED_PAIRING, PAIRING_LED_state);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_POWER_PIN
|
||||
writeStatusPixel(powerPixel, NEOPIXEL_STATUS_POWER_COLOR, CHARGE_LED_state == LED_STATE_ON);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_PAIRING_PIN
|
||||
writeStatusPixel(pairingPixel, NEOPIXEL_STATUS_PAIRING_COLOR, PAIRING_LED_state == LED_STATE_ON);
|
||||
#endif
|
||||
|
||||
#ifdef RGB_LED_POWER
|
||||
if (!config.device.led_heartbeat_disabled) {
|
||||
@@ -225,6 +252,12 @@ void StatusLEDModule::setPowerLED(bool LEDon)
|
||||
#ifdef LED_PAIRING
|
||||
digitalWrite(LED_PAIRING, ledState);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_POWER_PIN
|
||||
writeStatusPixel(powerPixel, NEOPIXEL_STATUS_POWER_COLOR, LEDon);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_PAIRING_PIN
|
||||
writeStatusPixel(pairingPixel, NEOPIXEL_STATUS_PAIRING_COLOR, LEDon);
|
||||
#endif
|
||||
|
||||
#ifdef Battery_LED_1
|
||||
digitalWrite(Battery_LED_1, ledState);
|
||||
|
||||
@@ -13,6 +13,25 @@
|
||||
#include "input/InputBroker.h"
|
||||
#endif
|
||||
|
||||
// WS2812/NeoPixel status-LED support. A variant may define
|
||||
// NEOPIXEL_STATUS_POWER_PIN (required to enable the power/charge pixel)
|
||||
// NEOPIXEL_STATUS_POWER_COLOR (optional, default red 0xFF0000)
|
||||
// NEOPIXEL_STATUS_PAIRING_PIN / _COLOR (default blue 0x0000FF)
|
||||
// Each pixel is a standalone 1-LED strand on its own GPIO — this mirrors how
|
||||
// boards like the LilyGo T-Echo-Card expose three independent WS2812s.
|
||||
#if defined(NEOPIXEL_STATUS_POWER_PIN) || defined(NEOPIXEL_STATUS_PAIRING_PIN)
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#ifndef NEOPIXEL_STATUS_TYPE
|
||||
#define NEOPIXEL_STATUS_TYPE (NEO_GRB + NEO_KHZ800)
|
||||
#endif
|
||||
#ifndef NEOPIXEL_STATUS_POWER_COLOR
|
||||
#define NEOPIXEL_STATUS_POWER_COLOR 0xFF0000 // red
|
||||
#endif
|
||||
#ifndef NEOPIXEL_STATUS_PAIRING_COLOR
|
||||
#define NEOPIXEL_STATUS_PAIRING_COLOR 0x0000FF // blue
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class StatusLEDModule : private concurrency::OSThread
|
||||
{
|
||||
bool slowTrack = false;
|
||||
@@ -27,6 +46,13 @@ class StatusLEDModule : private concurrency::OSThread
|
||||
|
||||
void setPowerLED(bool);
|
||||
|
||||
#ifdef NEOPIXEL_STATUS_POWER_PIN
|
||||
Adafruit_NeoPixel powerPixel = Adafruit_NeoPixel(1, NEOPIXEL_STATUS_POWER_PIN, NEOPIXEL_STATUS_TYPE);
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_PAIRING_PIN
|
||||
Adafruit_NeoPixel pairingPixel = Adafruit_NeoPixel(1, NEOPIXEL_STATUS_PAIRING_PIN, NEOPIXEL_STATUS_TYPE);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
unsigned int my_interval = 1000; // interval in millisconds
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
@@ -610,7 +610,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
display->setFont(FONT_SMALL);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
||||
|
||||
+55
-1
@@ -1,13 +1,65 @@
|
||||
#include "AudioBoard.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef M5STACK_CARDPUTER_ADV
|
||||
|
||||
#include "AudioBoard.h"
|
||||
#include <Wire.h>
|
||||
|
||||
DriverPins PinsAudioBoardES8311;
|
||||
AudioBoard board(AudioDriverES8311, PinsAudioBoardES8311);
|
||||
|
||||
// PI4IOE5V6408 on the optional Cap LoRa-1262 (and Cap LoRa868).
|
||||
#define PI4IO_ADDR 0x43
|
||||
#define PI4IO_REG_IO_DIR 0x03
|
||||
#define PI4IO_REG_OUT_SET 0x05
|
||||
#define PI4IO_REG_OUT_H_IM 0x07
|
||||
|
||||
static TwoWire *findLoraCapBus()
|
||||
{
|
||||
TwoWire *candidates[] = {&Wire1, &Wire};
|
||||
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
|
||||
candidates[i]->beginTransmission(PI4IO_ADDR);
|
||||
if (candidates[i]->endTransmission() == 0) {
|
||||
return candidates[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool pi4ioWrite(TwoWire *bus, uint8_t reg, uint8_t val)
|
||||
{
|
||||
bus->beginTransmission(PI4IO_ADDR);
|
||||
bus->write(reg);
|
||||
bus->write(val);
|
||||
uint8_t status = bus->endTransmission();
|
||||
if (status != 0) {
|
||||
LOG_DEBUG("PI4IO write reg=0x%02x val=0x%02x failed, I2C status=%u", reg, val, status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void initLoraCap()
|
||||
{
|
||||
TwoWire *bus = findLoraCapBus();
|
||||
if (!bus) {
|
||||
LOG_ERROR("Cap LoRa-1262 not found");
|
||||
return;
|
||||
}
|
||||
bool ok = pi4ioWrite(bus, PI4IO_REG_IO_DIR, 0b00000001);
|
||||
ok = ok && pi4ioWrite(bus, PI4IO_REG_OUT_H_IM, 0b00000000);
|
||||
ok = ok && pi4ioWrite(bus, PI4IO_REG_OUT_SET, 0b00000001);
|
||||
if (!ok) {
|
||||
LOG_ERROR("Antenna switch init failed");
|
||||
}
|
||||
}
|
||||
|
||||
// M5stack Cardputer ADV specific init
|
||||
|
||||
void lateInitVariant()
|
||||
{
|
||||
initLoraCap();
|
||||
|
||||
// AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug);
|
||||
// I2C: function, scl, sda
|
||||
PinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire);
|
||||
@@ -38,3 +90,5 @@ void lateInitVariant()
|
||||
es8311_write_reg(0x32, 0xBF); // DAC volume (0dB)
|
||||
es8311_write_reg(0x37, 0x08); // EQ bypass
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -85,6 +85,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO
|
||||
#elif defined(T_ECHO_LITE)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_LITE
|
||||
#elif defined(T_ECHO_CARD)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_CARD
|
||||
#elif defined(TTGO_T_ECHO_PLUS)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_PLUS
|
||||
#elif defined(ELECROW_ThinkNode_M1)
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "PositionPrecision.h"
|
||||
#include "TestUtil.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <unity.h>
|
||||
|
||||
static meshtastic_Position makePosition()
|
||||
{
|
||||
meshtastic_Position position = meshtastic_Position_init_default;
|
||||
position.has_latitude_i = true;
|
||||
position.latitude_i = static_cast<int32_t>(0x12345678);
|
||||
position.has_longitude_i = true;
|
||||
position.longitude_i = static_cast<int32_t>(0x22345678);
|
||||
position.has_altitude = true;
|
||||
position.altitude = 123;
|
||||
position.time = 42;
|
||||
position.location_source = meshtastic_Position_LocSource_LOC_EXTERNAL;
|
||||
position.timestamp = 43;
|
||||
position.sats_in_view = 10;
|
||||
return position;
|
||||
}
|
||||
|
||||
static meshtastic_Channel makeChannel(meshtastic_Channel_Role role, bool hasModuleSettings, uint32_t positionPrecision)
|
||||
{
|
||||
meshtastic_Channel channel = meshtastic_Channel_init_default;
|
||||
channel.has_settings = true;
|
||||
channel.role = role;
|
||||
channel.settings.has_module_settings = hasModuleSettings;
|
||||
channel.settings.module_settings.position_precision = positionPrecision;
|
||||
return channel;
|
||||
}
|
||||
|
||||
static void test_applyPositionPrecision_clampsLatLonAndSetsPrecisionBits()
|
||||
{
|
||||
meshtastic_Position position = makePosition();
|
||||
|
||||
applyPositionPrecision(position, 16);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x12348000), position.latitude_i);
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x22348000), position.longitude_i);
|
||||
TEST_ASSERT_EQUAL_UINT32(16, position.precision_bits);
|
||||
TEST_ASSERT_TRUE(position.has_latitude_i);
|
||||
TEST_ASSERT_TRUE(position.has_longitude_i);
|
||||
}
|
||||
|
||||
static void test_applyPositionPrecision_fullPrecisionKeepsLatLon()
|
||||
{
|
||||
meshtastic_Position position = makePosition();
|
||||
|
||||
applyPositionPrecision(position, 32);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x12345678), position.latitude_i);
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x22345678), position.longitude_i);
|
||||
TEST_ASSERT_EQUAL_UINT32(32, position.precision_bits);
|
||||
}
|
||||
|
||||
static void test_applyPositionPrecision_zeroScrubsLocationButKeepsTime()
|
||||
{
|
||||
meshtastic_Position position = makePosition();
|
||||
|
||||
applyPositionPrecision(position, 0);
|
||||
|
||||
TEST_ASSERT_FALSE(position.has_latitude_i);
|
||||
TEST_ASSERT_EQUAL_INT32(0, position.latitude_i);
|
||||
TEST_ASSERT_FALSE(position.has_longitude_i);
|
||||
TEST_ASSERT_EQUAL_INT32(0, position.longitude_i);
|
||||
TEST_ASSERT_FALSE(position.has_altitude);
|
||||
TEST_ASSERT_EQUAL_INT32(0, position.altitude);
|
||||
TEST_ASSERT_EQUAL_UINT32(42, position.time);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, position.timestamp);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, position.sats_in_view);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, position.precision_bits);
|
||||
}
|
||||
|
||||
static void test_applyPositionPrecision_reencodesPositionPacket()
|
||||
{
|
||||
meshtastic_Position position = makePosition();
|
||||
meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_default;
|
||||
packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
packet.decoded.portnum = meshtastic_PortNum_POSITION_APP;
|
||||
packet.decoded.payload.size = pb_encode_to_bytes(packet.decoded.payload.bytes, sizeof(packet.decoded.payload.bytes),
|
||||
&meshtastic_Position_msg, &position);
|
||||
|
||||
TEST_ASSERT_TRUE(applyPositionPrecision(packet, 16));
|
||||
|
||||
meshtastic_Position decoded = meshtastic_Position_init_default;
|
||||
TEST_ASSERT_TRUE(
|
||||
pb_decode_from_bytes(packet.decoded.payload.bytes, packet.decoded.payload.size, &meshtastic_Position_msg, &decoded));
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x12348000), decoded.latitude_i);
|
||||
TEST_ASSERT_EQUAL_INT32(static_cast<int32_t>(0x22348000), decoded.longitude_i);
|
||||
TEST_ASSERT_EQUAL_UINT32(16, decoded.precision_bits);
|
||||
}
|
||||
|
||||
static void test_getPositionPrecisionForChannel_explicitPrecisionIsHonored()
|
||||
{
|
||||
meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, true, 16);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(16, getPositionPrecisionForChannel(channel));
|
||||
}
|
||||
|
||||
static void test_getPositionPrecisionForChannel_explicitZeroDisablesPrimary()
|
||||
{
|
||||
meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, true, 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel));
|
||||
}
|
||||
|
||||
static void test_getPositionPrecisionForChannel_primaryWithoutModuleSettingsFailsClosed()
|
||||
{
|
||||
// Regression guard for #10509: precision 32 below must be ignored (no module settings).
|
||||
meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_PRIMARY, false, 32);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel));
|
||||
}
|
||||
|
||||
static void test_getPositionPrecisionForChannel_secondaryWithoutModuleSettingsFailsClosed()
|
||||
{
|
||||
meshtastic_Channel channel = makeChannel(meshtastic_Channel_Role_SECONDARY, false, 32);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(0, getPositionPrecisionForChannel(channel));
|
||||
}
|
||||
|
||||
void setUp(void) {}
|
||||
|
||||
void tearDown(void) {}
|
||||
|
||||
extern "C" {
|
||||
void setup()
|
||||
{
|
||||
initializeTestEnvironment();
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_applyPositionPrecision_clampsLatLonAndSetsPrecisionBits);
|
||||
RUN_TEST(test_applyPositionPrecision_fullPrecisionKeepsLatLon);
|
||||
RUN_TEST(test_applyPositionPrecision_zeroScrubsLocationButKeepsTime);
|
||||
RUN_TEST(test_applyPositionPrecision_reencodesPositionPacket);
|
||||
RUN_TEST(test_getPositionPrecisionForChannel_explicitPrecisionIsHonored);
|
||||
RUN_TEST(test_getPositionPrecisionForChannel_explicitZeroDisablesPrimary);
|
||||
RUN_TEST(test_getPositionPrecisionForChannel_primaryWithoutModuleSettingsFailsClosed);
|
||||
RUN_TEST(test_getPositionPrecisionForChannel_secondaryWithoutModuleSettingsFailsClosed);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@1.4.3
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
https://github.com/dbinfrago/libpax/archive/df424747f9acb86ab07c5a206ded1e8e3650726a.zip
|
||||
https://github.com/dbinfrago/libpax/archive/17302340f100efbc4bee5022ecc72047d6d93ed4.zip
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.3
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
|
||||
@@ -52,23 +52,25 @@ void c6l_init()
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_CHIP_RESET, &in_data);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_IO_DIR, 0b11000000); // 0: input 1: output
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_IO_DIR, 0b11100000); // P5,P6,P7 as outputs
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_H_IM, 0b00111100); // 使用到的引脚关闭High-Impedance
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_H_IM, 0b00011100); // High-Impedance
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_PULL_SEL, 0b11000011); // pull up/down select, 0 down, 1 up
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_PULL_SEL, 0b11100011); // pull up/down select, 0 down, 1 up
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_PULL_EN, 0b11000011); // pull up/down enable, 0 disable, 1 enable
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_PULL_EN, 0b11100011); // pull up/down enable, 0 disable, 1 enable
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_IN_DEF_STA, 0b00000011); // P0 P1 默认高电平, 按键按下触发中断
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_IN_DEF_STA, 0b00000011); // P0 P1 default to high level; button press triggers the interrupt
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_INT_MASK, 0b11111100); // P0 P1 中断使能 0 enable, 1 disable
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_INT_MASK, 0b11111100); // P0 P1 interrupts enabled (0 = enable, 1 = disable)
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_SET, 0b10000000); // 默认输出为0
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_SET, 0b10000000); // default output is 0
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_IRQ_STA, &in_data); // read IRQ_STA to clear the flag
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_IRQ_STA, &in_data); // 读取IRQ_STA清除标志
|
||||
|
||||
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_SET, &in_data);
|
||||
setbit(in_data, 6); // HIGH
|
||||
setbit(in_data, 6); // enable SX_ANT_SW
|
||||
setbit(in_data, 5); // enable SX_LNA_EN
|
||||
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_OUT_SET, in_data);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ void c6l_init();
|
||||
#define SSD1306_RESET 15
|
||||
// #define OLED_DG 1
|
||||
#endif
|
||||
// Tiny OLED panel — opts into compile-time layout/font/feature substitutions
|
||||
// gated on OLED_TINY across the graphics stack.
|
||||
#define OLED_TINY
|
||||
#define SCREEN_TRANSITION_FRAMERATE 10
|
||||
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
|
||||
|
||||
|
||||
@@ -21,4 +21,4 @@ build_src_filter =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=github-tags depName=ESP32-CH390 packageName=meshtastic/ESP32-CH390
|
||||
https://github.com/meshtastic/ESP32-CH390/archive/refs/tags/v1.0.1.zip
|
||||
https://github.com/meshtastic/ESP32-CH390/archive/v1.1.0.zip
|
||||
|
||||
@@ -20,4 +20,4 @@ build_src_filter =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=github-tags depName=ESP32-CH390 packageName=meshtastic/ESP32-CH390
|
||||
https://github.com/meshtastic/ESP32-CH390/archive/refs/tags/v1.0.1.zip
|
||||
https://github.com/meshtastic/ESP32-CH390/archive/v1.1.0.zip
|
||||
|
||||
@@ -10,9 +10,6 @@ build_flags =
|
||||
-D M5STACK_CARDPUTER_ADV
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-I variants/esp32s3/m5stack_cardputer_adv
|
||||
build_src_filter =
|
||||
${esp32s3_base.build_src_filter}
|
||||
+<../variants/esp32s3/m5stack_cardputer_adv>
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-st7789 packageName=https://github.com/meshtastic/st7789 gitBranch=main
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define ST7789_BUSY -1
|
||||
// #define VTFT_CTRL 38
|
||||
#define VTFT_LEDA 38
|
||||
#define TFT_BACKLIGHT_ON HIGH
|
||||
// #define ST7789_BL (32+6)
|
||||
#define ST7789_SPI_HOST SPI2_HOST
|
||||
// #define TFT_BL (32+6)
|
||||
|
||||
@@ -29,7 +29,7 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.21
|
||||
; # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
|
||||
https://github.com/pine64/libch341-spi-userspace/archive/23c42319a69cffcb65868e3c72e6bed83974a393.zip
|
||||
https://github.com/pine64/libch341-spi-userspace/archive/2e5ff751d0c39667993df672cb683740ed5c9394.zip
|
||||
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
|
||||
adafruit/Adafruit seesaw Library@1.7.9
|
||||
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[env:t-echo-card]
|
||||
extends = nrf52840_base
|
||||
board = t-echo
|
||||
board_level = extra
|
||||
debug_tool = jlink
|
||||
|
||||
build_flags = ${nrf52840_base.build_flags}
|
||||
-I variants/nrf52840/t-echo-card
|
||||
-D T_ECHO_CARD
|
||||
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-echo-card>
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Sandeep Mistry All right reserved.
|
||||
Copyright (c) 2018, Adafruit Industries (adafruit.com)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "variant.h"
|
||||
#include "Arduino.h"
|
||||
#include "nrf.h"
|
||||
#include "wiring_constants.h"
|
||||
#include "wiring_digital.h"
|
||||
|
||||
const uint32_t g_ADigitalPinMap[] = {
|
||||
// P0 - pins 0 and 1 are hardwired for xtal and should never be enabled
|
||||
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
|
||||
// P1
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
|
||||
|
||||
void initVariant()
|
||||
{
|
||||
// No plain GPIO LEDs on this board (only WS2812 addressable LEDs, not driven here).
|
||||
}
|
||||
|
||||
// Reproduces the vendor firmware's boot sequence from
|
||||
// examples/original_test/original_test.ino. Runs before Meshtastic touches
|
||||
// PIN_POWER_EN, so the RT9080 LDO gets a clean reset pulse and peripherals
|
||||
// whose EN pins must be LOW at boot (GPS_EN, GPS_RF_EN, BUZZER) aren't left
|
||||
// floating while the 3V3 rail is ramping.
|
||||
void earlyInitVariant()
|
||||
{
|
||||
// 3.3V rail: toggle RT9080_EN HIGH → LOW → HIGH with 100 ms dwell so the
|
||||
// LDO enters enable from a known state. The single-shot HIGH in main.cpp
|
||||
// is not enough on this hardware — if the chip was in a half-enabled
|
||||
// state from a previous reset, the rail brown-outs once LoRa TX fires.
|
||||
pinMode(PIN_POWER_EN, OUTPUT);
|
||||
digitalWrite(PIN_POWER_EN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(PIN_POWER_EN, LOW);
|
||||
delay(100);
|
||||
digitalWrite(PIN_POWER_EN, HIGH);
|
||||
delay(100);
|
||||
|
||||
// Park peripherals with active-high enables LOW so they don't sink
|
||||
// current while the rest of setup() runs.
|
||||
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
||||
digitalWrite(PIN_GPS_STANDBY, LOW);
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
digitalWrite(PIN_GPS_RESET, LOW);
|
||||
pinMode(PIN_BUZZER, OUTPUT);
|
||||
digitalWrite(PIN_BUZZER, LOW);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
// Variant definition for LilyGo T-Echo-Card (nRF52840)
|
||||
|
||||
#ifndef _VARIANT_T_ECHO_CARD_
|
||||
#define _VARIANT_T_ECHO_CARD_
|
||||
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
#define USE_LFXO // Board uses 32kHz crystal for LF
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Number of pins defined in PinDescription array
|
||||
#define PINS_COUNT (48)
|
||||
#define NUM_DIGITAL_PINS (48)
|
||||
#define NUM_ANALOG_INPUTS (1)
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
// LEDs - board only exposes 3x WS2812 addressable LEDs. No plain GPIO LEDs.
|
||||
// Intentionally do not define PIN_LED1 on this variant, so nRF52 platform
|
||||
// code does not auto-enable a nonexistent GPIO power/status LED.
|
||||
#define LED_STATE_ON 1
|
||||
|
||||
// Three independent WS2812 data lines (one LED per line, not a daisy chain).
|
||||
// Each is driven as a 1-pixel NeoPixel by StatusLEDModule / ExternalNotification,
|
||||
// assigns LED_POWER (red) and LED_NOTIFICATION (green).
|
||||
#define WS2812_DATA_1 (32 + 7) // P1.7 - charge/heartbeat (red)
|
||||
#define WS2812_DATA_2 (32 + 12) // P1.12 - external notification (green)
|
||||
#define WS2812_DATA_3 (0 + 28) // P0.28 - BLE pairing (blue)
|
||||
|
||||
// Wire each WS2812 to a status role. Colour defaults are scaled to 25%
|
||||
// brightness (0x40) — the bare-die WS2812s on this board are very bright at
|
||||
// full intensity in a close-range enclosure.
|
||||
#define NEOPIXEL_STATUS_POWER_PIN WS2812_DATA_1
|
||||
#define NEOPIXEL_STATUS_NOTIFICATION_PIN WS2812_DATA_2
|
||||
#define NEOPIXEL_STATUS_PAIRING_PIN WS2812_DATA_3
|
||||
#define NEOPIXEL_STATUS_POWER_COLOR 0x400000 // red @ 25%
|
||||
#define NEOPIXEL_STATUS_NOTIFICATION_COLOR 0x004000 // green @ 25%
|
||||
#define NEOPIXEL_STATUS_PAIRING_COLOR 0x000040 // blue @ 25%
|
||||
|
||||
// The charger IC does not blink on its own; let StatusLEDModule do the
|
||||
// software blink while charging
|
||||
// If left defined: hardware would be expected to handle the charging pulse.
|
||||
// #define POWER_LED_HARDWARE_BLINKS_WHILE_CHARGING
|
||||
|
||||
// Buttons
|
||||
#define PIN_BUTTON1 (32 + 10) // KEY_1: P1.10
|
||||
|
||||
#define BUTTON_CLICK_MS 400
|
||||
|
||||
// Analog pins
|
||||
#define PIN_A0 (0 + 2) // Battery ADC (BATTERY_ADC_DATA)
|
||||
|
||||
#define BATTERY_PIN PIN_A0
|
||||
|
||||
static const uint8_t A0 = PIN_A0;
|
||||
|
||||
#define ADC_RESOLUTION 14
|
||||
|
||||
// BATTERY_MEASUREMENT_CONTROL - enable divider for battery reading
|
||||
#define ADC_CTRL (0 + 31)
|
||||
#define ADC_CTRL_ENABLED HIGH
|
||||
|
||||
// NFC placeholders, not used
|
||||
#define PIN_NFC1 (9)
|
||||
#define PIN_NFC2 (10)
|
||||
|
||||
// Wire Interfaces (IIC_1 on the vendor header)
|
||||
#define WIRE_INTERFACES_COUNT 1
|
||||
|
||||
#define PIN_WIRE_SDA (32 + 4) // IIC_1_SDA: P1.4
|
||||
#define PIN_WIRE_SCL (32 + 2) // IIC_1_SCL: P1.2
|
||||
|
||||
// External serial flash ZD25WQ32CEIGR
|
||||
// QSPI Pins
|
||||
#define PIN_QSPI_SCK (0 + 4)
|
||||
#define PIN_QSPI_CS (0 + 12)
|
||||
#define PIN_QSPI_IO0 (0 + 6) // MOSI if using two bit interface
|
||||
#define PIN_QSPI_IO1 (0 + 8) // MISO if using two bit interface
|
||||
#define PIN_QSPI_IO2 (32 + 9) // WP
|
||||
#define PIN_QSPI_IO3 (0 + 26) // HOLD
|
||||
|
||||
// On-board QSPI Flash
|
||||
#define EXTERNAL_FLASH_DEVICES ZD25WQ32CEIGR
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
// Lora S62F (SX1262)
|
||||
#define USE_SX1262
|
||||
#define SX126X_CS (0 + 11)
|
||||
#define SX126X_DIO1 (32 + 8)
|
||||
#define SX126X_DIO2 (0 + 5)
|
||||
#define SX126X_BUSY (0 + 14)
|
||||
#define SX126X_RESET (0 + 7)
|
||||
#define SX126X_RXEN (32 + 1) // SX1262_RF_VC2
|
||||
#define SX126X_TXEN (0 + 27) // SX1262_RF_VC1
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// OLED display: SSD1315 on I2C @ 0x3C (IIC_1). SSD1315 is register-compatible
|
||||
// with SSD1306, so USE_SSD1306 initializes the controller correctly.
|
||||
//
|
||||
// Viewport: the physical panel is 72×40, mapped into the SSD1315's 128×64
|
||||
// GDDRAM at columns 28..99, pages 3..7 (rows 24..63). The firmware handles
|
||||
// this by:
|
||||
// * asking the library for GEOMETRY_72_40, which sets the framebuffer to
|
||||
// 72×40 and emits the right SETMULTIPLEX (39) / SETCOMPINS at init;
|
||||
// * relying on SSD1306Wire's built-in horizontal auto-centering
|
||||
// ((128 - width) / 2 = 28), so no horizontal shim is needed;
|
||||
// * calling SSD1306Wire::setYOffset(3) in Screen.cpp when
|
||||
// OLED_Y_OFFSET_PAGES is defined — this shifts every PAGEADDR write by
|
||||
// three pages (24 rows) so data lands on the visible rows.
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
#define HAS_SCREEN 1
|
||||
#define USE_SSD1306
|
||||
#define OLED_GEOMETRY_OVERRIDE GEOMETRY_72_40
|
||||
#define OLED_Y_OFFSET_PAGES 3
|
||||
#define OLED_TINY
|
||||
|
||||
// Controls power 3V3 for all peripherals (GPS + LoRa + Sensor)
|
||||
#define PIN_POWER_EN (0 + 30) // RT9080_EN
|
||||
|
||||
// SPI1 is unused (no external SPI display). Keep declarations for the core.
|
||||
#define PIN_SPI1_MISO (-1)
|
||||
#define PIN_SPI1_MOSI (-1)
|
||||
#define PIN_SPI1_SCK (-1)
|
||||
|
||||
// GPS pins
|
||||
#define GPS_L76K
|
||||
#define GPS_BAUDRATE 9600
|
||||
#define HAS_GPS 1
|
||||
|
||||
#define PIN_GPS_EN (32 + 15) // GPS_EN: P1.15 - GPS power enable
|
||||
#define GPS_EN_ACTIVE 1
|
||||
#define PIN_GPS_STANDBY (0 + 25) // GPS_WAKE_UP: P0.25 - wakeup pin
|
||||
#define PIN_GPS_PPS (0 + 23) // GPS_1PPS: P0.23
|
||||
#define GPS_RX_PIN (0 + 19) // MCU RX ← GPS's TX (vendor GPS_UART_TX / P0.19)
|
||||
#define GPS_TX_PIN (0 + 21) // MCU TX → GPS's RX (vendor GPS_UART_RX / P0.21)
|
||||
#define PIN_GPS_RESET (0 + 29) // GPS_RF_EN: GPS RF enable / reset
|
||||
|
||||
#define GPS_THREAD_INTERVAL 50
|
||||
|
||||
#define PIN_SERIAL1_RX GPS_RX_PIN
|
||||
#define PIN_SERIAL1_TX GPS_TX_PIN
|
||||
|
||||
// SPI Interfaces (LoRa on SPI0)
|
||||
#define SPI_INTERFACES_COUNT 2
|
||||
|
||||
// For LORA, SPI 0
|
||||
#define PIN_SPI_MISO (0 + 17)
|
||||
#define PIN_SPI_MOSI (0 + 15)
|
||||
#define PIN_SPI_SCK (0 + 13)
|
||||
|
||||
// Battery
|
||||
// The battery sense is hooked to PIN_A0 (P0.2) via a divider controlled by ADC_CTRL.
|
||||
#define BATTERY_SENSE_RESOLUTION_BITS 12
|
||||
#define BATTERY_SENSE_RESOLUTION 4096.0
|
||||
#undef AREF_VOLTAGE
|
||||
#define AREF_VOLTAGE 3.0
|
||||
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
|
||||
#define ADC_MULTIPLIER (2.0F)
|
||||
|
||||
// Buzzer (PWM output, passive piezo)
|
||||
#define PIN_BUZZER (32 + 6) // BUZZER_DATA: P1.6
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// I²S speaker (MAX98357 Class-D amp). Stereo I²S data path.
|
||||
// Not supported on nrf52. These defines exist for out-of-tree code only.
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
#define SPEAKER_EN (32 + 11) // P1.11 - amp main enable
|
||||
#define SPEAKER_EN_2 (0 + 3) // P0.3 - secondary enable (vendor firmware toggles both)
|
||||
#define SPEAKER_BCLK (0 + 16) // P0.16 - I2S bit clock
|
||||
#define SPEAKER_DATA (0 + 20) // P0.20 - I2S data (SDOUT)
|
||||
#define SPEAKER_WS_LRCK (0 + 22) // P0.22 - I2S word select / LRCK
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// PDM microphone (ST MP34DT05).
|
||||
// TODO to enable a mic path:
|
||||
// Use Adafruit nRF52 core's built-in PDM.h wrapper (Arduino-compatible
|
||||
// API exists on nRF52840). Clock on MIC_SCLK, data on MIC_DATA.
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
#define MIC_SCLK (32 + 3) // P1.3 - PDM clock (MIC_SCLK on vendor header)
|
||||
#define MIC_DATA (32 + 5) // P1.5 - PDM data (MIC_DATA on vendor header)
|
||||
|
||||
#define SERIAL_PRINT_PORT 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 24
|
||||
build = 25
|
||||
|
||||
Reference in New Issue
Block a user