Merge branch 'master' into change-pubsubclient

This commit is contained in:
Tom Fifield 2025-01-03 14:35:03 +08:00 committed by GitHub
commit a5dff12429
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 912 additions and 212 deletions

14
.github/actions/setup-native/action.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Setup native build
description: Install libraries needed for building the Native/Portduino build
runs:
using: composite
steps:
- name: Setup base
id: base
uses: ./.github/actions/setup-base
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev

View File

@ -10,12 +10,6 @@ jobs:
build-native:
runs-on: ubuntu-latest
steps:
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
- name: Checkout code
uses: actions/checkout@v4
with:
@ -23,17 +17,9 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Build Native
run: bin/build-native.sh

View File

@ -137,6 +137,9 @@ jobs:
package-native:
uses: ./.github/workflows/package_amd64.yml
test-native:
uses: ./.github/workflows/test_native.yml
build-docker:
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/build_docker.yml

169
.github/workflows/test_native.yml vendored Normal file
View File

@ -0,0 +1,169 @@
name: Run Tests on Native platform
on:
workflow_call:
workflow_dispatch:
permissions: {}
env:
LCOV_CAPTURE_FLAGS: --quiet --capture --include "${PWD}/src/*" --exclude '*/src/mesh/generated/*' --directory .pio/build/coverage/src --base-directory "${PWD}"
jobs:
simulator-tests:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Install simulator dependencies
run: pip install -U dotmap
# We now run integration test before other build steps (to quickly see runtime failures)
- name: Build for native/coverage
run: platformio run -e coverage
- name: Capture initial coverage information
shell: bash
run: |
sudo apt-get install -y lcov
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --initial --output-file coverage_base.info
sed -i -e "s#${PWD}#.#" coverage_base.info # Make paths relative.
- name: Integration test
run: |
.pio/build/coverage/program &
PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
wait
- name: Capture coverage information
if: always() # run this step even if previous step failed
run: |
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name integration --output-file coverage_integration.info
sed -i -e "s#${PWD}#.#" coverage_integration.info # Make paths relative.
- name: Get release version string
if: always() # run this step even if previous step failed
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Save coverage information
uses: actions/upload-artifact@v4
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.version }}.zip
overwrite: true
path: ./coverage_*.info
platformio-tests:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
# Disable (comment-out) BUILD_EPOCH. It causes a full rebuild between tests and resets the
# coverage information each time.
- name: Disable BUILD_EPOCH
run: sed -i 's/-DBUILD_EPOCH=$UNIX_TIME/#-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
- name: PlatformIO Tests
run: platformio test -e coverage --junit-output-path testreport.xml
- name: Save test results
if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v4
with:
name: platformio-test-report-${{ steps.version.outputs.version }}.zip
overwrite: true
path: ./testreport.xml
- name: Capture coverage information
if: always() # run this step even if previous step failed
run: |
sudo apt-get install -y lcov
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name tests --output-file coverage_tests.info
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information
uses: actions/upload-artifact@v4
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.version }}.zip
overwrite: true
path: ./coverage_*.info
generate-reports:
name: Generate Test Reports
runs-on: ubuntu-latest
permissions: # Needed for dorny/test-reporter.
contents: read
actions: read
checks: write
needs:
- simulator-tests
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download test artifacts
uses: actions/download-artifact@v4
with:
name: platformio-test-report-${{ steps.version.outputs.version }}.zip
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v1.9.1
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit
- name: Download coverage artifacts
uses: actions/download-artifact@v4
with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.version }}.zip
path: code-coverage-report
merge-multiple: true
- name: Generate Code Coverage Report
run: |
sudo apt-get install -y lcov
lcov --quiet --add-tracefile code-coverage-report/coverage_base.info --add-tracefile code-coverage-report/coverage_integration.info --add-tracefile code-coverage-report/coverage_tests.info --output-file code-coverage-report/coverage_src.info
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
- name: Save Code Coverage Report
uses: actions/upload-artifact@v4
with:
name: code-coverage-report-${{ steps.version.outputs.version }}.zip
path: code-coverage-report

View File

@ -6,79 +6,8 @@ on:
workflow_dispatch: {}
jobs:
test-simulator:
runs-on: ubuntu-latest
env:
LCOV_CAPTURE_FLAGS: --quiet --capture --include "${PWD}/src/*" --exclude '*/src/mesh/generated/*' --directory .pio/build/coverage/src --base-directory "${PWD}"
steps:
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
sudo apt-get install -y lcov
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil dotmap
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Build Native
run: bin/build-native.sh
# We now run integration test before other build steps (to quickly see runtime failures)
- name: Build for native/coverage
run: |
platformio run -e coverage
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --initial --output-file coverage_base.info
- name: Integration test
run: |
.pio/build/coverage/program &
PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
wait
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name integration --output-file coverage_integration.info
- name: PlatformIO Tests
run: |
platformio test -e coverage --junit-output-path testreport.xml
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name tests --output-file coverage_tests.info
- name: Test Report
uses: dorny/test-reporter@v1.9.1
if: success() || failure() # run this step even if previous step failed
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit
- name: Generate Code Coverage Report
run: |
lcov --quiet --add-tracefile coverage_base.info --add-tracefile coverage_integration.info --add-tracefile coverage_tests.info --output-file coverage_src.info
mkdir code-coverage-report
genhtml --quiet --legend --prefix "${PWD}" coverage_src.info --output-directory code-coverage-report
mv coverage_*.info code-coverage-report
- name: Save Code Coverage Report
uses: actions/upload-artifact@v4
with:
name: code-coverage-report
path: code-coverage-report
native-tests:
uses: ./.github/workflows/test_native.yml
hardware-tests:
runs-on: test-runner

View File

@ -30,6 +30,7 @@ default_envs = tbeam
;default_envs = rak4631
;default_envs = rak4631_eth_gw
;default_envs = rak2560
;default_envs = rak11310
;default_envs = rak_wismeshtap
;default_envs = wio-e5
;default_envs = radiomaster_900_bandit_nano
@ -123,7 +124,8 @@ lib_deps =
[radiolib_base]
lib_deps =
jgromes/RadioLib@7.1.0
; jgromes/RadioLib@7.1.0
https://github.com/jgromes/RadioLib.git#92b687821ff4e6c358d866f84566f66672ab02b8
; Common libs for environmental measurements in telemetry module
; (not included in native / portduino)

View File

@ -9,6 +9,7 @@
*
*/
#include "FSCommon.h"
#include "SPILock.h"
#include "configuration.h"
#ifdef HAS_SDCARD
@ -102,6 +103,8 @@ bool copyFile(const char *from, const char *to)
return true;
#elif defined(FSCom)
// take SPI Lock
concurrency::LockGuard g(spiLock);
unsigned char cbuffer[16];
File f1 = FSCom.open(from, FILE_O_READ);
@ -145,16 +148,23 @@ bool renameFile(const char *pathFrom, const char *pathTo)
return false;
}
#elif defined(FSCom)
#ifdef ARCH_ESP32
// take SPI Lock
spiLock->lock();
// rename was fixed for ESP32 IDF LittleFS in April
return FSCom.rename(pathFrom, pathTo);
bool result = FSCom.rename(pathFrom, pathTo);
spiLock->unlock();
return result;
#else
// copyFile does its own locking.
if (copyFile(pathFrom, pathTo) && FSCom.remove(pathFrom)) {
return true;
} else {
return false;
}
#endif
#endif
}
@ -164,6 +174,7 @@ bool renameFile(const char *pathFrom, const char *pathTo)
* @brief Get the list of files in a directory.
*
* This function returns a list of files in a directory. The list includes the full path of each file.
* We can't use SPILOCK here because of recursion. Callers of this function should use SPILOCK.
*
* @param dirname The name of the directory.
* @param levels The number of levels of subdirectories to list.
@ -212,6 +223,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
/**
* Lists the contents of a directory.
* We can't use SPILOCK here because of recursion. Callers of this function should use SPILOCK.
*
* @param dirname The name of the directory to list.
* @param levels The number of levels of subdirectories to list.
@ -325,18 +337,21 @@ void listDir(const char *dirname, uint8_t levels, bool del)
void rmDir(const char *dirname)
{
#ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
listDir(dirname, 10, true);
#elif defined(ARCH_NRF52)
// nRF52 implementation of LittleFS has a recursive delete function
FSCom.rmdir_r(dirname);
#endif
#endif
}
void fsInit()
{
#ifdef FSCom
spiLock->lock();
if (!FSBegin()) {
LOG_ERROR("Filesystem mount failed");
// assert(0); This auto-formats the partition, so no need to fail here.
@ -347,6 +362,7 @@ void fsInit()
LOG_DEBUG("Filesystem files:");
#endif
listDir("/", 10);
spiLock->unlock();
#endif
}
@ -356,6 +372,7 @@ void fsInit()
void setupSDCard()
{
#ifdef HAS_SDCARD
concurrency::LockGuard g(spiLock);
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
if (!SD.begin(SDCARD_CS, SDHandler)) {
@ -383,4 +400,4 @@ void setupSDCard()
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
#endif
}
}

View File

@ -5,6 +5,7 @@
// Only way to work on both esp32 and nrf52
static File openFile(const char *filename, bool fullAtomic)
{
concurrency::LockGuard g(spiLock);
if (!fullAtomic)
FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists)
@ -53,14 +54,19 @@ bool SafeFile::close()
if (!f)
return false;
spiLock->lock();
f.close();
spiLock->unlock();
if (!testReadback())
return false;
// brief window of risk here ;-)
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
LOG_ERROR("Can't remove old pref file");
return false;
{ // Scope for lock
concurrency::LockGuard g(spiLock);
// brief window of risk here ;-)
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
LOG_ERROR("Can't remove old pref file");
return false;
}
}
String filenameTmp = filename;
@ -76,6 +82,7 @@ bool SafeFile::close()
/// Read our (closed) tempfile back in and compare the hash
bool SafeFile::testReadback()
{
concurrency::LockGuard g(spiLock);
bool lfs_failed = lfs_assert_failed;
lfs_assert_failed = false;

View File

@ -1,6 +1,7 @@
#pragma once
#include "FSCommon.h"
#include "SPILock.h"
#include "configuration.h"
#ifdef FSCom

View File

@ -178,13 +178,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define TCA9535_ADDR 0x20
#define TCA9555_ADDR 0x26
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#ifndef GPS_THREAD_INTERVAL
#define GPS_THREAD_INTERVAL 200
#endif
// -----------------------------------------------------------------------------
// Touchscreen
// -----------------------------------------------------------------------------
@ -206,6 +199,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VEXT_ON_VALUE LOW
#endif
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#ifndef GPS_BAUDRATE
#define GPS_BAUDRATE 9600
#define GPS_BAUDRATE_FIXED 0
@ -213,6 +210,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define GPS_BAUDRATE_FIXED 1
#endif
#ifndef GPS_THREAD_INTERVAL
#define GPS_THREAD_INTERVAL 200
#endif
/* Step #2: follow with defines common to the architecture;
also enable HAS_ option not specifically disabled by variant.h */
#include "architecture.h"

View File

@ -1015,7 +1015,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil);
} else if (strcmp(msg, "♥️") == 0 || strcmp(msg, "\U0001F9E1") == 0 || strcmp(msg, "\U00002763") == 0 ||
strcmp(msg, "\U00002764") == 0 || strcmp(msg, "\U0001F495") == 0 || strcmp(msg, "\U0001F496") == 0 ||
strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F496") == 0) {
strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F498") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart);
} else {
@ -1506,7 +1506,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif ARCH_PORTDUINO
#elif ARCH_PORTDUINO && !HAS_TFT
if (settingsMap[displayPanel] != no_screen) {
LOG_DEBUG("Make TFTDisplay!");
dispdev = new TFTDisplay(address.address, -1, -1, geometry,

View File

@ -352,7 +352,7 @@ static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_LCD *_panel_instance;
lgfx::Panel_Device *_panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::ITouch *_touch_instance;
@ -366,10 +366,21 @@ class LGFX : public lgfx::LGFX_Device
_panel_instance = new lgfx::Panel_ST7735;
else if (settingsMap[displayPanel] == st7735s)
_panel_instance = new lgfx::Panel_ST7735S;
else if (settingsMap[displayPanel] == st7796)
_panel_instance = new lgfx::Panel_ST7796;
else if (settingsMap[displayPanel] == ili9341)
_panel_instance = new lgfx::Panel_ILI9341;
else if (settingsMap[displayPanel] == ili9342)
_panel_instance = new lgfx::Panel_ILI9342;
else if (settingsMap[displayPanel] == ili9488)
_panel_instance = new lgfx::Panel_ILI9488;
else if (settingsMap[displayPanel] == hx8357d)
_panel_instance = new lgfx::Panel_HX8357D;
else {
_panel_instance = new lgfx::Panel_NULL;
LOG_ERROR("Unknown display panel configured!");
}
auto buscfg = _bus_instance.config();
buscfg.spi_mode = 0;
buscfg.spi_host = settingsMap[displayspidev];
@ -383,12 +394,12 @@ class LGFX : public lgfx::LGFX_Device
LOG_DEBUG("Height: %d, Width: %d ", settingsMap[displayHeight], settingsMap[displayWidth]);
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = settingsMap[displayReset];
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
cfg.offset_rotation = settingsMap[displayOffsetRotate]; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed
_panel_instance->config(cfg);
@ -410,7 +421,7 @@ class LGFX : public lgfx::LGFX_Device
touch_cfg.y_max = settingsMap[displayWidth] - 1;
touch_cfg.pin_int = settingsMap[touchscreenIRQ];
touch_cfg.bus_shared = true;
touch_cfg.offset_rotation = 1;
touch_cfg.offset_rotation = settingsMap[touchscreenRotate];
if (settingsMap[touchscreenI2CAddr] != -1) {
touch_cfg.i2c_addr = settingsMap[touchscreenI2CAddr];
} else {

View File

@ -364,6 +364,8 @@ void setup()
#endif
#endif
initSPI();
OSThread::setup();
ledPeriodic = new Periodic("Blink", ledBlinker);
@ -640,8 +642,6 @@ void setup()
rp2040Setup();
#endif
initSPI(); // needed here before reading from littleFS
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB = new NodeDB;

View File

@ -4,6 +4,7 @@
#include "NodeDB.h"
#include "configuration.h"
#include "modules/RoutingModule.h"
#include <algorithm>
#include <assert.h>
std::vector<MeshModule *> *MeshModule::modules;
@ -29,7 +30,9 @@ void MeshModule::setup() {}
MeshModule::~MeshModule()
{
assert(0); // FIXME - remove from list of modules once someone needs this feature
auto it = std::find(modules->begin(), modules->end(), this);
assert(it != modules->end());
modules->erase(it);
}
meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,

View File

@ -13,6 +13,7 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "SPILock.h"
#include "SafeFile.h"
#include "TypeConversions.h"
#include "error.h"
@ -409,6 +410,13 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
rebootAtMsec = millis() + (5 * 1000);
}
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && defined(HAS_TFT)
// as long as PhoneAPI shares BT and TFT app switch BT off
config.bluetooth.enabled = false;
if (moduleConfig.external_notification.nag_timeout == 60)
moduleConfig.external_notification.nag_timeout = 0;
#endif
return didFactoryReset;
}
@ -416,12 +424,15 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
{
LOG_INFO("Perform factory reset!");
// first, remove the "/prefs" (this removes most prefs)
rmDir("/prefs");
spiLock->lock();
rmDir("/prefs"); // this uses spilock internally...
#ifdef FSCom
if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) {
LOG_ERROR("Could not remove rangetest.csv file");
}
#endif
spiLock->unlock();
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
installDefaultConfig(!eraseBleBonds); // Also preserve the private key if we're not erasing BLE bonds
@ -906,6 +917,7 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t
{
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
#ifdef FSCom
concurrency::LockGuard g(spiLock);
auto f = FSCom.open(filename, FILE_O_READ);
@ -939,8 +951,10 @@ void NodeDB::loadFromDisk()
// disk we will still factoryReset to restore things.
#ifdef ARCH_ESP32
spiLock->lock();
if (FSCom.exists("/static/static"))
rmDir("/static/static"); // Remove bad static web files bundle from initial 2.5.13 release
spiLock->unlock();
#endif
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
@ -985,8 +999,11 @@ void NodeDB::loadFromDisk()
// Make sure we load hard coded admin keys even when the configuration file has none.
// Initialize admin_key_count to zero
byte numAdminKeys = 0;
#if defined(USERPREFS_USE_ADMIN_KEY_0) || defined(USERPREFS_USE_ADMIN_KEY_1) || defined(USERPREFS_USE_ADMIN_KEY_2)
uint16_t sum = 0;
#endif
#ifdef USERPREFS_USE_ADMIN_KEY_0
for (uint8_t b = 0; b < 32; b++) {
sum += config.security.admin_key[0].bytes[b];
}
@ -995,8 +1012,6 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 0 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32);
config.security.admin_key[0].size = 32;
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
@ -1010,8 +1025,6 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 1 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32);
config.security.admin_key[1].size = 32;
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
@ -1025,10 +1038,14 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 2 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32);
config.security.admin_key[2].size = 32;
}
#endif
if (numAdminKeys > 0) {
LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys);
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
&meshtastic_LocalModuleConfig_msg, &moduleConfig);
@ -1087,9 +1104,6 @@ void NodeDB::loadFromDisk()
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic)
{
#ifdef ARCH_ESP32
concurrency::LockGuard g(spiLock);
#endif
bool okay = false;
#ifdef FSCom
auto f = SafeFile(filename, fullAtomic);
@ -1117,7 +1131,9 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_
bool NodeDB::saveChannelsToDisk()
{
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
return saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
}
@ -1125,7 +1141,9 @@ bool NodeDB::saveChannelsToDisk()
bool NodeDB::saveDeviceStateToDisk()
{
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB
// Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this
@ -1138,7 +1156,9 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
bool success = true;
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
if (saveWhat & SEGMENT_CONFIG) {
config.has_device = true;
@ -1189,7 +1209,9 @@ bool NodeDB::saveToDisk(int saveWhat)
if (!success) {
LOG_ERROR("Failed to save to disk, retrying");
#ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion
spiLock->lock();
FSCom.format();
spiLock->unlock();
#endif
success = saveToDiskNoRetry(saveWhat);
@ -1508,4 +1530,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co
LOG_ERROR("A critical failure occurred, portduino is exiting");
exit(2);
#endif
}
}

View File

@ -12,6 +12,7 @@
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "SPILock.h"
#include "TypeConversions.h"
#include "main.h"
#include "xmodem.h"
@ -54,7 +55,9 @@ void PhoneAPI::handleStartConfig()
// even if we were already connected - restart our state machine
state = STATE_SEND_MY_INFO;
pauseBluetoothLogging = true;
spiLock->lock();
filesManifest = getFiles("/", 10);
spiLock->unlock();
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
LOG_INFO("Start API client config");

View File

@ -66,8 +66,9 @@ static int32_t reconnectETH()
syslog.enable();
}
// initWebServer();
#if !MESHTASTIC_EXCLUDE_SOCKETAPI
initApiServer();
#endif
ethStartupComplete = true;
}
@ -107,6 +108,11 @@ static int32_t reconnectETH()
bool initEthernet()
{
if (config.network.eth_enabled) {
#ifdef PIN_ETH_POWER_EN
pinMode(PIN_ETH_POWER_EN, OUTPUT);
digitalWrite(PIN_ETH_POWER_EN, HIGH); // Power up.
delay(100);
#endif
#ifdef PIN_ETHERNET_RESET
pinMode(PIN_ETHERNET_RESET, OUTPUT);
@ -115,6 +121,12 @@ bool initEthernet()
digitalWrite(PIN_ETHERNET_RESET, HIGH); // Reset Time.
#endif
#ifdef RAK11310 // Initialize the SPI port
ETH_SPI_PORT.setSCK(PIN_SPI0_SCK);
ETH_SPI_PORT.setTX(PIN_SPI0_MOSI);
ETH_SPI_PORT.setRX(PIN_SPI0_MISO);
ETH_SPI_PORT.begin();
#endif
Ethernet.init(ETH_SPI_PORT, PIN_ETHERNET_SS);
uint8_t mac[6];

View File

@ -10,6 +10,7 @@
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "Led.h"
#include "SPILock.h"
#include "power.h"
#include "serialization/JSON.h"
#include <FSCommon.h>
@ -236,6 +237,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
void htmlDeleteDir(const char *dirname)
{
File root = FSCom.open(dirname);
if (!root) {
return;
@ -318,6 +320,7 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
concurrency::LockGuard g(spiLock);
auto fileList = htmlListDir("/static", 10);
// create json output structure
@ -349,9 +352,12 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "DELETE");
if (params->getQueryParameter("delete", paramValDelete)) {
std::string pathDelete = "/" + paramValDelete;
concurrency::LockGuard g(spiLock);
if (FSCom.remove(pathDelete.c_str())) {
LOG_INFO("%s", pathDelete.c_str());
JSONObject jsonObjOuter;
jsonObjOuter["status"] = new JSONValue("ok");
@ -360,6 +366,7 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
delete value;
return;
} else {
LOG_INFO("%s", pathDelete.c_str());
JSONObject jsonObjOuter;
jsonObjOuter["status"] = new JSONValue("Error");
@ -393,6 +400,8 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
filenameGzip = "/static/index.html.gz";
}
concurrency::LockGuard g(spiLock);
if (FSCom.exists(filename.c_str())) {
file = FSCom.open(filename.c_str());
if (!file.available()) {
@ -410,6 +419,7 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
file = FSCom.open(filenameGzip.c_str());
res->setHeader("Content-Type", "text/html");
if (!file.available()) {
LOG_WARN("File not available - %s", filenameGzip.c_str());
res->println("Web server is running.<br><br>The content you are looking for can't be found. Please see: <a "
"href=https://meshtastic.org/docs/software/web-client/>FAQ</a>.<br><br><a "
@ -535,6 +545,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// concepts of the body parser functionality easier to understand.
std::string pathname = "/static/" + filename;
concurrency::LockGuard g(spiLock);
// Create a new file to stream the data into
File file = FSCom.open(pathname.c_str(), FILE_O_WRITE);
size_t fileLength = 0;
@ -571,6 +582,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
file.flush();
file.close();
res->printf("<p>Saved %d bytes to %s</p>", (int)fileLength, pathname.c_str());
}
if (!didwrite) {
@ -642,9 +654,11 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
jsonObjMemory["heap_free"] = new JSONValue((int)memGet.getFreeHeap());
jsonObjMemory["psram_total"] = new JSONValue((int)memGet.getPsramSize());
jsonObjMemory["psram_free"] = new JSONValue((int)memGet.getFreePsram());
spiLock->lock();
jsonObjMemory["fs_total"] = new JSONValue((int)FSCom.totalBytes());
jsonObjMemory["fs_used"] = new JSONValue((int)FSCom.usedBytes());
jsonObjMemory["fs_free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
spiLock->unlock();
// data->power
JSONObject jsonObjPower;
@ -725,7 +739,6 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
node["position"] = new JSONValue(position);
}
JSONObject user;
node["long_name"] = new JSONValue(tempNodeInfo->user.long_name);
node["short_name"] = new JSONValue(tempNodeInfo->user.short_name);
char macStr[18];
@ -787,6 +800,7 @@ void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res)
LOG_INFO("Delete files from /static/* : ");
concurrency::LockGuard g(spiLock);
htmlDeleteDir("/static");
res->println("<p><hr><p><a href=/admin>Back to admin</a>");

View File

@ -1,6 +1,7 @@
#include "configuration.h"
#include "FSCommon.h"
#include "SPILock.h"
#include "mesh-pb-constants.h"
#include <Arduino.h>
#include <pb_decode.h>
@ -55,9 +56,12 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
/// Write to an arduino file
bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
spiLock->lock();
auto file = (Print *)stream->state;
// LOG_DEBUG("writing %d bytes to protobuf file", count);
return file->write(buf, count) == count;
bool status = file->write(buf, count) == count;
spiLock->unlock();
return status;
}
#endif
@ -68,4 +72,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count)
return true;
return false;
}
}

View File

@ -106,7 +106,9 @@ static void onNetworkConnected()
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
initWebServer();
#endif
#if !MESHTASTIC_EXCLUDE_SOCKETAPI
initApiServer();
#endif
APStartupComplete = true;
}

View File

@ -4,6 +4,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "SPILock.h"
#include "meshUtils.h"
#include <FSCommon.h>
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
@ -358,12 +359,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
case meshtastic_AdminMessage_delete_file_request_tag: {
LOG_DEBUG("Client requesting to delete file: %s", r->delete_file_request);
#ifdef FSCom
spiLock->lock();
if (FSCom.remove(r->delete_file_request)) {
LOG_DEBUG("Successfully deleted file");
} else {
LOG_DEBUG("Failed to delete file");
}
spiLock->unlock();
#endif
break;
}

View File

@ -9,6 +9,7 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h" // needed for button bypass
#include "SPILock.h"
#include "detect/ScanI2C.h"
#include "input/ScanAndSelect.h"
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
@ -982,6 +983,7 @@ bool CannedMessageModule::interceptingKeyboardInput()
}
}
#if !HAS_TFT
void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
char buffer[50];
@ -1140,6 +1142,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
}
}
}
#endif //! HAS_TFT
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
@ -1182,8 +1185,10 @@ bool CannedMessageModule::saveProtoForModule()
{
bool okay = true;
#ifdef FS
FS.mkdir("/prefs");
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
okay &= nodeDB->saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,

View File

@ -15,6 +15,7 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "SPILock.h"
#include "airtime.h"
#include "configuration.h"
#include "gps/GeoCoord.h"
@ -205,6 +206,7 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
LOG_DEBUG("gpsStatus->getDOP() %d", gpsStatus->getDOP());
LOG_DEBUG("-----------------------------------------");
*/
concurrency::LockGuard g(spiLock);
if (!FSBegin()) {
LOG_DEBUG("An Error has occurred while mounting the filesystem");
return 0;

View File

@ -64,6 +64,10 @@ CGRadSensSensor cgRadSens;
#include "Sensor/T1000xSensor.h"
T1000xSensor t1000xSensor;
#endif
#ifdef SENSECAP_INDICATOR
#include "Sensor/IndicatorSensor.h"
IndicatorSensor indicatorSensor;
#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@ -103,6 +107,9 @@ int32_t EnvironmentTelemetryModule::runOnce()
LOG_INFO("Environment Telemetry: init");
// it's possible to have this module enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
#ifdef SENSECAP_INDICATOR
result = indicatorSensor.runOnce();
#endif
#ifdef T1000X_SENSOR_EN
result = t1000xSensor.runOnce();
#elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
@ -298,6 +305,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero;
#ifdef SENSECAP_INDICATOR
valid = valid && indicatorSensor.getMetrics(m);
hasSensor = true;
#endif
#ifdef T1000X_SENSOR_EN // add by WayenWeng
valid = valid && t1000xSensor.getMetrics(m);
hasSensor = true;
@ -410,7 +421,6 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
valid = valid && cgRadSens.getMetrics(m);
hasSensor = true;
}
#endif
return valid && hasSensor;
}

View File

@ -5,6 +5,7 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "BME680Sensor.h"
#include "FSCommon.h"
#include "SPILock.h"
#include "TelemetrySensor.h"
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
@ -75,6 +76,7 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
void BME680Sensor::loadState()
{
#ifdef FSCom
spiLock->lock();
auto file = FSCom.open(bsecConfigFileName, FILE_O_READ);
if (file) {
file.read((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE);
@ -84,6 +86,7 @@ void BME680Sensor::loadState()
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, bsecConfigFileName);
}
spiLock->unlock();
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
@ -92,6 +95,7 @@ void BME680Sensor::loadState()
void BME680Sensor::updateState()
{
#ifdef FSCom
spiLock->lock();
bool update = false;
if (stateUpdateCounter == 0) {
/* First state update when IAQ accuracy is >= 3 */
@ -127,6 +131,7 @@ void BME680Sensor::updateState()
LOG_INFO("Can't write %s state (File: %s)", sensorName, bsecConfigFileName);
}
}
spiLock->unlock();
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif

View File

@ -41,13 +41,12 @@ void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr)
float CGRadSensSensor::getStaticRadiation()
{
// Read a register, following the same pattern as the RCWL9620Sensor
uint32_t data;
_wire->beginTransmission(_addr); // Transfer data to addr.
_wire->write(0x06); // Radiation intensity (static period T = 500 sec)
if (_wire->endTransmission() == 0) {
if (_wire->requestFrom(_addr, (uint8_t)3)) {
; // Request 3 bytes
data = _wire->read();
uint32_t data = _wire->read();
data <<= 8;
data |= _wire->read();
data <<= 8;

View File

@ -0,0 +1,166 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(SENSECAP_INDICATOR)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "IndicatorSensor.h"
#include "TelemetrySensor.h"
#include "serialization/cobs.h"
#include <Adafruit_Sensor.h>
#include <driver/uart.h>
IndicatorSensor::IndicatorSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR_UNSET, "Indicator") {}
#define SENSOR_BUF_SIZE (512)
uint8_t buf[SENSOR_BUF_SIZE]; // recv
uint8_t data[SENSOR_BUF_SIZE]; // decode
#define ACK_PKT_PARA "ACK"
enum sensor_pkt_type {
PKT_TYPE_ACK = 0x00, // uin32_t
PKT_TYPE_CMD_COLLECT_INTERVAL = 0xA0, // uin32_t
PKT_TYPE_CMD_BEEP_ON = 0xA1, // uin32_t ms: on time
PKT_TYPE_CMD_BEEP_OFF = 0xA2,
PKT_TYPE_CMD_SHUTDOWN = 0xA3, // uin32_t
PKT_TYPE_CMD_POWER_ON = 0xA4,
PKT_TYPE_SENSOR_SCD41_TEMP = 0xB0, // float
PKT_TYPE_SENSOR_SCD41_HUMIDITY = 0xB1, // float
PKT_TYPE_SENSOR_SCD41_CO2 = 0xB2, // float
PKT_TYPE_SENSOR_AHT20_TEMP = 0xB3, // float
PKT_TYPE_SENSOR_AHT20_HUMIDITY = 0xB4, // float
PKT_TYPE_SENSOR_TVOC_INDEX = 0xB5, // float
};
static int cmd_send(uint8_t cmd, const char *p_data, uint8_t len)
{
uint8_t send_buf[32] = {0};
uint8_t send_data[32] = {0};
if (len > 31) {
return -1;
}
uint8_t index = 1;
send_data[0] = cmd;
if (len > 0 && p_data != NULL) {
memcpy(&send_data[1], p_data, len);
index += len;
}
cobs_encode_result ret = cobs_encode(send_buf, sizeof(send_buf), send_data, index);
// LOG_DEBUG("cobs TX status:%d, len:%d, type 0x%x", ret.status, ret.out_len, cmd);
if (ret.status == COBS_ENCODE_OK) {
return uart_write_bytes(SENSOR_PORT_NUM, send_buf, ret.out_len + 1);
}
return -1;
}
int32_t IndicatorSensor::runOnce()
{
LOG_INFO("%s: init", sensorName);
setup();
return 2 * DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; // give it some time to start up
}
void IndicatorSensor::setup()
{
uart_config_t uart_config = {
.baud_rate = SENSOR_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
int intr_alloc_flags = 0;
char buffer[11];
uart_driver_install(SENSOR_PORT_NUM, SENSOR_BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags);
uart_param_config(SENSOR_PORT_NUM, &uart_config);
uart_set_pin(SENSOR_PORT_NUM, SENSOR_RP2040_TXD, SENSOR_RP2040_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
cmd_send(PKT_TYPE_CMD_POWER_ON, NULL, 0);
// measure and send only once every minute, for the phone API
const char *interval = ultoa(60000, buffer, 10);
cmd_send(PKT_TYPE_CMD_COLLECT_INTERVAL, interval, strlen(interval) + 1);
}
bool IndicatorSensor::getMetrics(meshtastic_Telemetry *measurement)
{
cobs_decode_result ret;
int len = uart_read_bytes(SENSOR_PORT_NUM, buf, (SENSOR_BUF_SIZE - 1), 100 / portTICK_PERIOD_MS);
float value = 0.0;
uint8_t *p_buf_start = buf;
uint8_t *p_buf_end = buf;
if (len > 0) {
while (p_buf_start < (buf + len)) {
p_buf_end = p_buf_start;
while (p_buf_end < (buf + len)) {
if (*p_buf_end == 0x00) {
break;
}
p_buf_end++;
}
// decode buf
memset(data, 0, sizeof(data));
ret = cobs_decode(data, sizeof(data), p_buf_start, p_buf_end - p_buf_start);
// LOG_DEBUG("cobs RX status:%d, len:%d, type:0x%x ", ret.status, ret.out_len, data[0]);
if (ret.out_len > 1 && ret.status == COBS_DECODE_OK) {
value = 0.0;
uint8_t pkt_type = data[0];
switch (pkt_type) {
case PKT_TYPE_SENSOR_SCD41_CO2: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("CO2: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
break;
}
case PKT_TYPE_SENSOR_AHT20_TEMP: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Temp: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = value;
break;
}
case PKT_TYPE_SENSOR_AHT20_HUMIDITY: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Humidity: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = value;
break;
}
case PKT_TYPE_SENSOR_TVOC_INDEX: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Tvoc: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.iaq = value;
break;
}
default:
break;
}
}
p_buf_start = p_buf_end + 1; // next message
}
return true;
}
return false;
}
#endif

View File

@ -0,0 +1,19 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
class IndicatorSensor : public TelemetrySensor
{
protected:
virtual void setup() override;
public:
IndicatorSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@ -5,6 +5,7 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "FSCommon.h"
#include "NAU7802Sensor.h"
#include "SPILock.h"
#include "SafeFile.h"
#include "TelemetrySensor.h"
#include <Throttle.h>
@ -111,13 +112,16 @@ bool NAU7802Sensor::saveCalibrationData()
} else {
okay = true;
}
spiLock->lock();
okay &= file.close();
spiLock->unlock();
return okay;
}
bool NAU7802Sensor::loadCalibrationData()
{
spiLock->lock();
auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
bool okay = false;
if (file) {
@ -134,7 +138,8 @@ bool NAU7802Sensor::loadCalibrationData()
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, nau7802ConfigFileName);
}
spiLock->unlock();
return okay;
}
#endif
#endif

View File

@ -2,6 +2,7 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "ServiceEnvelope.h"
#include "configuration.h"
#include "main.h"
#include "mesh/Channels.h"
@ -25,7 +26,6 @@
#endif
#include <Throttle.h>
#include <assert.h>
#include <pb_decode.h>
#include <utility>
#include <IPAddress.h>
@ -47,23 +47,6 @@ static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for cha
static bool isMqttServerAddressPrivate = false;
// meshtastic_ServiceEnvelope that automatically releases dynamically allocated memory when it goes out of scope.
struct DecodedServiceEnvelope : public meshtastic_ServiceEnvelope {
DecodedServiceEnvelope() = delete;
DecodedServiceEnvelope(const uint8_t *payload, size_t length)
: meshtastic_ServiceEnvelope(meshtastic_ServiceEnvelope_init_default),
validDecode(pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, this))
{
}
~DecodedServiceEnvelope()
{
if (validDecode)
pb_release(&meshtastic_ServiceEnvelope_msg, this);
}
// Clients must check that this is true before using.
const bool validDecode;
};
inline void onReceiveProto(char *topic, byte *payload, size_t length)
{
const DecodedServiceEnvelope e(payload, length);
@ -299,7 +282,9 @@ void mqttInit()
}
#if HAS_NETWORKING
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
MQTT::MQTT() : MQTT(std::unique_ptr<MQTTClient>(new MQTTClient())) {}
MQTT::MQTT(std::unique_ptr<MQTTClient> _mqttClient)
: concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE), mqttClient(std::move(_mqttClient)), pubSub(*mqttClient)
#else
MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
#endif
@ -437,13 +422,13 @@ void MQTT::reconnect()
}
} else {
LOG_INFO("Use non-TLS-encrypted session");
pubSub.setClient(mqttClient);
pubSub.setClient(*mqttClient);
}
#else
pubSub.setClient(mqttClient);
pubSub.setClient(*mqttClient);
#endif
#elif HAS_NETWORKING
pubSub.setClient(mqttClient);
pubSub.setClient(*mqttClient);
#endif
std::pair<String, uint16_t> hostAndPort = parseHostAndPort(serverAddr, serverPort);
@ -461,7 +446,7 @@ void MQTT::reconnect()
enabled = true; // Start running background process again
runASAP = true;
reconnectCount = 0;
isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP());
isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient->remoteIP());
publishNodeInfo();
sendSubscriptions();
@ -627,8 +612,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
// mp_decoded will not be decoded when it's PKI encrypted and not directed to us
if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
// For uplinking other's packets, check if it's not OK to MQTT or if it's an older packet without the bitfield
bool dontUplink = !mp_decoded.decoded.has_bitfield ||
(mp_decoded.decoded.has_bitfield && !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK));
bool dontUplink = !mp_decoded.decoded.has_bitfield || !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK);
// check for the lowest bit of the data bitfield set false, and the use of one of the default keys.
if (!isFromUs(&mp_decoded) && !isMqttServerAddressPrivate && dontUplink &&
(ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) ||

View File

@ -22,6 +22,7 @@
#if HAS_NETWORKING
#include <PubSubClient.h>
#include <memory>
#endif
#define MAX_MQTT_QUEUE 16
@ -32,24 +33,7 @@
*/
class MQTT : private concurrency::OSThread
{
// supposedly the current version is busted:
// http://www.iotsharing.com/2017/08/how-to-use-esp32-mqtts-with-mqtts-mosquitto-broker-tls-ssl.html
#if HAS_WIFI
WiFiClient mqttClient;
#if !defined(ARCH_PORTDUINO)
#if (defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3) || defined(RPI_PICO)
WiFiClientSecure wifiSecureClient;
#endif
#endif
#endif
#if HAS_ETHERNET
EthernetClient mqttClient;
#endif
public:
#if HAS_NETWORKING
PubSubClient pubSub;
#endif
MQTT();
/**
@ -93,7 +77,29 @@ class MQTT : private concurrency::OSThread
virtual int32_t runOnce() override;
#ifndef PIO_UNIT_TESTING
private:
#endif
// supposedly the current version is busted:
// http://www.iotsharing.com/2017/08/how-to-use-esp32-mqtts-with-mqtts-mosquitto-broker-tls-ssl.html
#if HAS_WIFI
using MQTTClient = WiFiClient;
#if !defined(ARCH_PORTDUINO)
#if (defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3) || defined(RPI_PICO)
WiFiClientSecure wifiSecureClient;
#endif
#endif
#endif
#if HAS_ETHERNET
using MQTTClient = EthernetClient;
#endif
#if HAS_NETWORKING
std::unique_ptr<MQTTClient> mqttClient;
PubSubClient pubSub;
explicit MQTT(std::unique_ptr<MQTTClient> mqttClient);
#endif
std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID
std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID
std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages

View File

@ -0,0 +1,23 @@
#include "ServiceEnvelope.h"
#include "mesh-pb-constants.h"
#include <pb_decode.h>
DecodedServiceEnvelope::DecodedServiceEnvelope(const uint8_t *payload, size_t length)
: meshtastic_ServiceEnvelope(meshtastic_ServiceEnvelope_init_default),
validDecode(pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, this))
{
}
DecodedServiceEnvelope::DecodedServiceEnvelope(DecodedServiceEnvelope &&other)
: meshtastic_ServiceEnvelope(meshtastic_ServiceEnvelope_init_zero), validDecode(other.validDecode)
{
std::swap(packet, other.packet);
std::swap(channel_id, other.channel_id);
std::swap(gateway_id, other.gateway_id);
}
DecodedServiceEnvelope::~DecodedServiceEnvelope()
{
if (validDecode)
pb_release(&meshtastic_ServiceEnvelope_msg, this);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "mesh/generated/meshtastic/mqtt.pb.h"
// meshtastic_ServiceEnvelope that automatically releases dynamically allocated memory when it goes out of scope.
struct DecodedServiceEnvelope : public meshtastic_ServiceEnvelope {
DecodedServiceEnvelope(const uint8_t *payload, size_t length);
DecodedServiceEnvelope(DecodedServiceEnvelope &) = delete;
DecodedServiceEnvelope(DecodedServiceEnvelope &&);
~DecodedServiceEnvelope();
// Clients must check that this is true before using.
const bool validDecode;
};

View File

@ -90,7 +90,7 @@ void getMacAddr(uint8_t *dmac)
if (strlen(optionMac) >= 12) {
MAC_from_string(optionMac, dmac);
} else {
uint32_t hwId;
uint32_t hwId = {0};
sscanf(optionMac, "%u", &hwId);
dmac[0] = 0x80;
dmac[1] = 0;
@ -104,7 +104,7 @@ void getMacAddr(uint8_t *dmac)
exit;
} else {
struct hci_dev_info di;
struct hci_dev_info di = {0};
di.dev_id = 0;
bdaddr_t bdaddr;
int btsock;
@ -231,6 +231,9 @@ void portduinoSetup()
dmac[3] = hash[3];
dmac[4] = hash[4];
dmac[5] = hash[5];
char macBuf[13] = {0};
sprintf(macBuf, "%02X%02X%02X%02X%02X%02X", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]);
settingsStrings[mac_address] = macBuf;
}
}
@ -240,7 +243,7 @@ void portduinoSetup()
std::cout << "Please set a MAC Address in config.yaml using either MACAddress or MACAddressSource." << std::endl;
exit(EXIT_FAILURE);
}
std::cout << "MAC Address: " << std::hex << +dmac[0] << +dmac[1] << +dmac[2] << +dmac[3] << +dmac[4] << +dmac[5] << std::endl;
printf("MAC ADDRESS: %02X:%02X:%02X:%02X:%02X:%02X\n", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]);
// Rather important to set this, if not running simulated.
randomSeed(time(NULL));

View File

@ -26,7 +26,7 @@ class Ch341Hal : public RadioLibHal
{
public:
// default constructor - initializes the base HAL and any needed private members
Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
explicit Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
{
}

View File

@ -13,6 +13,8 @@
#include "mesh/generated/meshtastic/remote_hardware.pb.h"
#include <sys/types.h>
static const char *errStr = "Error decoding proto for %s message!";
std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog)
{
// the created jsonObj is immutable after creation, so

View File

@ -2,7 +2,6 @@
#include <string>
static const char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static const char *errStr = "Error decoding proto for %s message!";
class MeshPacketSerializer
{

129
src/serialization/cobs.cpp Normal file
View File

@ -0,0 +1,129 @@
#include "cobs.h"
#include <stdlib.h>
#ifdef SENSECAP_INDICATOR
cobs_encode_result cobs_encode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t *src_ptr, size_t src_len)
{
cobs_encode_result result = {0, COBS_ENCODE_OK};
if (!dst_buf_ptr || !src_ptr) {
result.status = COBS_ENCODE_NULL_POINTER;
return result;
}
const uint8_t *src_read_ptr = src_ptr;
const uint8_t *src_end_ptr = src_read_ptr + src_len;
uint8_t *dst_buf_start_ptr = dst_buf_ptr;
uint8_t *dst_buf_end_ptr = dst_buf_start_ptr + dst_buf_len;
uint8_t *dst_code_write_ptr = dst_buf_ptr;
uint8_t *dst_write_ptr = dst_code_write_ptr + 1;
uint8_t search_len = 1;
if (src_len != 0) {
for (;;) {
if (dst_write_ptr >= dst_buf_end_ptr) {
result.status = (cobs_encode_status)(result.status | (cobs_encode_status)COBS_ENCODE_OUT_BUFFER_OVERFLOW);
break;
}
uint8_t src_byte = *src_read_ptr++;
if (src_byte == 0) {
*dst_code_write_ptr = search_len;
dst_code_write_ptr = dst_write_ptr++;
search_len = 1;
if (src_read_ptr >= src_end_ptr) {
break;
}
} else {
*dst_write_ptr++ = src_byte;
search_len++;
if (src_read_ptr >= src_end_ptr) {
break;
}
if (search_len == 0xFF) {
*dst_code_write_ptr = search_len;
dst_code_write_ptr = dst_write_ptr++;
search_len = 1;
}
}
}
}
if (dst_code_write_ptr >= dst_buf_end_ptr) {
result.status = (cobs_encode_status)(result.status | (cobs_encode_status)COBS_ENCODE_OUT_BUFFER_OVERFLOW);
dst_write_ptr = dst_buf_end_ptr;
} else {
*dst_code_write_ptr = search_len;
}
result.out_len = dst_write_ptr - dst_buf_start_ptr;
return result;
}
cobs_decode_result cobs_decode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t *src_ptr, size_t src_len)
{
cobs_decode_result result = {0, COBS_DECODE_OK};
if (!dst_buf_ptr || !src_ptr) {
result.status = COBS_DECODE_NULL_POINTER;
return result;
}
const uint8_t *src_read_ptr = src_ptr;
const uint8_t *src_end_ptr = src_read_ptr + src_len;
uint8_t *dst_buf_start_ptr = dst_buf_ptr;
const uint8_t *dst_buf_end_ptr = dst_buf_start_ptr + dst_buf_len;
uint8_t *dst_write_ptr = dst_buf_ptr;
if (src_len != 0) {
for (;;) {
uint8_t len_code = *src_read_ptr++;
if (len_code == 0) {
result.status = (cobs_decode_status)(result.status | (cobs_decode_status)COBS_DECODE_ZERO_BYTE_IN_INPUT);
break;
}
len_code--;
size_t remaining_bytes = src_end_ptr - src_read_ptr;
if (len_code > remaining_bytes) {
result.status = (cobs_decode_status)(result.status | (cobs_decode_status)COBS_DECODE_INPUT_TOO_SHORT);
len_code = remaining_bytes;
}
remaining_bytes = dst_buf_end_ptr - dst_write_ptr;
if (len_code > remaining_bytes) {
result.status = (cobs_decode_status)(result.status | (cobs_decode_status)COBS_DECODE_OUT_BUFFER_OVERFLOW);
len_code = remaining_bytes;
}
for (uint8_t i = len_code; i != 0; i--) {
uint8_t src_byte = *src_read_ptr++;
if (src_byte == 0) {
result.status = (cobs_decode_status)(result.status | (cobs_decode_status)COBS_DECODE_ZERO_BYTE_IN_INPUT);
}
*dst_write_ptr++ = src_byte;
}
if (src_read_ptr >= src_end_ptr) {
break;
}
if (len_code != 0xFE) {
if (dst_write_ptr >= dst_buf_end_ptr) {
result.status = (cobs_decode_status)(result.status | (cobs_decode_status)COBS_DECODE_OUT_BUFFER_OVERFLOW);
break;
}
*dst_write_ptr++ = 0;
}
}
}
result.out_len = dst_write_ptr - dst_buf_start_ptr;
return result;
}
#endif

75
src/serialization/cobs.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef COBS_H_
#define COBS_H_
#include "configuration.h"
#ifdef SENSECAP_INDICATOR
#include <stdint.h>
#include <stdlib.h>
#define COBS_ENCODE_DST_BUF_LEN_MAX(SRC_LEN) ((SRC_LEN) + (((SRC_LEN) + 253u) / 254u))
#define COBS_DECODE_DST_BUF_LEN_MAX(SRC_LEN) (((SRC_LEN) == 0) ? 0u : ((SRC_LEN)-1u))
#define COBS_ENCODE_SRC_OFFSET(SRC_LEN) (((SRC_LEN) + 253u) / 254u)
typedef enum {
COBS_ENCODE_OK = 0x00,
COBS_ENCODE_NULL_POINTER = 0x01,
COBS_ENCODE_OUT_BUFFER_OVERFLOW = 0x02
} cobs_encode_status;
typedef struct {
size_t out_len;
cobs_encode_status status;
} cobs_encode_result;
typedef enum {
COBS_DECODE_OK = 0x00,
COBS_DECODE_NULL_POINTER = 0x01,
COBS_DECODE_OUT_BUFFER_OVERFLOW = 0x02,
COBS_DECODE_ZERO_BYTE_IN_INPUT = 0x04,
COBS_DECODE_INPUT_TOO_SHORT = 0x08
} cobs_decode_status;
typedef struct {
size_t out_len;
cobs_decode_status status;
} cobs_decode_result;
#ifdef __cplusplus
extern "C" {
#endif
/* COBS-encode a string of input bytes.
*
* dst_buf_ptr: The buffer into which the result will be written
* dst_buf_len: Length of the buffer into which the result will be written
* src_ptr: The byte string to be encoded
* src_len Length of the byte string to be encoded
*
* returns: A struct containing the success status of the encoding
* operation and the length of the result (that was written to
* dst_buf_ptr)
*/
cobs_encode_result cobs_encode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t *src_ptr, size_t src_len);
/* Decode a COBS byte string.
*
* dst_buf_ptr: The buffer into which the result will be written
* dst_buf_len: Length of the buffer into which the result will be written
* src_ptr: The byte string to be decoded
* src_len Length of the byte string to be decoded
*
* returns: A struct containing the success status of the decoding
* operation and the length of the result (that was written to
* dst_buf_ptr)
*/
cobs_decode_result cobs_decode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t *src_ptr, size_t src_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SENSECAP_INDICATOR */
#endif /* COBS_H_ */

View File

@ -58,7 +58,7 @@ RTC_DATA_ATTR int bootCount = 0;
*/
void setCPUFast(bool on)
{
#if defined(ARCH_ESP32) && HAS_WIFI
#if defined(ARCH_ESP32) && HAS_WIFI && !HAS_TFT
if (isWifiAvailable()) {
/*
@ -271,6 +271,12 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
gpio_hold_en((gpio_num_t)BUTTON_PIN);
}
#endif
#ifdef SENSECAP_INDICATOR
// Portexpander definition does not pass GPIO_IS_VALID_OUTPUT_GPIO
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
gpio_hold_en((gpio_num_t)LORA_CS);
#else
if (GPIO_IS_VALID_OUTPUT_GPIO(LORA_CS)) {
// LoRa CS (RADIO_NSS) needs to stay HIGH, even during deep sleep
pinMode(LORA_CS, OUTPUT);
@ -278,6 +284,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
gpio_hold_en((gpio_num_t)LORA_CS);
}
#endif
#endif
#ifdef HAS_PMU
if (pmu_found && PMU) {
@ -384,6 +391,9 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_enable(pin, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
#endif
#ifdef INPUTDRIVER_ENCODER_BTN
gpio_wakeup_enable((gpio_num_t)INPUTDRIVER_ENCODER_BTN, GPIO_INTR_LOW_LEVEL);
#endif
#ifdef T_WATCH_S3
gpio_wakeup_enable((gpio_num_t)SCREEN_TOUCH_INT, GPIO_INTR_LOW_LEVEL);
#endif

View File

@ -49,6 +49,7 @@
**********************************************************************************************************************/
#include "xmodem.h"
#include "SPILock.h"
#ifdef FSCom
@ -119,8 +120,11 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
if ((xmodemPacket.seq == 0) && !isReceiving && !isTransmitting) {
// NULL packet has the destination filename
memcpy(filename, &xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
if (xmodemPacket.control == meshtastic_XModem_Control_SOH) { // Receive this file and put to Flash
spiLock->lock();
file = FSCom.open(filename, FILE_O_WRITE);
spiLock->unlock();
if (file) {
sendControl(meshtastic_XModem_Control_ACK);
isReceiving = true;
@ -132,14 +136,18 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
break;
} else { // Transmit this file from Flash
LOG_INFO("XModem: Transmit file %s", filename);
spiLock->lock();
file = FSCom.open(filename, FILE_O_READ);
spiLock->unlock();
if (file) {
packetno = 1;
isTransmitting = true;
xmodemStore = meshtastic_XModem_init_zero;
xmodemStore.control = meshtastic_XModem_Control_SOH;
xmodemStore.seq = packetno;
spiLock->lock();
xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes));
spiLock->unlock();
xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size);
LOG_DEBUG("XModem: STX Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size);
if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) {
@ -159,7 +167,9 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
if ((xmodemPacket.seq == packetno) &&
check(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size, xmodemPacket.crc16)) {
// valid packet
spiLock->lock();
file.write(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
spiLock->unlock();
sendControl(meshtastic_XModem_Control_ACK);
packetno++;
break;
@ -178,16 +188,21 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
case meshtastic_XModem_Control_EOT:
// End of transmission
sendControl(meshtastic_XModem_Control_ACK);
spiLock->lock();
file.flush();
file.close();
spiLock->unlock();
isReceiving = false;
break;
case meshtastic_XModem_Control_CAN:
// Cancel transmission and remove file
sendControl(meshtastic_XModem_Control_ACK);
spiLock->lock();
file.flush();
file.close();
FSCom.remove(filename);
spiLock->unlock();
isReceiving = false;
break;
case meshtastic_XModem_Control_ACK:
@ -195,7 +210,9 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
if (isTransmitting) {
if (isEOT) {
sendControl(meshtastic_XModem_Control_EOT);
spiLock->lock();
file.close();
spiLock->unlock();
LOG_INFO("XModem: Finished send file %s", filename);
isTransmitting = false;
isEOT = false;
@ -206,7 +223,9 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
xmodemStore = meshtastic_XModem_init_zero;
xmodemStore.control = meshtastic_XModem_Control_SOH;
xmodemStore.seq = packetno;
spiLock->lock();
xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes));
spiLock->unlock();
xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size);
LOG_DEBUG("XModem: ACK Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size);
if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) {
@ -224,7 +243,9 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
if (isTransmitting) {
if (--retrans <= 0) {
sendControl(meshtastic_XModem_Control_CAN);
spiLock->lock();
file.close();
spiLock->unlock();
LOG_INFO("XModem: Retransmit timeout, cancel file %s", filename);
isTransmitting = false;
break;
@ -232,8 +253,11 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
xmodemStore = meshtastic_XModem_init_zero;
xmodemStore.control = meshtastic_XModem_Control_SOH;
xmodemStore.seq = packetno;
spiLock->lock();
file.seek((packetno - 1) * sizeof(meshtastic_XModem_buffer_t::bytes));
xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes));
spiLock->unlock();
xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size);
LOG_DEBUG("XModem: NAK Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size);
if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) {

View File

@ -178,7 +178,7 @@ build_flags = ${mesh_tab_base.build_flags}
-D LGFX_TOUCH_Y_MIN=0
-D LGFX_TOUCH_Y_MAX=479
-D LGFX_TOUCH_ROTATION=0
-D LGFX_TOUCH_I2C_FREQ=1000000
-D LGFX_TOUCH_I2C_FREQ=400000
; 3.5" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005006893699919.html
[env:mesh-tab-3-5-IPS-capacitive]
@ -204,7 +204,7 @@ build_flags = ${mesh_tab_base.build_flags}
-D LGFX_TOUCH_Y_MIN=0
-D LGFX_TOUCH_Y_MAX=479
-D LGFX_TOUCH_ROTATION=1
-D LGFX_TOUCH_I2C_FREQ=1000000
-D LGFX_TOUCH_I2C_FREQ=400000
; 4.0" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005007082906950.html
[env:mesh-tab-4-0-IPS-capacitive]
@ -230,4 +230,4 @@ build_flags = ${mesh_tab_base.build_flags}
-D LGFX_TOUCH_Y_MIN=0
-D LGFX_TOUCH_Y_MAX=479
-D LGFX_TOUCH_ROTATION=1
-D LGFX_TOUCH_I2C_FREQ=1000000
-D LGFX_TOUCH_I2C_FREQ=400000

View File

@ -38,15 +38,15 @@ static const uint8_t A3 = PIN_A3;
#define PIN_SERIAL2_RX (5ul)
// SPI
#define PIN_SPI0_MISO (12u)
#define PIN_SPI0_MOSI (11u)
#define PIN_SPI0_SCK (10u)
#define PIN_SPI0_SS (13u)
#define PIN_SPI1_MISO (12u)
#define PIN_SPI1_MOSI (11u)
#define PIN_SPI1_SCK (10u)
#define PIN_SPI1_SS (13u)
#define PIN_SPI1_MISO (16u)
#define PIN_SPI1_MOSI (19u)
#define PIN_SPI1_SCK (18u)
#define PIN_SPI1_SS (17u)
#define PIN_SPI0_MISO (16u)
#define PIN_SPI0_MOSI (19u)
#define PIN_SPI0_SCK (18u)
#define PIN_SPI0_SS (17u)
// Wire
#define PIN_WIRE0_SDA (2u)
@ -65,4 +65,4 @@ static const uint8_t MISO = PIN_SPI0_MISO;
static const uint8_t SCK = PIN_SPI0_SCK;
static const uint8_t SDA = PIN_WIRE0_SDA;
static const uint8_t SCL = PIN_WIRE0_SCL;
static const uint8_t SCL = PIN_WIRE0_SCL;

View File

@ -12,7 +12,10 @@ build_flags = ${rp2040_base.build_flags}
-Ivariants/rak11310
-DDEBUG_RP2040_PORT=Serial
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus"
build_src_filter = ${rp2040_base.build_src_filter} +<../variants/rak11310> +<mesh/eth/> +<mesh/api/> +<mqtt/>
lib_deps =
${rp2040_base.lib_deps}
${networking_base.lib_deps}
https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2
debug_build_flags = ${rp2040_base.build_flags}, -g
debug_tool = cmsis-dap ; for e.g. Picotool
debug_tool = cmsis-dap ; for e.g. Picotool

View File

@ -28,10 +28,10 @@
// RAK BSP somehow uses SPI1 instead of SPI0
#define HW_SPI1_DEVICE
#define LORA_SCK PIN_SPI0_SCK
#define LORA_MOSI PIN_SPI0_MOSI
#define LORA_MISO PIN_SPI0_MISO
#define LORA_CS PIN_SPI0_SS
#define LORA_SCK (10u)
#define LORA_MOSI (11u)
#define LORA_MISO (12u)
#define LORA_CS (13u)
#define LORA_DIO0 RADIOLIB_NC
#define LORA_RESET 14
@ -49,3 +49,10 @@
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
#define HAS_ETHERNET 1
#define PIN_ETHERNET_RESET 7 // IO3
#define PIN_ETHERNET_SS 17
#define ETH_SPI_PORT SPI
#define PIN_ETH_POWER_EN 22

View File

@ -2,7 +2,7 @@
[env:seeed-sensecap-indicator]
extends = esp32s3_base
platform_packages =
platformio/framework-arduinoespressif32 @ https://github.com/mverch67/arduino-esp32.git#add_tca9535 ; based on 2.0.16
platformio/framework-arduinoespressif32 @ https://github.com/mverch67/arduino-esp32.git#aef7fef6de3329ed6f75512d46d63bba12b09bb5 ; add_tca9535 (based on 2.0.16)
board = seeed-sensecap-indicator
board_check = true
@ -24,5 +24,5 @@ build_flags = ${esp32_base.build_flags}
lib_deps = ${esp32s3_base.lib_deps}
https://github.com/mverch67/LovyanGFX#develop
earlephilhower/ESP8266Audio@^1.9.7
earlephilhower/ESP8266SAM@^1.0.1
earlephilhower/ESP8266Audio@^1.9.9
earlephilhower/ESP8266SAM@^1.0.1

View File

@ -1,6 +1,12 @@
#define I2C_SDA 39
#define I2C_SCL 40
// This board has a serial coprocessor for sensor readings
#define SENSOR_RP2040_TXD 19
#define SENSOR_RP2040_RXD 20
#define SENSOR_PORT_NUM 2
#define SENSOR_BAUD_RATE 115200
#define BUTTON_PIN 38
// #define BUTTON_NEED_PULLUP
@ -19,8 +25,7 @@
#define ST7701_BL 45
#define ST7701_SPI_HOST SPI2_HOST
#define ST7701_BACKLIGHT_EN 45
#define SPI_FREQUENCY 20000000
#define SPI_READ_FREQUENCY 16000000
#define SPI_FREQUENCY 12000000
#define TFT_HEIGHT 480
#define TFT_WIDTH 480
#define TFT_OFFSET_X 0

View File

@ -6,7 +6,7 @@ board_check = true
upload_protocol = esptool
#upload_port = COM29
build_flags = ${esp32_base.build_flags}
build_flags = ${esp32s3_base.build_flags}
-DT_DECK
-DBOARD_HAS_PSRAM
-DMAX_THREADS=40
@ -16,4 +16,4 @@ build_flags = ${esp32_base.build_flags}
lib_deps = ${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@^1.1.9
earlephilhower/ESP8266Audio@^1.9.9
earlephilhower/ESP8266SAM@^1.0.1
earlephilhower/ESP8266SAM@^1.0.1

View File

@ -27,8 +27,10 @@
#define SLEEP_TIME 120
#ifndef HAS_TFT
#define BUTTON_PIN 0
// #define BUTTON_NEED_PULLUP
#endif
#define GPS_DEFAULT_NOT_PRESENT 1
#define GPS_RX_PIN 44
#define GPS_TX_PIN 43
@ -60,7 +62,7 @@
#define TB_DOWN 15
#define TB_LEFT 1
#define TB_RIGHT 2
#define TB_PRESS BUTTON_PIN
#define TB_PRESS 0 // BUTTON_PIN
// microphone
#define ES7210_SCK 47
@ -98,4 +100,4 @@
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
// code)

View File

@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 5
build = 18
build = 19