mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-01 15:40:49 +00:00
Compare commits
74 Commits
v2.0.11.89
...
v2.0.13.7e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e27729dae | ||
|
|
d49f19a67c | ||
|
|
c33569f833 | ||
|
|
7936c7c8ae | ||
|
|
2f3f19fda7 | ||
|
|
c89ca50cc4 | ||
|
|
867e55b9e7 | ||
|
|
28b1616630 | ||
|
|
42122f3d0f | ||
|
|
f905763161 | ||
|
|
bd2b766a36 | ||
|
|
bd51cbd721 | ||
|
|
e3fd17772d | ||
|
|
440b965e71 | ||
|
|
cf0fb13a12 | ||
|
|
c0166773e8 | ||
|
|
fc775012ea | ||
|
|
d7a71e46aa | ||
|
|
721f87af7e | ||
|
|
a354cebd88 | ||
|
|
97680ca730 | ||
|
|
17a2589b35 | ||
|
|
4f71ab07c9 | ||
|
|
1f6a8eae86 | ||
|
|
ab9d0ba543 | ||
|
|
e13fb9919e | ||
|
|
a3b93a4dcf | ||
|
|
6b0f18e1e4 | ||
|
|
312ef790fc | ||
|
|
713f7d5996 | ||
|
|
3985008e06 | ||
|
|
8f2155f8f9 | ||
|
|
1fc5d70221 | ||
|
|
e7d425ef6e | ||
|
|
9c1cfe9358 | ||
|
|
0181e186ac | ||
|
|
86748bf88e | ||
|
|
5fd00b2538 | ||
|
|
5cec370cf5 | ||
|
|
81ee6dd799 | ||
|
|
49172e416e | ||
|
|
57f185c26b | ||
|
|
3a8ffe7ac2 | ||
|
|
d8b85f9a09 | ||
|
|
4d7402839e | ||
|
|
8465467aa8 | ||
|
|
2a0b8093ea | ||
|
|
96fa5dafb8 | ||
|
|
be2d3f7769 | ||
|
|
b283e526bf | ||
|
|
03c5dfc7a8 | ||
|
|
52cfd62031 | ||
|
|
c98dc4cae0 | ||
|
|
f21212dd3e | ||
|
|
0e0ccad489 | ||
|
|
9f1c77da76 | ||
|
|
7c1c49b8ab | ||
|
|
f4099261df | ||
|
|
450e7362f6 | ||
|
|
0b9c8e62ea | ||
|
|
09efbb75b1 | ||
|
|
e5d9f1f946 | ||
|
|
2400dd43b3 | ||
|
|
988d5af69b | ||
|
|
78b6916b1b | ||
|
|
9740f0a505 | ||
|
|
b64a74c0c5 | ||
|
|
d9f0dc7ea4 | ||
|
|
30ae4c2a38 | ||
|
|
d077be1496 | ||
|
|
4f26a2a74a | ||
|
|
41f9636ba3 | ||
|
|
b544163c3f | ||
|
|
6d989a29dd |
7
.github/workflows/build_esp32.yml
vendored
7
.github/workflows/build_esp32.yml
vendored
@@ -29,6 +29,13 @@ jobs:
|
||||
tar -xf build.tar -C data/static
|
||||
rm build.tar
|
||||
|
||||
- name: Remove debug flags for release
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
run: |
|
||||
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
|
||||
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
|
||||
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
|
||||
|
||||
- name: Build ESP32
|
||||
run: bin/build-esp32.sh ${{ inputs.board }}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ build_flags =
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DDEBUG_HEAP
|
||||
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
@@ -27,6 +27,7 @@ build_flags =
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DHAS_BLUETOOTH=0
|
||||
-DDEBUG_HEAP
|
||||
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
@@ -26,6 +26,7 @@ build_flags =
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DDEBUG_HEAP
|
||||
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
@@ -8,7 +8,7 @@ build_flags =
|
||||
${arduino_base.build_flags} -Wno-unused-variable
|
||||
-Isrc/platform/nrf52
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/rp2040> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/rp2040> -<mesh/eth/>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ build_flags =
|
||||
-D__PLAT_RP2040__
|
||||
# -D _POSIX_THREADS
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
lib_deps =
|
||||
|
||||
@@ -10,7 +10,7 @@ build_flags =
|
||||
# Arduino/PlatformIO framework-arduinoststm32 package does not presently have SUBGHZSPI support
|
||||
# -DPIN_SPI_MOSI=PINSUBGHZSPIMOSI -DPIN_SPI_MISO=PINSUBGHZSPIMISO -DPIN_SPI_SCK=PINSUBGHZSPISCK
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<mqtt/> -<graphics> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<mqtt/> -<graphics> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
|
||||
|
||||
Submodule protobufs updated: 6048ecd2f8...e00b5ba7d0
@@ -170,8 +170,8 @@ void fsInit()
|
||||
#ifdef FSCom
|
||||
if (!FSBegin())
|
||||
{
|
||||
LOG_ERROR("Filesystem mount Failed. Formatting...\n");
|
||||
assert(0); // FIXME - report failure to phone
|
||||
LOG_ERROR("Filesystem mount Failed.\n");
|
||||
// assert(0); This auto-formats the partition, so no need to fail here.
|
||||
}
|
||||
#ifdef ARCH_ESP32
|
||||
LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes());
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// RP2040
|
||||
#include "LittleFS.h"
|
||||
#define FSCom LittleFS
|
||||
#define FSBegin() FSCom.begin()
|
||||
#define FSBegin() FSCom.begin() // set autoformat
|
||||
#define FILE_O_WRITE "w"
|
||||
#define FILE_O_READ "r"
|
||||
#endif
|
||||
@@ -26,7 +26,7 @@
|
||||
// ESP32 version
|
||||
#include "LittleFS.h"
|
||||
#define FSCom LittleFS
|
||||
#define FSBegin() FSCom.begin(true)
|
||||
#define FSBegin() FSCom.begin(true) // format on failure
|
||||
#define FILE_O_WRITE "w"
|
||||
#define FILE_O_READ "r"
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@
|
||||
// NRF52 version
|
||||
#include "InternalFileSystem.h"
|
||||
#define FSCom InternalFS
|
||||
#define FSBegin() FSCom.begin()
|
||||
#define FSBegin() FSCom.begin() // InternalFS formats on failure
|
||||
using namespace Adafruit_LittleFS_Namespace;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
|
||||
template <class T> class Observable;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "RTC.h"
|
||||
#include "NodeDB.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
// #include "wifi/WiFiServerAPI.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
@@ -27,10 +26,6 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
// FIXME - clean this up, the whole relationship of this class to SerialConsole to TCP/bluetooth debug log output is kinda messed up. But for now, just have this hack to
|
||||
// optionally send chars to TCP also
|
||||
//WiFiServerPort::debugOut(c);
|
||||
|
||||
if (!config.has_lora || config.device.serial_enabled)
|
||||
dest->write(c);
|
||||
|
||||
@@ -108,3 +103,31 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) {
|
||||
const char alphabet[17] = "0123456789abcdef";
|
||||
log(logLevel, " +------------------------------------------------+ +----------------+\n");
|
||||
log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n");
|
||||
for (uint16_t i = 0; i < len; i += 16) {
|
||||
if (i % 128 == 0)
|
||||
log(logLevel, " +------------------------------------------------+ +----------------+\n");
|
||||
char s[] = "| | | |\n";
|
||||
uint8_t ix = 1, iy = 52;
|
||||
for (uint8_t j = 0; j < 16; j++) {
|
||||
if (i + j < len) {
|
||||
uint8_t c = buf[i + j];
|
||||
s[ix++] = alphabet[(c >> 4) & 0x0F];
|
||||
s[ix++] = alphabet[c & 0x0F];
|
||||
ix++;
|
||||
if (c > 31 && c < 128) s[iy++] = c;
|
||||
else s[iy++] = '.';
|
||||
}
|
||||
}
|
||||
uint8_t index = i / 16;
|
||||
if (i < 256) log(logLevel, " ");
|
||||
log(logLevel, "%02x",index);
|
||||
log(logLevel, ".");
|
||||
log(logLevel, s);
|
||||
}
|
||||
log(logLevel, " +------------------------------------------------+ +----------------+\n");
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ class RedirectablePrint : public Print
|
||||
|
||||
/** like printf but va_list based */
|
||||
size_t vprintf(const char *format, va_list arg);
|
||||
|
||||
void hexDump(const char *logLevel, unsigned char *buf, uint16_t len);
|
||||
};
|
||||
|
||||
class NoopPrint : public Print
|
||||
|
||||
@@ -20,9 +20,7 @@ void consolePrintf(const char *format, ...)
|
||||
va_start(arg, format);
|
||||
console->vprintf(format, arg);
|
||||
va_end(arg);
|
||||
#ifdef ARCH_ESP32
|
||||
console->flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole")
|
||||
@@ -51,6 +49,10 @@ int32_t SerialConsole::runOnce()
|
||||
return runOncePart();
|
||||
}
|
||||
|
||||
void SerialConsole::flush() {
|
||||
Port.flush();
|
||||
}
|
||||
|
||||
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages
|
||||
bool SerialConsole::checkIsConnected()
|
||||
{
|
||||
|
||||
@@ -26,6 +26,8 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
|
||||
@@ -117,6 +117,31 @@ float AirTime::utilizationTXPercent()
|
||||
return (float(sum) / float(MS_IN_HOUR)) * 100;
|
||||
}
|
||||
|
||||
bool AirTime::isTxAllowedChannelUtil(bool polite)
|
||||
{
|
||||
uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent);
|
||||
if (channelUtilizationPercent() < percentage) {
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("Channel utilization is >%d percent. Skipping this opportunity to send.\n", percentage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AirTime::isTxAllowedAirUtil()
|
||||
{
|
||||
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
|
||||
if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) {
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("Tx air utilization is >%d percent. Skipping this opportunity to send.\n", myRegion->dutyCycle * polite_duty_cycle_percent / 100);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the amount of minutes we have to be silent before we can send again
|
||||
uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include "MeshRadio.h"
|
||||
|
||||
/*
|
||||
TX_LOG - Time on air this device has transmitted
|
||||
@@ -59,12 +60,17 @@ class AirTime : private concurrency::OSThread
|
||||
uint32_t getSecondsSinceBoot();
|
||||
uint32_t *airtimeReport(reportTypes reportType);
|
||||
uint8_t getSilentMinutes(float txPercent, float dutyCycle);
|
||||
bool isTxAllowedChannelUtil(bool polite=false);
|
||||
bool isTxAllowedAirUtil();
|
||||
|
||||
private:
|
||||
bool firstTime = true;
|
||||
uint8_t lastUtilPeriod = 0;
|
||||
uint8_t lastUtilPeriodTX = 0;
|
||||
uint32_t secSinceBoot = 0;
|
||||
uint8_t max_channel_util_percent = 40;
|
||||
uint8_t polite_channel_util_percent = 25;
|
||||
uint8_t polite_duty_cycle_percent = 50; // half of Duty Cycle allowance is ok for metadata
|
||||
|
||||
struct airtimeStruct {
|
||||
uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "configuration.h"
|
||||
#include "NotifiedWorkerThread.h"
|
||||
#include "main.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "RTC.h"
|
||||
#include "configuration.h"
|
||||
#include "sleep.h"
|
||||
#include <assert.h>
|
||||
|
||||
// If we have a serial GPS port it will not be null
|
||||
#ifdef GPS_SERIAL_NUM
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiServerAPI.h"
|
||||
#include "mesh/api/WiFiServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET
|
||||
#include "mesh/eth/ethServerAPI.h"
|
||||
#include "mesh/api/ethServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
|
||||
@@ -239,7 +239,7 @@ void setup()
|
||||
#ifdef RAK4630
|
||||
// We need to enable 3.3V periphery in order to scan it
|
||||
pinMode(PIN_3V3_EN, OUTPUT);
|
||||
digitalWrite(PIN_3V3_EN, 1);
|
||||
digitalWrite(PIN_3V3_EN, HIGH);
|
||||
#endif
|
||||
|
||||
// Currently only the tbeam has a PMU
|
||||
|
||||
318
src/memtest.cpp
318
src/memtest.cpp
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* mtest - Perform a memory test
|
||||
*
|
||||
* (C) Copyright 2000
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
#include "configuration.h"
|
||||
|
||||
/*
|
||||
* Perform a memory test. A more complete alternative test can be
|
||||
* configured using CONFIG_CMD_MTEST_ALTERNATIVE. The complete test
|
||||
* loops until interrupted by ctrl-c or by a failure of one of the
|
||||
* sub-tests.
|
||||
*/
|
||||
#ifdef CONFIG_CMD_MTEST_ALTERNATIVE
|
||||
static int mem_test(uint32_t _start, uint32_t _end, uint32_t pattern_unused)
|
||||
{
|
||||
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||
volatile uint32_t *end = (volatile uint32_t *)_end;
|
||||
volatile uint32_t *addr;
|
||||
uint32_t val;
|
||||
uint32_t readback;
|
||||
vu_long addr_mask;
|
||||
vu_long offset;
|
||||
vu_long test_offset;
|
||||
vu_long pattern;
|
||||
vu_long temp;
|
||||
vu_long anti_pattern;
|
||||
vu_long num_words;
|
||||
#ifdef CFG_MEMTEST_SCRATCH
|
||||
volatile uint32_t *dummy = (vu_long *)CFG_MEMTEST_SCRATCH;
|
||||
#else
|
||||
volatile uint32_t *dummy = start;
|
||||
#endif
|
||||
int j;
|
||||
int iterations = 1;
|
||||
static const uint32_t bitpattern[] = {
|
||||
0x00000001, /* single bit */
|
||||
0x00000003, /* two adjacent bits */
|
||||
0x00000007, /* three adjacent bits */
|
||||
0x0000000F, /* four adjacent bits */
|
||||
0x00000005, /* two non-adjacent bits */
|
||||
0x00000015, /* three non-adjacent bits */
|
||||
0x00000055, /* four non-adjacent bits */
|
||||
0xaaaaaaaa, /* alternating 1/0 */
|
||||
};
|
||||
/* XXX: enforce alignment of start and end? */
|
||||
for (;;) {
|
||||
if (ctrlc()) {
|
||||
putchar('\n');
|
||||
return 1;
|
||||
}
|
||||
printf("Iteration: %6d\r", iterations);
|
||||
iterations++;
|
||||
/*
|
||||
* Data line test: write a pattern to the first
|
||||
* location, write the 1's complement to a 'parking'
|
||||
* address (changes the state of the data bus so a
|
||||
* floating bus doen't give a false OK), and then
|
||||
* read the value back. Note that we read it back
|
||||
* into a variable because the next time we read it,
|
||||
* it might be right (been there, tough to explain to
|
||||
* the quality guys why it prints a failure when the
|
||||
* "is" and "should be" are obviously the same in the
|
||||
* error message).
|
||||
*
|
||||
* Rather than exhaustively testing, we test some
|
||||
* patterns by shifting '1' bits through a field of
|
||||
* '0's and '0' bits through a field of '1's (i.e.
|
||||
* pattern and ~pattern).
|
||||
*/
|
||||
addr = start;
|
||||
/* XXX */
|
||||
if (addr == dummy)
|
||||
++addr;
|
||||
for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) {
|
||||
val = bitpattern[j];
|
||||
for (; val != 0; val <<= 1) {
|
||||
*addr = val;
|
||||
*dummy = ~val; /* clear the test data off of the bus */
|
||||
readback = *addr;
|
||||
if (readback != val) {
|
||||
printf("FAILURE (data line): "
|
||||
"expected 0x%08lx, actual 0x%08lx at address 0x%p\n",
|
||||
val, readback, addr);
|
||||
}
|
||||
*addr = ~val;
|
||||
*dummy = val;
|
||||
readback = *addr;
|
||||
if (readback != ~val) {
|
||||
printf("FAILURE (data line): "
|
||||
"Is 0x%08lx, should be 0x%08lx at address 0x%p\n",
|
||||
readback, ~val, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Based on code whose Original Author and Copyright
|
||||
* information follows: Copyright (c) 1998 by Michael
|
||||
* Barr. This software is placed into the public
|
||||
* domain and may be used for any purpose. However,
|
||||
* this notice must not be changed or removed and no
|
||||
* warranty is either expressed or implied by its
|
||||
* publication or distribution.
|
||||
*/
|
||||
/*
|
||||
* Address line test
|
||||
*
|
||||
* Description: Test the address bus wiring in a
|
||||
* memory region by performing a walking
|
||||
* 1's test on the relevant bits of the
|
||||
* address and checking for aliasing.
|
||||
* This test will find single-bit
|
||||
* address failures such as stuck -high,
|
||||
* stuck-low, and shorted pins. The base
|
||||
* address and size of the region are
|
||||
* selected by the caller.
|
||||
*
|
||||
* Notes: For best results, the selected base
|
||||
* address should have enough LSB 0's to
|
||||
* guarantee single address bit changes.
|
||||
* For example, to test a 64-Kbyte
|
||||
* region, select a base address on a
|
||||
* 64-Kbyte boundary. Also, select the
|
||||
* region size as a power-of-two if at
|
||||
* all possible.
|
||||
*
|
||||
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||
*
|
||||
* ## NOTE ## Be sure to specify start and end
|
||||
* addresses such that addr_mask has
|
||||
* lots of bits set. For example an
|
||||
* address range of 01000000 02000000 is
|
||||
* bad while a range of 01000000
|
||||
* 01ffffff is perfect.
|
||||
*/
|
||||
addr_mask = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long);
|
||||
pattern = (vu_long)0xaaaaaaaa;
|
||||
anti_pattern = (vu_long)0x55555555;
|
||||
debug("%s:%d: addr mask = 0x%.8lx\n", __FUNCTION__, __LINE__, addr_mask);
|
||||
/*
|
||||
* Write the default pattern at each of the
|
||||
* power-of-two offsets.
|
||||
*/
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1)
|
||||
start[offset] = pattern;
|
||||
/*
|
||||
* Check for address bits stuck high.
|
||||
*/
|
||||
test_offset = 0;
|
||||
start[test_offset] = anti_pattern;
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||
temp = start[offset];
|
||||
if (temp != pattern) {
|
||||
printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
start[test_offset] = pattern;
|
||||
/*
|
||||
* Check for addr bits stuck low or shorted.
|
||||
*/
|
||||
for (test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1) {
|
||||
start[test_offset] = anti_pattern;
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||
temp = start[offset];
|
||||
if ((temp != pattern) && (offset != test_offset)) {
|
||||
printf("\nFAILURE: Address bit stuck low or shorted @"
|
||||
" 0x%.8lx: expected 0x%.8lx, actual 0x%.8lx\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
start[test_offset] = pattern;
|
||||
}
|
||||
/*
|
||||
* Description: Test the integrity of a physical
|
||||
* memory device by performing an
|
||||
* increment/decrement test over the
|
||||
* entire region. In the process every
|
||||
* storage bit in the device is tested
|
||||
* as a zero and a one. The base address
|
||||
* and the size of the region are
|
||||
* selected by the caller.
|
||||
*
|
||||
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||
*/
|
||||
num_words = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long) + 1;
|
||||
/*
|
||||
* Fill memory with a known pattern.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
start[offset] = pattern;
|
||||
}
|
||||
/*
|
||||
* Check each location and invert it for the second pass.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
temp = start[offset];
|
||||
if (temp != pattern) {
|
||||
printf("\nFAILURE (read/write) @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
anti_pattern = ~pattern;
|
||||
start[offset] = anti_pattern;
|
||||
}
|
||||
/*
|
||||
* Check each location for the inverted pattern and zero it.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
anti_pattern = ~pattern;
|
||||
temp = start[offset];
|
||||
if (temp != anti_pattern) {
|
||||
printf("\nFAILURE (read/write): @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||
(uint32_t)&start[offset], anti_pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
start[offset] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int mem_test(uint32_t *_start, size_t len, bool doRead = true, bool doWrite = true)
|
||||
{
|
||||
volatile uint32_t *addr;
|
||||
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||
const volatile uint32_t *end = start + len / sizeof(uint32_t);
|
||||
uint32_t pattern = 0;
|
||||
uint32_t val;
|
||||
uint32_t readback;
|
||||
uint32_t incr;
|
||||
int rcode = 0;
|
||||
incr = 1;
|
||||
|
||||
//LOG_DEBUG("memtest read=%d, write=%d\n", doRead, doWrite);
|
||||
|
||||
if (doWrite) {
|
||||
//LOG_DEBUG("writing\n");
|
||||
for (addr = start, val = pattern; addr < end; addr++) {
|
||||
*addr = val;
|
||||
val += incr;
|
||||
}
|
||||
}
|
||||
|
||||
if (doRead) {
|
||||
//LOG_DEBUG("reading\n");
|
||||
for (addr = start, val = pattern; addr < end; addr++) {
|
||||
readback = *addr;
|
||||
if (readback != val) {
|
||||
LOG_ERROR("Mem error @ 0x%08X: "
|
||||
"found 0x%08lX, expected 0x%08lX\n",
|
||||
addr, readback, val);
|
||||
rcode++;
|
||||
}
|
||||
val += incr;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Flip the pattern each time to make lots of zeros and
|
||||
* then, the next time, lots of ones. We decrement
|
||||
* the "negative" patterns and increment the "positive"
|
||||
* patterns to preserve this feature.
|
||||
*/
|
||||
if(pattern & 0x80000000) {
|
||||
pattern = -pattern; /* complement & increment */
|
||||
}
|
||||
else {
|
||||
pattern = ~pattern;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rcode;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TESTBUF_LEN 16384
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void doMemTest()
|
||||
{
|
||||
static uint32_t *testBuf;
|
||||
static int iter;
|
||||
|
||||
if (!testBuf)
|
||||
testBuf = (uint32_t *)malloc(TESTBUF_LEN);
|
||||
|
||||
assert(testBuf);
|
||||
if (mem_test(testBuf, TESTBUF_LEN, iter % 2 == 1, iter % 2 == 0) > 0)
|
||||
assert(0); // FIXME report error better
|
||||
|
||||
iter++;
|
||||
}
|
||||
@@ -191,15 +191,26 @@ void Channels::onConfigChanged()
|
||||
|
||||
Channel &Channels::getByIndex(ChannelIndex chIndex)
|
||||
{
|
||||
assert(chIndex < channelFile.channels_count); // This should be equal to MAX_NUM_CHANNELS
|
||||
Channel *ch = channelFile.channels + chIndex;
|
||||
return *ch;
|
||||
// remove this assert cause malformed packets can make our firmware reboot here.
|
||||
if (chIndex < channelFile.channels_count) { // This should be equal to MAX_NUM_CHANNELS
|
||||
Channel *ch = channelFile.channels + chIndex;
|
||||
return *ch;
|
||||
} else {
|
||||
LOG_ERROR("Invalid channel index %d > %d, malformed packet received?\n", chIndex , channelFile.channels_count);
|
||||
|
||||
static Channel *ch = (Channel *)malloc(sizeof(Channel));
|
||||
memset(ch, 0, sizeof(Channel));
|
||||
// ch.index -1 means we don't know the channel locally and need to look it up by settings.name
|
||||
// not sure this is handled right everywhere
|
||||
ch->index = -1;
|
||||
return *ch;
|
||||
}
|
||||
}
|
||||
|
||||
Channel &Channels::getByName(const char* chName)
|
||||
{
|
||||
for (ChannelIndex i = 0; i < getNumChannels(); i++) {
|
||||
if (strcasecmp(channelFile.channels[i].settings.name, chName) == 0) {
|
||||
if (strcasecmp(getGlobalId(i), chName) == 0) {
|
||||
return channelFile.channels[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,23 @@
|
||||
#include "SX126xInterface.cpp"
|
||||
#include "SX128xInterface.h"
|
||||
#include "SX128xInterface.cpp"
|
||||
#include "api/ServerAPI.h"
|
||||
#include "api/ServerAPI.cpp"
|
||||
|
||||
// We need this declaration for proper linking in derived classes
|
||||
template class SX126xInterface<SX1262>;
|
||||
template class SX126xInterface<SX1268>;
|
||||
template class SX126xInterface<LLCC68>;
|
||||
template class SX128xInterface<SX1280>;
|
||||
|
||||
#if HAS_ETHERNET
|
||||
#include "api/ethServerAPI.h"
|
||||
template class ServerAPI<EthernetClient>;
|
||||
template class APIServerPort<ethServerAPI, EthernetServer>;
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI
|
||||
#include "api/WiFiServerAPI.h"
|
||||
template class ServerAPI<WiFiClient>;
|
||||
template class APIServerPort<WiFiServerAPI, WiFiServer>;
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "configuration.h"
|
||||
#include "MeshPacketQueue.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "MeshTypes.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <queue>
|
||||
|
||||
|
||||
@@ -26,6 +25,12 @@ class MeshPacketQueue
|
||||
/** return true if the queue is empty */
|
||||
bool empty();
|
||||
|
||||
/** return amount of free packets in Queue */
|
||||
size_t getFree() { return maxLen - queue.size(); }
|
||||
|
||||
/** return total size of the Queue */
|
||||
size_t getMaxLen() { return maxLen; }
|
||||
|
||||
MeshPacket *dequeue();
|
||||
|
||||
MeshPacket *getFront();
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <string>
|
||||
|
||||
#include "GPS.h"
|
||||
//#include "MeshBluetoothService.h"
|
||||
#include "../concurrency/Periodic.h"
|
||||
#include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
|
||||
#include "MeshService.h"
|
||||
@@ -52,11 +51,15 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
|
||||
|
||||
MeshService service;
|
||||
|
||||
static MemoryDynamic<QueueStatus> staticQueueStatusPool;
|
||||
|
||||
Allocator<QueueStatus> &queueStatusPool = staticQueueStatusPool;
|
||||
|
||||
#include "Router.h"
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
||||
lastQueueStatus = { 0, 0, 16, 0 };
|
||||
}
|
||||
|
||||
void MeshService::init()
|
||||
@@ -83,6 +86,11 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
||||
void MeshService::loop()
|
||||
{
|
||||
if (lastQueueStatus.free == 0) { // check if there is now free space in TX queue
|
||||
QueueStatus qs = router->getQueueStatus();
|
||||
if (qs.free != lastQueueStatus.free)
|
||||
(void)sendQueueStatusToPhone(qs, 0, 0);
|
||||
}
|
||||
if (oldFromNum != fromNum) { // We don't want to generate extra notifies for multiple new packets
|
||||
fromNumChanged.notifyObservers(fromNum);
|
||||
oldFromNum = fromNum;
|
||||
@@ -179,12 +187,43 @@ bool MeshService::cancelSending(PacketId id)
|
||||
return router->cancelSending(nodeDB.getNodeNum(), id);
|
||||
}
|
||||
|
||||
ErrorCode MeshService::sendQueueStatusToPhone(const QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id)
|
||||
{
|
||||
QueueStatus *copied = queueStatusPool.allocCopy(qs);
|
||||
|
||||
copied->res = res;
|
||||
copied->mesh_packet_id = mesh_packet_id;
|
||||
|
||||
if (toPhoneQueueStatusQueue.numFree() == 0) {
|
||||
LOG_DEBUG("NOTE: tophone queue status queue is full, discarding oldest\n");
|
||||
QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0);
|
||||
if (d)
|
||||
releaseQueueStatusToPool(d);
|
||||
}
|
||||
|
||||
lastQueueStatus = *copied;
|
||||
|
||||
res = toPhoneQueueStatusQueue.enqueue(copied, 0);
|
||||
fromNum++;
|
||||
|
||||
return res ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
}
|
||||
|
||||
void MeshService::sendToMesh(MeshPacket *p, RxSource src, bool ccToPhone)
|
||||
{
|
||||
uint32_t mesh_packet_id = p->id;
|
||||
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
||||
|
||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||
router->sendLocal(p, src);
|
||||
ErrorCode res = router->sendLocal(p, src);
|
||||
|
||||
/* NOTE(pboldin): Prepare and send QueueStatus message to the phone as a
|
||||
* high-priority message. */
|
||||
QueueStatus qs = router->getQueueStatus();
|
||||
ErrorCode r = sendQueueStatusToPhone(qs, res, mesh_packet_id);
|
||||
if (r != ERRNO_OK) {
|
||||
LOG_DEBUG("Can't send status to phone");
|
||||
}
|
||||
|
||||
if (ccToPhone) {
|
||||
sendToPhone(p);
|
||||
@@ -220,7 +259,7 @@ void MeshService::sendToPhone(MeshPacket *p)
|
||||
|
||||
MeshPacket *copied = packetPool.allocCopy(*p);
|
||||
perhapsDecode(copied);
|
||||
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
|
||||
assert(toPhoneQueue.enqueue(copied, 0));
|
||||
fromNum++;
|
||||
}
|
||||
|
||||
@@ -241,8 +280,7 @@ NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
node->last_heard =
|
||||
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
||||
|
||||
// For the time in the position field, only set that if we have a real GPS clock
|
||||
position.time = getValidTime(RTCQualityGPS);
|
||||
position.time = getValidTime(RTCQualityFromNet);
|
||||
|
||||
updateBatteryLevel(powerStatus->getBatteryChargePercent());
|
||||
|
||||
@@ -288,4 +326,4 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
bool MeshService::isToPhoneQueueEmpty()
|
||||
{
|
||||
return toPhoneQueue.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "../platform/portduino/SimRadio.h"
|
||||
#endif
|
||||
|
||||
extern Allocator<QueueStatus> &queueStatusPool;
|
||||
|
||||
/**
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
*
|
||||
@@ -29,6 +31,12 @@ class MeshService
|
||||
/// FIXME - save this to flash on deep sleep
|
||||
PointerQueue<MeshPacket> toPhoneQueue;
|
||||
|
||||
// keep list of QueueStatus packets to be send to the phone
|
||||
PointerQueue<QueueStatus> toPhoneQueueStatusQueue;
|
||||
|
||||
// This holds the last QueueStatus send
|
||||
QueueStatus lastQueueStatus;
|
||||
|
||||
/// The current nonce for the newest packet which has been queued for the phone
|
||||
uint32_t fromNum = 0;
|
||||
|
||||
@@ -56,6 +64,12 @@ class MeshService
|
||||
/// Allows the bluetooth handler to free packets after they have been sent
|
||||
void releaseToPool(MeshPacket *p) { packetPool.release(p); }
|
||||
|
||||
/// Return the next QueueStatus packet destined to the phone.
|
||||
QueueStatus *getQueueStatusForPhone() { return toPhoneQueueStatusQueue.dequeuePtr(0); }
|
||||
|
||||
// Release QueueStatus packet to pool
|
||||
void releaseQueueStatusToPool(QueueStatus *p) { queueStatusPool.release(p); }
|
||||
|
||||
/**
|
||||
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep
|
||||
@@ -100,6 +114,8 @@ class MeshService
|
||||
/// needs to keep the packet around it makes a copy
|
||||
int handleFromRadio(const MeshPacket *p);
|
||||
friend class RoutingModule;
|
||||
|
||||
ErrorCode sendQueueStatusToPhone(const QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);
|
||||
};
|
||||
|
||||
extern MeshService service;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include "Channels.h"
|
||||
#include "CryptoEngine.h"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
|
||||
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
#error FromRadio is too big
|
||||
@@ -50,6 +49,7 @@ void PhoneAPI::close()
|
||||
|
||||
unobserve(&service.fromNumChanged);
|
||||
releasePhonePacket(); // Don't leak phone packets on shutdown
|
||||
releaseQueueStatusPhonePacket();
|
||||
|
||||
onConnectionChanged(false);
|
||||
}
|
||||
@@ -208,6 +208,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
fromRadioScratch.config.which_payload_variant = Config_bluetooth_tag;
|
||||
fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown config type %d\n", config_state);
|
||||
}
|
||||
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||
@@ -261,6 +263,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_remote_hardware_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.remote_hardware = moduleConfig.remote_hardware;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown module config type %d\n", config_state);
|
||||
}
|
||||
|
||||
config_state++;
|
||||
@@ -282,18 +286,23 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
case STATE_SEND_PACKETS:
|
||||
// Do we have a message from the mesh?
|
||||
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
||||
if (packetForPhone) {
|
||||
if (queueStatusPacketForPhone) {
|
||||
|
||||
fromRadioScratch.which_payload_variant = FromRadio_queueStatus_tag;
|
||||
fromRadioScratch.queueStatus = *queueStatusPacketForPhone;
|
||||
releaseQueueStatusPhonePacket();
|
||||
} else if (packetForPhone) {
|
||||
printPacket("phone downloaded packet", packetForPhone);
|
||||
|
||||
// Encapsulate as a FromRadio packet
|
||||
fromRadioScratch.which_payload_variant = FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = *packetForPhone;
|
||||
releasePhonePacket();
|
||||
}
|
||||
releasePhonePacket();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
LOG_ERROR("getFromRadio unexpected state %d\n", state);
|
||||
}
|
||||
|
||||
// Do we have a message from the mesh?
|
||||
@@ -322,6 +331,14 @@ void PhoneAPI::releasePhonePacket()
|
||||
}
|
||||
}
|
||||
|
||||
void PhoneAPI::releaseQueueStatusPhonePacket()
|
||||
{
|
||||
if (queueStatusPacketForPhone) {
|
||||
service.releaseQueueStatusToPool(queueStatusPacketForPhone);
|
||||
queueStatusPacketForPhone = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have data available to send to the phone
|
||||
*/
|
||||
@@ -342,14 +359,20 @@ bool PhoneAPI::available()
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
case STATE_SEND_PACKETS: {
|
||||
if (!queueStatusPacketForPhone)
|
||||
queueStatusPacketForPhone = service.getQueueStatusForPhone();
|
||||
bool hasPacket = !!queueStatusPacketForPhone;
|
||||
if (hasPacket)
|
||||
return true;
|
||||
|
||||
if (!packetForPhone)
|
||||
packetForPhone = service.getForPhone();
|
||||
bool hasPacket = !!packetForPhone;
|
||||
hasPacket = !!packetForPhone;
|
||||
// LOG_DEBUG("available hasPacket=%d\n", hasPacket);
|
||||
return hasPacket;
|
||||
}
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
LOG_ERROR("PhoneAPI::available unexpected state %d\n", state);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -42,6 +42,9 @@ class PhoneAPI : public Observer<uint32_t> // FIXME, we shouldn't be inheriting
|
||||
/// downloads it
|
||||
MeshPacket *packetForPhone = NULL;
|
||||
|
||||
// Keep QueueStatus packet just as packetForPhone
|
||||
QueueStatus *queueStatusPacketForPhone = NULL;
|
||||
|
||||
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
|
||||
const NodeInfo *nodeInfoForPhone = NULL;
|
||||
|
||||
@@ -83,9 +86,6 @@ class PhoneAPI : public Observer<uint32_t> // FIXME, we shouldn't be inheriting
|
||||
|
||||
void setInitialState() { state = STATE_SEND_MY_INFO; }
|
||||
|
||||
/// emit a debugging log character, FIXME - implement
|
||||
void debugOut(char c) { }
|
||||
|
||||
protected:
|
||||
/// Our fromradio packet while it is being assembled
|
||||
FromRadio fromRadioScratch = {};
|
||||
@@ -115,6 +115,8 @@ class PhoneAPI : public Observer<uint32_t> // FIXME, we shouldn't be inheriting
|
||||
private:
|
||||
void releasePhonePacket();
|
||||
|
||||
void releaseQueueStatusPhonePacket();
|
||||
|
||||
/// begin a new connection
|
||||
void handleStartConfig();
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Router.h"
|
||||
#include "assert.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
@@ -111,6 +110,8 @@ const RegionInfo regions[] = {
|
||||
|
||||
const RegionInfo *myRegion;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
|
||||
void initRegion()
|
||||
{
|
||||
const RegionInfo *r = regions;
|
||||
@@ -164,17 +165,19 @@ uint32_t RadioInterface::getPacketTime(uint32_t pl)
|
||||
|
||||
uint32_t RadioInterface::getPacketTime(MeshPacket *p)
|
||||
{
|
||||
assert(p->which_payload_variant == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
uint32_t pl = p->encrypted.size + sizeof(PacketHeader);
|
||||
|
||||
uint32_t pl = 0;
|
||||
if(p->which_payload_variant == MeshPacket_encrypted_tag) {
|
||||
pl = p->encrypted.size + sizeof(PacketHeader);
|
||||
} else {
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &Data_msg, &p->decoded);
|
||||
pl = numbytes + sizeof(PacketHeader);
|
||||
}
|
||||
return getPacketTime(pl);
|
||||
}
|
||||
|
||||
/** The delay to use for retransmitting dropped packets */
|
||||
uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p)
|
||||
{
|
||||
assert(slotTimeMsec); // Better be non zero
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &Data_msg, &p->decoded);
|
||||
uint32_t packetAirtime = getPacketTime(numbytes + sizeof(PacketHeader));
|
||||
// Make sure enough time has elapsed for this packet to be sent and an ACK is received.
|
||||
@@ -271,9 +274,6 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
RadioInterface::RadioInterface()
|
||||
{
|
||||
assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||
|
||||
// Can't print strings this early - serial not setup yet
|
||||
// LOG_DEBUG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||
}
|
||||
|
||||
bool RadioInterface::reconfigure()
|
||||
@@ -384,7 +384,7 @@ void RadioInterface::applyModemConfig()
|
||||
cr = 8;
|
||||
sf = 10;
|
||||
break;
|
||||
case Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
sf = 11;
|
||||
@@ -399,8 +399,6 @@ void RadioInterface::applyModemConfig()
|
||||
cr = 8;
|
||||
sf = 12;
|
||||
break;
|
||||
default:
|
||||
assert(0); // Unknown enum
|
||||
}
|
||||
} else {
|
||||
sf = loraConfig.spread_factor;
|
||||
@@ -422,7 +420,6 @@ void RadioInterface::applyModemConfig()
|
||||
}
|
||||
|
||||
power = loraConfig.tx_power;
|
||||
assert(myRegion); // Should have been found in init
|
||||
|
||||
if ((power == 0) || ((power > myRegion->powerLimit) && !devicestate.owner.is_licensed))
|
||||
power = myRegion->powerLimit;
|
||||
@@ -502,7 +499,10 @@ size_t RadioInterface::beginSending(MeshPacket *p)
|
||||
h->to = p->to;
|
||||
h->id = p->id;
|
||||
h->channel = p->channel;
|
||||
assert(p->hop_limit <= HOP_MAX);
|
||||
if (p->hop_limit > HOP_MAX) {
|
||||
LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_MAX);
|
||||
p->hop_limit = HOP_MAX;
|
||||
}
|
||||
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
|
||||
|
||||
// if the sender nodenum is zero, that means uninitialized
|
||||
|
||||
@@ -115,6 +115,13 @@ class RadioInterface
|
||||
*/
|
||||
virtual ErrorCode send(MeshPacket *p) = 0;
|
||||
|
||||
/** Return TX queue status */
|
||||
virtual QueueStatus getQueueStatus() {
|
||||
QueueStatus qs;
|
||||
qs.res = qs.mesh_packet_id = qs.free = qs.maxlen = 0;
|
||||
return qs;
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
virtual bool cancelSending(NodeNum from, PacketId id) { return false; }
|
||||
|
||||
|
||||
@@ -158,6 +158,17 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
#endif
|
||||
}
|
||||
|
||||
QueueStatus RadioLibInterface::getQueueStatus()
|
||||
{
|
||||
QueueStatus qs;
|
||||
|
||||
qs.res = qs.mesh_packet_id = 0;
|
||||
qs.free = txQueue.getFree();
|
||||
qs.maxlen = txQueue.getMaxLen();
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
bool RadioLibInterface::canSleep()
|
||||
{
|
||||
bool res = txQueue.empty();
|
||||
@@ -361,7 +372,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
// xmitMsec = getPacketTime(mp);
|
||||
airTime->logAirtime(RX_LOG, xmitMsec);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
|
||||
@@ -153,6 +153,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
virtual void startSend(MeshPacket *txp);
|
||||
|
||||
QueueStatus getQueueStatus();
|
||||
|
||||
protected:
|
||||
|
||||
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
|
||||
|
||||
@@ -39,6 +39,8 @@ static MemoryDynamic<MeshPacket> staticPool;
|
||||
|
||||
Allocator<MeshPacket> &packetPool = staticPool;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -94,8 +96,7 @@ PacketId generatePacketId()
|
||||
static uint32_t i; // Note: trying to keep this in noinit didn't help for working across reboots
|
||||
static bool didInit = false;
|
||||
|
||||
assert(sizeof(PacketId) == 4 || sizeof(PacketId) == 1); // only supported values
|
||||
uint32_t numPacketId = sizeof(PacketId) == 1 ? UINT8_MAX : UINT32_MAX; // 0 is consider invalid
|
||||
uint32_t numPacketId = UINT32_MAX;
|
||||
|
||||
if (!didInit) {
|
||||
didInit = true;
|
||||
@@ -137,7 +138,7 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelI
|
||||
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
||||
{
|
||||
LOG_ERROR("Error=%d, returning NAK and dropping packet.\n", err);
|
||||
sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel);
|
||||
sendAckNak(err, getFrom(p), p->id, p->channel);
|
||||
packetPool.release(p);
|
||||
}
|
||||
|
||||
@@ -148,6 +149,11 @@ void Router::setReceivedMessage()
|
||||
runASAP = true;
|
||||
}
|
||||
|
||||
QueueStatus Router::getQueueStatus()
|
||||
{
|
||||
return iface->getQueueStatus();
|
||||
}
|
||||
|
||||
ErrorCode Router::sendLocal(MeshPacket *p, RxSource src)
|
||||
{
|
||||
// No need to deliver externally if the destination is the local node
|
||||
@@ -186,18 +192,26 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes)
|
||||
*/
|
||||
ErrorCode Router::send(MeshPacket *p)
|
||||
{
|
||||
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
|
||||
if (p->to == nodeDB.getNodeNum()) {
|
||||
LOG_ERROR("BUG! send() called with packet destined for local node!\n");
|
||||
packetPool.release(p);
|
||||
return Routing_Error_BAD_REQUEST;
|
||||
} // should have already been handled by sendLocal
|
||||
|
||||
// Abort sending if we are violating the duty cycle
|
||||
if (!config.lora.override_duty_cycle && myRegion->dutyCycle != 100) {
|
||||
float hourlyTxPercent = airTime->utilizationTXPercent();
|
||||
if (hourlyTxPercent > myRegion->dutyCycle) {
|
||||
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
|
||||
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
|
||||
Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT;
|
||||
abortSendAndNak(err, p);
|
||||
return err;
|
||||
}
|
||||
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
|
||||
float hourlyTxPercent = airTime->utilizationTXPercent();
|
||||
if (hourlyTxPercent > myRegion->dutyCycle) {
|
||||
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
|
||||
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
|
||||
Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT;
|
||||
if (getFrom(p) == nodeDB.getNodeNum()) { // only send NAK to API, not to the mesh
|
||||
abortSendAndNak(err, p);
|
||||
} else {
|
||||
packetPool.release(p);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
@@ -281,7 +295,6 @@ bool Router::cancelSending(NodeNum from, PacketId id)
|
||||
*/
|
||||
void Router::sniffReceived(const MeshPacket *p, const Routing *c)
|
||||
{
|
||||
LOG_DEBUG("FIXME-update-db Sniffing packet\n");
|
||||
// FIXME, update nodedb here for any packet that passes through us
|
||||
}
|
||||
|
||||
@@ -300,7 +313,6 @@ bool perhapsDecode(MeshPacket *p)
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
size_t rawSize = p->encrypted.size;
|
||||
assert(rawSize <= sizeof(bytes));
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
@@ -320,14 +332,6 @@ bool perhapsDecode(MeshPacket *p)
|
||||
p->which_payload_variant = MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
/*
|
||||
if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
|
||||
LOG_DEBUG("\n\n** TEXT_MESSAGE_APP\n");
|
||||
} else if (p->decoded.portnum == PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
LOG_DEBUG("\n\n** PortNum_TEXT_MESSAGE_COMPRESSED_APP\n");
|
||||
}
|
||||
*/
|
||||
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
// Decompress the payload
|
||||
@@ -363,7 +367,6 @@ Routing_Error perhapsEncode(MeshPacket *p)
|
||||
{
|
||||
// If the packet is not yet encrypted, do so now
|
||||
if (p->which_payload_variant == MeshPacket_decoded_tag) {
|
||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &Data_msg, &p->decoded);
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ class Router : protected concurrency::OSThread
|
||||
*/
|
||||
MeshPacket *allocForSending();
|
||||
|
||||
/** Return Underlying interface's TX queue status */
|
||||
QueueStatus getQueueStatus();
|
||||
|
||||
/**
|
||||
* @return our local nodenum */
|
||||
NodeNum getNodeNum();
|
||||
|
||||
@@ -70,7 +70,7 @@ bool SX126xInterface<T>::init()
|
||||
#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
|
||||
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
|
||||
if (res == RADIOLIB_ERR_NONE)
|
||||
res = lora.setDio2AsRfSwitch(false);
|
||||
res = lora.setDio2AsRfSwitch(true);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -25,7 +25,12 @@ bool SX128xInterface<T>::init()
|
||||
digitalWrite(SX128X_POWER_EN, HIGH);
|
||||
pinMode(SX128X_POWER_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
pinMode(RF95_FAN_EN, OUTPUT);
|
||||
digitalWrite(RF95_FAN_EN, 1);
|
||||
#endif
|
||||
|
||||
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
|
||||
digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output
|
||||
pinMode(SX128X_RXEN, OUTPUT);
|
||||
|
||||
@@ -18,9 +18,6 @@ class SinglePortModule : public MeshModule
|
||||
SinglePortModule(const char *_name, PortNum _ourPortNum) : MeshModule(_name), ourPortNum(_ourPortNum) {}
|
||||
|
||||
protected:
|
||||
uint32_t max_channel_util_percent = 40;
|
||||
uint32_t polite_channel_util_percent = 25;
|
||||
|
||||
/**
|
||||
* @return true if you want to receive the specified portnum
|
||||
*/
|
||||
|
||||
67
src/mesh/api/ServerAPI.cpp
Normal file
67
src/mesh/api/ServerAPI.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "ServerAPI.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
template<typename T>
|
||||
ServerAPI<T>::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client)
|
||||
{
|
||||
LOG_INFO("Incoming wifi connection\n");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ServerAPI<T>::~ServerAPI()
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ServerAPI<T>::close()
|
||||
{
|
||||
client.stop(); // drop tcp connection
|
||||
StreamAPI::close();
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
template<typename T>
|
||||
bool ServerAPI<T>::checkIsConnected()
|
||||
{
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int32_t ServerAPI<T>::runOnce()
|
||||
{
|
||||
if (client.connected()) {
|
||||
return StreamAPI::runOncePart();
|
||||
} else {
|
||||
LOG_INFO("Client dropped connection, suspending API service\n");
|
||||
enabled = false; // we no longer need to run
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
APIServerPort<T, U>::APIServerPort(int port) : U(port), concurrency::OSThread("ApiServer") {}
|
||||
|
||||
template<class T, class U>
|
||||
void APIServerPort<T, U>::init()
|
||||
{
|
||||
U::begin();
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
int32_t APIServerPort<T, U>::runOnce()
|
||||
{
|
||||
auto client = U::available();
|
||||
if (client) {
|
||||
// Close any previous connection (see FIXME in header file)
|
||||
if (openAPI) {
|
||||
LOG_INFO("Force closing previous TCP connection\n");
|
||||
delete openAPI;
|
||||
}
|
||||
|
||||
openAPI = new T(client);
|
||||
}
|
||||
|
||||
return 100; // only check occasionally for incoming connections
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class WiFiServerAPI : public StreamAPI, private concurrency::OSThread
|
||||
template<class T>
|
||||
class ServerAPI : public StreamAPI, private concurrency::OSThread
|
||||
{
|
||||
private:
|
||||
WiFiClient client;
|
||||
T client;
|
||||
|
||||
public:
|
||||
explicit WiFiServerAPI(WiFiClient &_client);
|
||||
explicit ServerAPI(T &_client);
|
||||
|
||||
virtual ~WiFiServerAPI();
|
||||
virtual ~ServerAPI();
|
||||
|
||||
/// override close to also shutdown the TCP link
|
||||
virtual void close();
|
||||
@@ -34,25 +34,21 @@ class WiFiServerAPI : public StreamAPI, private concurrency::OSThread
|
||||
/**
|
||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||
*/
|
||||
class WiFiServerPort : public WiFiServer, private concurrency::OSThread
|
||||
template<class T, class U>
|
||||
class APIServerPort : public U, private concurrency::OSThread
|
||||
{
|
||||
/** The currently open port
|
||||
*
|
||||
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
|
||||
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
|
||||
*/
|
||||
WiFiServerAPI *openAPI = NULL;
|
||||
T *openAPI = NULL;
|
||||
|
||||
public:
|
||||
explicit WiFiServerPort(int port);
|
||||
explicit APIServerPort(int port);
|
||||
|
||||
void init();
|
||||
|
||||
/// If an api server is running, we try to spit out debug 'serial' characters there
|
||||
static void debugOut(char c);
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override;
|
||||
};
|
||||
|
||||
void initApiServer(int port=4403);
|
||||
25
src/mesh/api/WiFiServerAPI.cpp
Normal file
25
src/mesh/api/WiFiServerAPI.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#if HAS_WIFI
|
||||
#include "WiFiServerAPI.h"
|
||||
|
||||
static WiFiServerPort *apiPort;
|
||||
|
||||
void initApiServer(int port)
|
||||
{
|
||||
// Start API server on port 4403
|
||||
if (!apiPort) {
|
||||
apiPort = new WiFiServerPort(port);
|
||||
LOG_INFO("API server listening on TCP port %d\n", port);
|
||||
apiPort->init();
|
||||
}
|
||||
}
|
||||
|
||||
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : ServerAPI(_client)
|
||||
{
|
||||
LOG_INFO("Incoming wifi connection\n");
|
||||
}
|
||||
|
||||
WiFiServerPort::WiFiServerPort(int port) : APIServerPort(port) {}
|
||||
#endif
|
||||
25
src/mesh/api/WiFiServerAPI.h
Normal file
25
src/mesh/api/WiFiServerAPI.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "ServerAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class WiFiServerAPI : public ServerAPI<WiFiClient>
|
||||
{
|
||||
public:
|
||||
explicit WiFiServerAPI(WiFiClient &_client);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||
*/
|
||||
class WiFiServerPort : public APIServerPort<WiFiServerAPI, WiFiServer>
|
||||
{
|
||||
public:
|
||||
explicit WiFiServerPort(int port);
|
||||
};
|
||||
|
||||
void initApiServer(int port=4403);
|
||||
27
src/mesh/api/ethServerAPI.cpp
Normal file
27
src/mesh/api/ethServerAPI.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#if HAS_ETHERNET
|
||||
|
||||
#include "ethServerAPI.h"
|
||||
|
||||
static ethServerPort *apiPort;
|
||||
|
||||
void initApiServer(int port)
|
||||
{
|
||||
// Start API server on port 4403
|
||||
if (!apiPort) {
|
||||
apiPort = new ethServerPort(port);
|
||||
LOG_INFO("API server listening on TCP port %d\n", port);
|
||||
apiPort->init();
|
||||
}
|
||||
}
|
||||
|
||||
ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client)
|
||||
{
|
||||
LOG_INFO("Incoming ethernet connection\n");
|
||||
}
|
||||
|
||||
ethServerPort::ethServerPort(int port) : APIServerPort(port) {}
|
||||
|
||||
#endif
|
||||
25
src/mesh/api/ethServerAPI.h
Normal file
25
src/mesh/api/ethServerAPI.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "ServerAPI.h"
|
||||
#include <RAK13800_W5100S.h>
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class ethServerAPI : public ServerAPI<EthernetClient>
|
||||
{
|
||||
public:
|
||||
explicit ethServerAPI(EthernetClient &_client);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||
*/
|
||||
class ethServerPort : public APIServerPort<ethServerAPI, EthernetServer>
|
||||
{
|
||||
public:
|
||||
explicit ethServerPort(int port);
|
||||
};
|
||||
|
||||
void initApiServer(int port=4403);
|
||||
@@ -33,14 +33,14 @@
|
||||
|
||||
#include "unishox2.h"
|
||||
|
||||
/// byte is unsigned char
|
||||
typedef unsigned char byte;
|
||||
/// uint8_t is unsigned char
|
||||
typedef unsigned char uint8_t;
|
||||
|
||||
/// possible horizontal sets and states
|
||||
enum {USX_ALPHA = 0, USX_SYM, USX_NUM, USX_DICT, USX_DELTA, USX_NUM_TEMP};
|
||||
|
||||
/// This 2D array has the characters for the sets USX_ALPHA, USX_SYM and USX_NUM. Where a character cannot fit into a byte, 0 is used and handled in code.
|
||||
byte usx_sets[][28] = {{ 0, ' ', 'e', 't', 'a', 'o', 'i', 'n',
|
||||
/// This 2D array has the characters for the sets USX_ALPHA, USX_SYM and USX_NUM. Where a character cannot fit into a uint8_t, 0 is used and handled in code.
|
||||
uint8_t usx_sets[][28] = {{ 0, ' ', 'e', 't', 'a', 'o', 'i', 'n',
|
||||
's', 'r', 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b',
|
||||
'g', 'w', 'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z'},
|
||||
{'"', '{', '}', '_', '<', '>', ':', '\n',
|
||||
@@ -53,22 +53,22 @@ byte usx_sets[][28] = {{ 0, ' ', 'e', 't', 'a', 'o', 'i', 'n',
|
||||
/// Stores position of letter in usx_sets.
|
||||
/// First 3 bits - position in usx_hcodes
|
||||
/// Next 5 bits - position in usx_vcodes
|
||||
byte usx_code_94[94];
|
||||
uint8_t usx_code_94[94];
|
||||
|
||||
/// Vertical codes starting from the MSB
|
||||
byte usx_vcodes[] = { 0x00, 0x40, 0x60, 0x80, 0x90, 0xA0, 0xB0,
|
||||
uint8_t usx_vcodes[] = { 0x00, 0x40, 0x60, 0x80, 0x90, 0xA0, 0xB0,
|
||||
0xC0, 0xD0, 0xD8, 0xE0, 0xE4, 0xE8, 0xEC,
|
||||
0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF7, 0xF8,
|
||||
0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
|
||||
|
||||
/// Length of each veritical code
|
||||
byte usx_vcode_lens[] = { 2, 3, 3, 4, 4, 4, 4,
|
||||
uint8_t usx_vcode_lens[] = { 2, 3, 3, 4, 4, 4, 4,
|
||||
4, 5, 5, 6, 6, 6, 7,
|
||||
7, 7, 7, 7, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8 };
|
||||
|
||||
/// Vertical Codes and Set number for frequent sequences in sets USX_SYM and USX_NUM. First 3 bits indicate set (USX_SYM/USX_NUM) and rest are vcode positions
|
||||
byte usx_freq_codes[] = {(1 << 5) + 25, (1 << 5) + 26, (1 << 5) + 27, (2 << 5) + 23, (2 << 5) + 24, (2 << 5) + 25};
|
||||
uint8_t usx_freq_codes[] = {(1 << 5) + 25, (1 << 5) + 26, (1 << 5) + 27, (2 << 5) + 23, (2 << 5) + 24, (2 << 5) + 25};
|
||||
|
||||
/// Not used
|
||||
const int UTF8_MASK[] = {0xE0, 0xF0, 0xF8};
|
||||
@@ -117,7 +117,7 @@ const int UTF8_PREFIX[] = {0xC0, 0xE0, 0xF0};
|
||||
#define USX_OFFSET_94 33
|
||||
|
||||
/// global to indicate whether initialization is complete or not
|
||||
byte is_inited = 0;
|
||||
uint8_t is_inited = 0;
|
||||
|
||||
/// Fills the usx_code_94 94 letter array based on sets of characters at usx_sets \n
|
||||
/// For each element in usx_code_94, first 3 msb bits is set (USX_ALPHA / USX_SYM / USX_NUM) \n
|
||||
@@ -128,7 +128,7 @@ void init_coder() {
|
||||
memset(usx_code_94, '\0', sizeof(usx_code_94));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 28; j++) {
|
||||
byte c = usx_sets[i][j];
|
||||
uint8_t c = usx_sets[i][j];
|
||||
if (c > 32) {
|
||||
usx_code_94[c - USX_OFFSET_94] = (i << 5) + j;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
@@ -145,7 +145,7 @@ unsigned int usx_mask[] = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
||||
/// Appends specified number of bits to the output (out) \n
|
||||
/// If maximum limit (olen) is reached, -1 is returned \n
|
||||
/// Otherwise clen bits in code are appended to out starting with MSB
|
||||
int append_bits(char *out, int olen, int ol, byte code, int clen) {
|
||||
int append_bits(char *out, int olen, int ol, uint8_t code, int clen) {
|
||||
|
||||
|
||||
//printf("%d,%x,%d,%d\n", ol, code, clen, state);
|
||||
@@ -154,8 +154,8 @@ int append_bits(char *out, int olen, int ol, byte code, int clen) {
|
||||
int oidx;
|
||||
unsigned char a_byte;
|
||||
|
||||
byte cur_bit = ol % 8;
|
||||
byte blen = clen;
|
||||
uint8_t cur_bit = ol % 8;
|
||||
uint8_t blen = clen;
|
||||
a_byte = code & usx_mask[blen - 1];
|
||||
a_byte >>= cur_bit;
|
||||
if (blen + cur_bit > 8)
|
||||
@@ -181,7 +181,7 @@ int append_bits(char *out, int olen, int ol, byte code, int clen) {
|
||||
} while (0)
|
||||
|
||||
/// Appends switch code to out depending on the state (USX_DELTA or other)
|
||||
int append_switch_code(char *out, int olen, int ol, byte state) {
|
||||
int append_switch_code(char *out, int olen, int ol, uint8_t state) {
|
||||
if (state == USX_DELTA) {
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, UNI_STATE_SPL_CODE, UNI_STATE_SPL_CODE_LEN));
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, UNI_STATE_SW_CODE, UNI_STATE_SW_CODE_LEN));
|
||||
@@ -191,9 +191,9 @@ int append_switch_code(char *out, int olen, int ol, byte state) {
|
||||
}
|
||||
|
||||
/// Appends given horizontal and veritical code bits to out
|
||||
int append_code(char *out, int olen, int ol, byte code, byte *state, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
byte hcode = code >> 5;
|
||||
byte vcode = code & 0x1F;
|
||||
int append_code(char *out, int olen, int ol, uint8_t code, uint8_t *state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
uint8_t hcode = code >> 5;
|
||||
uint8_t vcode = code & 0x1F;
|
||||
if (!usx_hcode_lens[hcode] && hcode != USX_ALPHA)
|
||||
return ol;
|
||||
switch (hcode) {
|
||||
@@ -221,11 +221,11 @@ int append_code(char *out, int olen, int ol, byte code, byte *state, const byte
|
||||
}
|
||||
|
||||
/// Length of bits used to represent count for each level
|
||||
const byte count_bit_lens[5] = {2, 4, 7, 11, 16};
|
||||
const uint8_t count_bit_lens[5] = {2, 4, 7, 11, 16};
|
||||
/// Cumulative counts represented at each level
|
||||
const int32_t count_adder[5] = {4, 20, 148, 2196, 67732};
|
||||
/// Codes used to specify the level that the count belongs to
|
||||
const byte count_codes[] = {0x01, 0x82, 0xC3, 0xE4, 0xF4};
|
||||
const uint8_t count_codes[] = {0x01, 0x82, 0xC3, 0xE4, 0xF4};
|
||||
/// Encodes given count to out
|
||||
int encodeCount(char *out, int olen, int ol, int count) {
|
||||
// First five bits are code and Last three bits of codes represent length
|
||||
@@ -245,15 +245,15 @@ int encodeCount(char *out, int olen, int ol, int count) {
|
||||
}
|
||||
|
||||
/// Length of bits used to represent delta code for each level
|
||||
const byte uni_bit_len[5] = {6, 12, 14, 16, 21};
|
||||
const uint8_t uni_bit_len[5] = {6, 12, 14, 16, 21};
|
||||
/// Cumulative delta codes represented at each level
|
||||
const int32_t uni_adder[5] = {0, 64, 4160, 20544, 86080};
|
||||
|
||||
/// Encodes the unicode code point given by code to out. prev_code is used to calculate the delta
|
||||
int encodeUnicode(char *out, int olen, int ol, int32_t code, int32_t prev_code) {
|
||||
// First five bits are code and Last three bits of codes represent length
|
||||
//const byte codes[8] = {0x00, 0x42, 0x83, 0xA3, 0xC3, 0xE4, 0xF5, 0xFD};
|
||||
const byte codes[6] = {0x01, 0x82, 0xC3, 0xE4, 0xF5, 0xFD};
|
||||
//const uint8_t codes[8] = {0x00, 0x42, 0x83, 0xA3, 0xC3, 0xE4, 0xF5, 0xFD};
|
||||
const uint8_t codes[6] = {0x01, 0x82, 0xC3, 0xE4, 0xF5, 0xFD};
|
||||
int32_t till = 0;
|
||||
int32_t diff = code - prev_code;
|
||||
if (diff < 0)
|
||||
@@ -331,7 +331,7 @@ int32_t readUTF8(const char *in, int len, int l, int *utf8len) {
|
||||
/// This is also used for Unicode strings \n
|
||||
/// This is a crude implementation that is not optimized. Assuming only short strings \n
|
||||
/// are encoded, this is not much of an issue.
|
||||
int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, byte *state, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, uint8_t *state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
int j, k;
|
||||
int longest_dist = 0;
|
||||
int longest_len = 0;
|
||||
@@ -372,7 +372,7 @@ int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol,
|
||||
/// This is also used for Unicode strings \n
|
||||
/// This is a crude implementation that is not optimized. Assuming only short strings \n
|
||||
/// are encoded, this is not much of an issue.
|
||||
int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, byte *state, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, uint8_t *state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
int last_ol = *ol;
|
||||
int last_len = 0;
|
||||
int last_dist = 0;
|
||||
@@ -431,7 +431,7 @@ int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, stru
|
||||
|
||||
/// Returns 4 bit code assuming ch falls between '0' to '9', \n
|
||||
/// 'A' to 'F' or 'a' to 'f'
|
||||
byte getBaseCode(char ch) {
|
||||
uint8_t getBaseCode(char ch) {
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return (ch - '0') << 4;
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
@@ -458,7 +458,7 @@ char getNibbleType(char ch) {
|
||||
}
|
||||
|
||||
/// Starts coding of nibble sets
|
||||
int append_nibble_escape(char *out, int olen, int ol, byte state, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
int append_nibble_escape(char *out, int olen, int ol, uint8_t state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
SAFE_APPEND_BITS(ol = append_switch_code(out, olen, ol, state));
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, usx_hcodes[USX_NUM], usx_hcode_lens[USX_NUM]));
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, 0, 2));
|
||||
@@ -471,7 +471,7 @@ long min_of(long c, long i) {
|
||||
}
|
||||
|
||||
/// Appends the terminator code depending on the state, preset and whether full terminator needs to be encoded to out or not \n
|
||||
int append_final_bits(char *const out, const int olen, int ol, const byte state, const byte is_all_upper, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
int append_final_bits(char *const out, const int olen, int ol, const uint8_t state, const uint8_t is_all_upper, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
if (usx_hcode_lens[USX_ALPHA]) {
|
||||
if (USX_NUM != state) {
|
||||
// for num state, append TERM_CODE directly
|
||||
@@ -486,7 +486,7 @@ int append_final_bits(char *const out, const int olen, int ol, const byte state,
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, TERM_BYTE_PRESET_1, is_all_upper ? TERM_BYTE_PRESET_1_LEN_UPPER : TERM_BYTE_PRESET_1_LEN_LOWER));
|
||||
}
|
||||
|
||||
// fill byte with the last bit
|
||||
// fill uint8_t with the last bit
|
||||
SAFE_APPEND_BITS(ol = append_bits(out, olen, ol, (ol == 0 || out[(ol-1)/8] << ((ol-1)&7) >= 0) ? 0 : 0xFF, (8 - ol % 8) & 7));
|
||||
|
||||
return ol;
|
||||
@@ -500,21 +500,21 @@ int append_final_bits(char *const out, const int olen, int ol, const byte state,
|
||||
} while (0)
|
||||
|
||||
// Main API function. See unishox2.h for documentation
|
||||
int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const byte usx_hcodes[], const byte usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines) {
|
||||
int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines) {
|
||||
|
||||
byte state;
|
||||
uint8_t state;
|
||||
|
||||
int l, ll, ol;
|
||||
char c_in, c_next;
|
||||
int prev_uni;
|
||||
byte is_upper, is_all_upper;
|
||||
uint8_t is_upper, is_all_upper;
|
||||
#if (UNISHOX_API_OUT_AND_LEN(0,1)) == 0
|
||||
const int olen = INT_MAX - 1;
|
||||
const int rawolen = olen;
|
||||
const byte need_full_term_codes = 0;
|
||||
const uint8_t need_full_term_codes = 0;
|
||||
#else
|
||||
const int rawolen = olen;
|
||||
byte need_full_term_codes = 0;
|
||||
uint8_t need_full_term_codes = 0;
|
||||
if (olen < 0) {
|
||||
need_full_term_codes = 1;
|
||||
olen *= -1;
|
||||
@@ -735,9 +735,9 @@ int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(cha
|
||||
}
|
||||
}
|
||||
if (state == USX_DELTA && (c_in == ' ' || c_in == '.' || c_in == ',')) {
|
||||
byte spl_code = (c_in == ',' ? 0xC0 : (c_in == '.' ? 0xE0 : (c_in == ' ' ? 0 : 0xFF)));
|
||||
uint8_t spl_code = (c_in == ',' ? 0xC0 : (c_in == '.' ? 0xE0 : (c_in == ' ' ? 0 : 0xFF)));
|
||||
if (spl_code != 0xFF) {
|
||||
byte spl_code_len = (c_in == ',' ? 3 : (c_in == '.' ? 4 : (c_in == ' ' ? 1 : 4)));
|
||||
uint8_t spl_code_len = (c_in == ',' ? 3 : (c_in == '.' ? 4 : (c_in == ' ' ? 1 : 4)));
|
||||
SAFE_APPEND_BITS2(rawolen, ol = append_bits(out, olen, ol, UNI_STATE_SPL_CODE, UNI_STATE_SPL_CODE_LEN));
|
||||
SAFE_APPEND_BITS2(rawolen, ol = append_bits(out, olen, ol, spl_code, spl_code_len));
|
||||
continue;
|
||||
@@ -833,7 +833,7 @@ int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(cha
|
||||
}
|
||||
|
||||
// Main API function. See unishox2.h for documentation
|
||||
int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const byte usx_hcodes[], const byte usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[]) {
|
||||
int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[]) {
|
||||
return unishox2_compress_lines(in, len, UNISHOX_API_OUT_AND_LEN(out, olen), usx_hcodes, usx_hcode_lens, usx_freq_seq, usx_templates, NULL);
|
||||
}
|
||||
|
||||
@@ -852,10 +852,10 @@ int read8bitCode(const char *in, int len, int bit_no) {
|
||||
int bit_pos = bit_no & 0x07;
|
||||
int char_pos = bit_no >> 3;
|
||||
len >>= 3;
|
||||
byte code = (((byte)in[char_pos]) << bit_pos);
|
||||
uint8_t code = (((uint8_t)in[char_pos]) << bit_pos);
|
||||
char_pos++;
|
||||
if (char_pos < len) {
|
||||
code |= ((byte)in[char_pos]) >> (8 - bit_pos);
|
||||
code |= ((uint8_t)in[char_pos]) >> (8 - bit_pos);
|
||||
} else
|
||||
code |= (0xFF >> (8 - bit_pos));
|
||||
return code;
|
||||
@@ -864,17 +864,17 @@ int read8bitCode(const char *in, int len, int bit_no) {
|
||||
/// The list of veritical codes is split into 5 sections. Used by readVCodeIdx()
|
||||
#define SECTION_COUNT 5
|
||||
/// Used by readVCodeIdx() for finding the section under which the code read using read8bitCode() falls
|
||||
byte usx_vsections[] = {0x7F, 0xBF, 0xDF, 0xEF, 0xFF};
|
||||
uint8_t usx_vsections[] = {0x7F, 0xBF, 0xDF, 0xEF, 0xFF};
|
||||
/// Used by readVCodeIdx() for finding the section vertical position offset
|
||||
byte usx_vsection_pos[] = {0, 4, 8, 12, 20};
|
||||
uint8_t usx_vsection_pos[] = {0, 4, 8, 12, 20};
|
||||
/// Used by readVCodeIdx() for masking the code read by read8bitCode()
|
||||
byte usx_vsection_mask[] = {0x7F, 0x3F, 0x1F, 0x0F, 0x0F};
|
||||
uint8_t usx_vsection_mask[] = {0x7F, 0x3F, 0x1F, 0x0F, 0x0F};
|
||||
/// Used by readVCodeIdx() for shifting the code read by read8bitCode() to obtain the vpos
|
||||
byte usx_vsection_shift[] = {5, 4, 3, 1, 0};
|
||||
uint8_t usx_vsection_shift[] = {5, 4, 3, 1, 0};
|
||||
|
||||
/// Vertical decoder lookup table - 3 bits code len, 5 bytes vertical pos
|
||||
/// code len is one less as 8 cannot be accommodated in 3 bits
|
||||
byte usx_vcode_lookup[36] = {
|
||||
uint8_t usx_vcode_lookup[36] = {
|
||||
(1 << 5) + 0, (1 << 5) + 0, (2 << 5) + 1, (2 << 5) + 2, // Section 1
|
||||
(3 << 5) + 3, (3 << 5) + 4, (3 << 5) + 5, (3 << 5) + 6, // Section 2
|
||||
(3 << 5) + 7, (3 << 5) + 7, (4 << 5) + 8, (4 << 5) + 9, // Section 3
|
||||
@@ -887,19 +887,19 @@ byte usx_vcode_lookup[36] = {
|
||||
};
|
||||
|
||||
/// Decodes the vertical code from the given bitstream at in \n
|
||||
/// This is designed to use less memory using a 36 byte buffer \n
|
||||
/// compared to using a 256 byte buffer to decode the next 8 bits read by read8bitCode() \n
|
||||
/// This is designed to use less memory using a 36 uint8_t buffer \n
|
||||
/// compared to using a 256 uint8_t buffer to decode the next 8 bits read by read8bitCode() \n
|
||||
/// by splitting the list of vertical codes. \n
|
||||
/// Decoder is designed for using less memory, not speed. \n
|
||||
/// Returns the veritical code index or 99 if match could not be found. \n
|
||||
/// Also updates bit_no_p with how many ever bits used by the vertical code.
|
||||
int readVCodeIdx(const char *in, int len, int *bit_no_p) {
|
||||
if (*bit_no_p < len) {
|
||||
byte code = read8bitCode(in, len, *bit_no_p);
|
||||
uint8_t code = read8bitCode(in, len, *bit_no_p);
|
||||
int i = 0;
|
||||
do {
|
||||
if (code <= usx_vsections[i]) {
|
||||
byte vcode = usx_vcode_lookup[usx_vsection_pos[i] + ((code & usx_vsection_mask[i]) >> usx_vsection_shift[i])];
|
||||
uint8_t vcode = usx_vcode_lookup[usx_vsection_pos[i] + ((code & usx_vsection_mask[i]) >> usx_vsection_shift[i])];
|
||||
(*bit_no_p) += ((vcode >> 5) + 1);
|
||||
if (*bit_no_p > len)
|
||||
return 99;
|
||||
@@ -912,16 +912,16 @@ int readVCodeIdx(const char *in, int len, int *bit_no_p) {
|
||||
|
||||
/// Mask for retrieving each code to be decoded according to its length \n
|
||||
/// Same as usx_mask so redundant
|
||||
byte len_masks[] = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
||||
uint8_t len_masks[] = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
||||
/// Decodes the horizontal code from the given bitstream at in \n
|
||||
/// depending on the hcodes defined using usx_hcodes and usx_hcode_lens \n
|
||||
/// Returns the horizontal code index or 99 if match could not be found. \n
|
||||
/// Also updates bit_no_p with how many ever bits used by the horizontal code.
|
||||
int readHCodeIdx(const char *in, int len, int *bit_no_p, const byte usx_hcodes[], const byte usx_hcode_lens[]) {
|
||||
int readHCodeIdx(const char *in, int len, int *bit_no_p, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) {
|
||||
if (!usx_hcode_lens[USX_ALPHA])
|
||||
return USX_ALPHA;
|
||||
if (*bit_no_p < len) {
|
||||
byte code = read8bitCode(in, len, *bit_no_p);
|
||||
uint8_t code = read8bitCode(in, len, *bit_no_p);
|
||||
for (int code_pos = 0; code_pos < 5; code_pos++) {
|
||||
if (usx_hcode_lens[code_pos] && (code & len_masks[usx_hcode_lens[code_pos] - 1]) == usx_hcodes[code_pos]) {
|
||||
*bit_no_p += usx_hcode_lens[code_pos];
|
||||
@@ -1083,12 +1083,12 @@ char getHexChar(int32_t nibble, int hex_type) {
|
||||
}
|
||||
|
||||
// Main API function. See unishox2.h for documentation
|
||||
int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const byte usx_hcodes[], const byte usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines) {
|
||||
int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines) {
|
||||
|
||||
int dstate;
|
||||
int bit_no;
|
||||
int h, v;
|
||||
byte is_all_upper;
|
||||
uint8_t is_all_upper;
|
||||
#if (UNISHOX_API_OUT_AND_LEN(0,1)) == 0
|
||||
const int olen = INT_MAX - 1;
|
||||
#endif
|
||||
@@ -1155,7 +1155,7 @@ int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(c
|
||||
} else
|
||||
h = dstate;
|
||||
char c = 0;
|
||||
byte is_upper = is_all_upper;
|
||||
uint8_t is_upper = is_all_upper;
|
||||
v = readVCodeIdx(in, len, &bit_no);
|
||||
if (v == 99 || h == 99) {
|
||||
bit_no = orig_bit_no;
|
||||
@@ -1357,7 +1357,7 @@ int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(c
|
||||
}
|
||||
|
||||
// Main API function. See unishox2.h for documentation
|
||||
int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const byte usx_hcodes[], const byte usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[]) {
|
||||
int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[]) {
|
||||
return unishox2_decompress_lines(in, len, UNISHOX_API_OUT_AND_LEN(out, olen), usx_hcodes, usx_hcode_lens, usx_freq_seq, usx_templates, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <SPI.h>
|
||||
#include <RAK13800_W5100S.h>
|
||||
#include "target_specific.h"
|
||||
#include "mesh/eth/ethServerAPI.h"
|
||||
#include "mesh/api/ethServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
|
||||
#ifndef DISABLE_NTP
|
||||
@@ -94,6 +94,7 @@ bool initEthernet()
|
||||
// createSSLCert();
|
||||
|
||||
getMacAddr(mac); // FIXME use the BLE MAC for now...
|
||||
mac[0] &= 0xfe; // Make sure this is not a multicast MAC
|
||||
|
||||
if (config.network.address_mode == Config_NetworkConfig_AddressMode_DHCP) {
|
||||
LOG_INFO("starting Ethernet DHCP\n");
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#include "ethServerAPI.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
static ethServerPort *apiPort;
|
||||
|
||||
void initApiServer(int port)
|
||||
{
|
||||
// Start API server on port 4403
|
||||
if (!apiPort) {
|
||||
apiPort = new ethServerPort(port);
|
||||
LOG_INFO("API server listening on TCP port %d\n", port);
|
||||
apiPort->init();
|
||||
}
|
||||
}
|
||||
|
||||
ethServerAPI::ethServerAPI(EthernetClient &_client) : StreamAPI(&client), concurrency::OSThread("ethServerAPI"), client(_client)
|
||||
{
|
||||
LOG_INFO("Incoming ethernet connection\n");
|
||||
}
|
||||
|
||||
ethServerAPI::~ethServerAPI()
|
||||
{
|
||||
client.stop();
|
||||
|
||||
// FIXME - delete this if the client dropps the connection!
|
||||
}
|
||||
|
||||
/// override close to also shutdown the TCP link
|
||||
void ethServerAPI::close()
|
||||
{
|
||||
client.stop(); // drop tcp connection
|
||||
StreamAPI::close();
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
bool ethServerAPI::checkIsConnected()
|
||||
{
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
int32_t ethServerAPI::runOnce()
|
||||
{
|
||||
if (client.connected()) {
|
||||
return StreamAPI::runOncePart();
|
||||
} else {
|
||||
LOG_INFO("Client dropped connection, suspending API service\n");
|
||||
enabled = false; // we no longer need to run
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// If an api server is running, we try to spit out debug 'serial' characters there
|
||||
void ethServerPort::debugOut(char c)
|
||||
{
|
||||
if (apiPort && apiPort->openAPI)
|
||||
apiPort->openAPI->debugOut(c);
|
||||
}
|
||||
|
||||
|
||||
ethServerPort::ethServerPort(int port) : EthernetServer(port), concurrency::OSThread("ApiServer") {}
|
||||
|
||||
void ethServerPort::init()
|
||||
{
|
||||
begin();
|
||||
}
|
||||
|
||||
int32_t ethServerPort::runOnce()
|
||||
{
|
||||
auto client = available();
|
||||
if (client) {
|
||||
// Close any previous connection (see FIXME in header file)
|
||||
if (openAPI) {
|
||||
LOG_WARN("Force closing previous TCP connection\n");
|
||||
delete openAPI;
|
||||
}
|
||||
|
||||
openAPI = new ethServerAPI(client);
|
||||
}
|
||||
|
||||
return 100; // only check occasionally for incoming connections
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamAPI.h"
|
||||
#include <RAK13800_W5100S.h>
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class ethServerAPI : public StreamAPI, private concurrency::OSThread
|
||||
{
|
||||
private:
|
||||
EthernetClient client;
|
||||
|
||||
public:
|
||||
explicit ethServerAPI(EthernetClient &_client);
|
||||
|
||||
virtual ~ethServerAPI();
|
||||
|
||||
/// override close to also shutdown the TCP link
|
||||
virtual void close();
|
||||
|
||||
protected:
|
||||
/// We override this method to prevent publishing EVENT_SERIAL_CONNECTED/DISCONNECTED for wifi links (we want the board to
|
||||
/// stay in the POWERED state to prevent disabling wifi)
|
||||
virtual void onConnectionChanged(bool connected) override {}
|
||||
|
||||
virtual int32_t runOnce() override; // Check for dropped client connections
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||
*/
|
||||
class ethServerPort : public EthernetServer, private concurrency::OSThread
|
||||
{
|
||||
/** The currently open port
|
||||
*
|
||||
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
|
||||
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
|
||||
*/
|
||||
ethServerAPI *openAPI = NULL;
|
||||
|
||||
public:
|
||||
explicit ethServerPort(int port);
|
||||
|
||||
void init();
|
||||
|
||||
/// If an api server is running, we try to spit out debug 'serial' characters there
|
||||
static void debugOut(char c);
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override;
|
||||
};
|
||||
|
||||
void initApiServer(int port=4403);
|
||||
@@ -157,7 +157,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg;
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define LocalConfig_size 391
|
||||
#define LocalModuleConfig_size 380
|
||||
#define LocalModuleConfig_size 412
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -36,6 +36,9 @@ PB_BIND(MyNodeInfo, MyNodeInfo, AUTO)
|
||||
PB_BIND(LogRecord, LogRecord, AUTO)
|
||||
|
||||
|
||||
PB_BIND(QueueStatus, QueueStatus, AUTO)
|
||||
|
||||
|
||||
PB_BIND(FromRadio, FromRadio, 2)
|
||||
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ typedef enum _HardwareModel {
|
||||
HardwareModel_HELTEC_V3 = 43,
|
||||
/* New Heltec Wireless Stick Lite with ESP32-S3 CPU */
|
||||
HardwareModel_HELTEC_WSL_V3 = 44,
|
||||
/* New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU */
|
||||
HardwareModel_BETAFPV_2400_TX = 45,
|
||||
/* 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. */
|
||||
HardwareModel_PRIVATE_HW = 255
|
||||
} HardwareModel;
|
||||
@@ -439,9 +441,10 @@ typedef struct _Waypoint {
|
||||
bool locked;
|
||||
/* Name of the waypoint - max 30 chars */
|
||||
char name[30];
|
||||
/* *
|
||||
Description of the waypoint - max 100 chars */
|
||||
/* Description of the waypoint - max 100 chars */
|
||||
char description[100];
|
||||
/* Designator icon for the waypoint in the form of a unicode emoji */
|
||||
uint32_t icon;
|
||||
} Waypoint;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
|
||||
@@ -621,6 +624,17 @@ typedef struct _LogRecord {
|
||||
LogRecord_Level level;
|
||||
} LogRecord;
|
||||
|
||||
typedef struct _QueueStatus {
|
||||
/* Last attempt to queue status, ErrorCode */
|
||||
int8_t res;
|
||||
/* Free entries in the outgoing queue */
|
||||
uint8_t free;
|
||||
/* Maximum entries in the outgoing queue */
|
||||
uint8_t maxlen;
|
||||
/* What was mesh packet id that generated this response? */
|
||||
uint32_t mesh_packet_id;
|
||||
} QueueStatus;
|
||||
|
||||
/* Packets from the radio to the phone will appear on the fromRadio characteristic.
|
||||
It will support READ and NOTIFY. When a new packet arrives the device will BLE notify?
|
||||
It will sit in that descriptor until consumed by the phone,
|
||||
@@ -657,6 +671,8 @@ typedef struct _FromRadio {
|
||||
ModuleConfig moduleConfig;
|
||||
/* One packet is sent for each channel */
|
||||
Channel channel;
|
||||
/* Queue status info */
|
||||
QueueStatus queueStatus;
|
||||
};
|
||||
} FromRadio;
|
||||
|
||||
@@ -755,6 +771,7 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
#define Compressed_portnum_ENUMTYPE PortNum
|
||||
|
||||
|
||||
@@ -764,11 +781,12 @@ extern "C" {
|
||||
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define Routing_init_default {0, {RouteDiscovery_init_default}}
|
||||
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
|
||||
#define Waypoint_init_default {0, 0, 0, 0, 0, "", ""}
|
||||
#define Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, false, DeviceMetrics_init_default}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
|
||||
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define QueueStatus_init_default {0, 0, 0, 0}
|
||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
#define Compressed_init_default {_PortNum_MIN, {0, {0}}}
|
||||
@@ -777,11 +795,12 @@ extern "C" {
|
||||
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
|
||||
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
|
||||
#define Waypoint_init_zero {0, 0, 0, 0, 0, "", ""}
|
||||
#define Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, false, DeviceMetrics_init_zero}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
|
||||
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define QueueStatus_init_zero {0, 0, 0, 0}
|
||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||
#define Compressed_init_zero {_PortNum_MIN, {0, {0}}}
|
||||
@@ -834,6 +853,7 @@ extern "C" {
|
||||
#define Waypoint_locked_tag 5
|
||||
#define Waypoint_name_tag 6
|
||||
#define Waypoint_description_tag 7
|
||||
#define Waypoint_icon_tag 8
|
||||
#define MeshPacket_from_tag 1
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_channel_tag 3
|
||||
@@ -873,6 +893,10 @@ extern "C" {
|
||||
#define LogRecord_time_tag 2
|
||||
#define LogRecord_source_tag 3
|
||||
#define LogRecord_level_tag 4
|
||||
#define QueueStatus_res_tag 1
|
||||
#define QueueStatus_free_tag 2
|
||||
#define QueueStatus_maxlen_tag 3
|
||||
#define QueueStatus_mesh_packet_id_tag 4
|
||||
#define FromRadio_id_tag 1
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
@@ -883,6 +907,7 @@ extern "C" {
|
||||
#define FromRadio_rebooted_tag 8
|
||||
#define FromRadio_moduleConfig_tag 9
|
||||
#define FromRadio_channel_tag 10
|
||||
#define FromRadio_queueStatus_tag 11
|
||||
#define ToRadio_packet_tag 1
|
||||
#define ToRadio_want_config_id_tag 3
|
||||
#define ToRadio_disconnect_tag 4
|
||||
@@ -959,7 +984,8 @@ X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, expire, 4) \
|
||||
X(a, STATIC, SINGULAR, BOOL, locked, 5) \
|
||||
X(a, STATIC, SINGULAR, STRING, name, 6) \
|
||||
X(a, STATIC, SINGULAR, STRING, description, 7)
|
||||
X(a, STATIC, SINGULAR, STRING, description, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, icon, 8)
|
||||
#define Waypoint_CALLBACK NULL
|
||||
#define Waypoint_DEFAULT NULL
|
||||
|
||||
@@ -1022,6 +1048,14 @@ X(a, STATIC, SINGULAR, UENUM, level, 4)
|
||||
#define LogRecord_CALLBACK NULL
|
||||
#define LogRecord_DEFAULT NULL
|
||||
|
||||
#define QueueStatus_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, res, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, free, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, maxlen, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, mesh_packet_id, 4)
|
||||
#define QueueStatus_CALLBACK NULL
|
||||
#define QueueStatus_DEFAULT NULL
|
||||
|
||||
#define FromRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 2) \
|
||||
@@ -1032,7 +1066,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,log_record,log_record), 6)
|
||||
X(a, STATIC, ONEOF, UINT32, (payload_variant,config_complete_id,config_complete_id), 7) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,rebooted,rebooted), 8) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11)
|
||||
#define FromRadio_CALLBACK NULL
|
||||
#define FromRadio_DEFAULT NULL
|
||||
#define FromRadio_payload_variant_packet_MSGTYPE MeshPacket
|
||||
@@ -1042,6 +1077,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10)
|
||||
#define FromRadio_payload_variant_log_record_MSGTYPE LogRecord
|
||||
#define FromRadio_payload_variant_moduleConfig_MSGTYPE ModuleConfig
|
||||
#define FromRadio_payload_variant_channel_MSGTYPE Channel
|
||||
#define FromRadio_payload_variant_queueStatus_MSGTYPE QueueStatus
|
||||
|
||||
#define ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
|
||||
@@ -1067,6 +1103,7 @@ extern const pb_msgdesc_t MeshPacket_msg;
|
||||
extern const pb_msgdesc_t NodeInfo_msg;
|
||||
extern const pb_msgdesc_t MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t LogRecord_msg;
|
||||
extern const pb_msgdesc_t QueueStatus_msg;
|
||||
extern const pb_msgdesc_t FromRadio_msg;
|
||||
extern const pb_msgdesc_t ToRadio_msg;
|
||||
extern const pb_msgdesc_t Compressed_msg;
|
||||
@@ -1082,6 +1119,7 @@ extern const pb_msgdesc_t Compressed_msg;
|
||||
#define NodeInfo_fields &NodeInfo_msg
|
||||
#define MyNodeInfo_fields &MyNodeInfo_msg
|
||||
#define LogRecord_fields &LogRecord_msg
|
||||
#define QueueStatus_fields &QueueStatus_msg
|
||||
#define FromRadio_fields &FromRadio_msg
|
||||
#define ToRadio_fields &ToRadio_msg
|
||||
#define Compressed_fields &Compressed_msg
|
||||
@@ -1095,11 +1133,12 @@ extern const pb_msgdesc_t Compressed_msg;
|
||||
#define MyNodeInfo_size 179
|
||||
#define NodeInfo_size 258
|
||||
#define Position_size 137
|
||||
#define QueueStatus_size 23
|
||||
#define RouteDiscovery_size 40
|
||||
#define Routing_size 42
|
||||
#define ToRadio_size 324
|
||||
#define User_size 77
|
||||
#define Waypoint_size 156
|
||||
#define Waypoint_size 161
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -80,7 +80,7 @@ typedef struct _ModuleConfig_MQTTConfig {
|
||||
bool enabled;
|
||||
/* The server to use for our MQTT global message gateway feature.
|
||||
If not set, the default server will be used */
|
||||
char address[32];
|
||||
char address[64];
|
||||
/* MQTT username to use (most useful for a custom MQTT server).
|
||||
If using a custom server, this will be honoured even if empty.
|
||||
If using the default server, this will only be honoured if set, otherwise the device will use the default username */
|
||||
@@ -301,6 +301,7 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
#define ModuleConfig_AudioConfig_bitrate_ENUMTYPE ModuleConfig_AudioConfig_Audio_Baud
|
||||
|
||||
#define ModuleConfig_SerialConfig_baud_ENUMTYPE ModuleConfig_SerialConfig_Serial_Baud
|
||||
@@ -552,13 +553,13 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg;
|
||||
#define ModuleConfig_AudioConfig_size 19
|
||||
#define ModuleConfig_CannedMessageConfig_size 49
|
||||
#define ModuleConfig_ExternalNotificationConfig_size 40
|
||||
#define ModuleConfig_MQTTConfig_size 169
|
||||
#define ModuleConfig_MQTTConfig_size 201
|
||||
#define ModuleConfig_RangeTestConfig_size 10
|
||||
#define ModuleConfig_RemoteHardwareConfig_size 2
|
||||
#define ModuleConfig_SerialConfig_size 26
|
||||
#define ModuleConfig_StoreForwardConfig_size 22
|
||||
#define ModuleConfig_TelemetryConfig_size 18
|
||||
#define ModuleConfig_size 172
|
||||
#define ModuleConfig_size 204
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "mesh/wifi/WiFiServerAPI.h"
|
||||
#include "mesh/api/WiFiServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
#include "target_specific.h"
|
||||
#include <ESPmDNS.h>
|
||||
@@ -55,11 +55,13 @@ static int32_t reconnectWiFi()
|
||||
|
||||
// Make sure we clear old connection credentials
|
||||
WiFi.disconnect(false, true);
|
||||
|
||||
LOG_INFO("Reconnecting to WiFi access point %s\n",wifiName);
|
||||
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
WiFi.begin(wifiName, wifiPsw);
|
||||
delay(5000);
|
||||
|
||||
if (!WiFi.isConnected()) {
|
||||
WiFi.begin(wifiName, wifiPsw);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NTP
|
||||
@@ -167,7 +169,7 @@ bool initWifi()
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
WiFi.setHostname(ourHost);
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
WiFi.setAutoReconnect(false);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.setSleep(false);
|
||||
if (config.network.address_mode == Config_NetworkConfig_AddressMode_STATIC && config.network.ipv4_config.ip != 0) {
|
||||
WiFi.config(config.network.ipv4_config.ip,
|
||||
@@ -182,7 +184,8 @@ bool initWifi()
|
||||
|
||||
WiFi.onEvent(
|
||||
[](WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
LOG_WARN("WiFi lost connection. Reason: %s", info.wifi_sta_disconnected.reason);
|
||||
|
||||
LOG_WARN("WiFi lost connection. Reason: %d\n", info.wifi_sta_disconnected.reason);
|
||||
|
||||
/*
|
||||
If we are disconnected from the AP for some reason,
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#include "WiFiServerAPI.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
static WiFiServerPort *apiPort;
|
||||
|
||||
void initApiServer(int port)
|
||||
{
|
||||
// Start API server on port 4403
|
||||
if (!apiPort) {
|
||||
apiPort = new WiFiServerPort(port);
|
||||
LOG_INFO("API server listening on TCP port %d\n", port);
|
||||
apiPort->init();
|
||||
}
|
||||
}
|
||||
|
||||
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), concurrency::OSThread("WiFiServerAPI"), client(_client)
|
||||
{
|
||||
LOG_INFO("Incoming wifi connection\n");
|
||||
}
|
||||
|
||||
WiFiServerAPI::~WiFiServerAPI()
|
||||
{
|
||||
client.stop();
|
||||
|
||||
// FIXME - delete this if the client dropps the connection!
|
||||
}
|
||||
|
||||
/// override close to also shutdown the TCP link
|
||||
void WiFiServerAPI::close()
|
||||
{
|
||||
client.stop(); // drop tcp connection
|
||||
StreamAPI::close();
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
bool WiFiServerAPI::checkIsConnected()
|
||||
{
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
int32_t WiFiServerAPI::runOnce()
|
||||
{
|
||||
if (client.connected()) {
|
||||
return StreamAPI::runOncePart();
|
||||
} else {
|
||||
LOG_INFO("Client dropped connection, suspending API service\n");
|
||||
enabled = false; // we no longer need to run
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// If an api server is running, we try to spit out debug 'serial' characters there
|
||||
void WiFiServerPort::debugOut(char c)
|
||||
{
|
||||
if (apiPort && apiPort->openAPI)
|
||||
apiPort->openAPI->debugOut(c);
|
||||
}
|
||||
|
||||
|
||||
WiFiServerPort::WiFiServerPort(int port) : WiFiServer(port), concurrency::OSThread("ApiServer") {}
|
||||
|
||||
void WiFiServerPort::init()
|
||||
{
|
||||
begin();
|
||||
}
|
||||
|
||||
int32_t WiFiServerPort::runOnce()
|
||||
{
|
||||
auto client = available();
|
||||
if (client) {
|
||||
// Close any previous connection (see FIXME in header file)
|
||||
if (openAPI) {
|
||||
LOG_INFO("Force closing previous TCP connection\n");
|
||||
delete openAPI;
|
||||
}
|
||||
|
||||
openAPI = new WiFiServerAPI(client);
|
||||
}
|
||||
|
||||
return 100; // only check occasionally for incoming connections
|
||||
}
|
||||
@@ -549,10 +549,10 @@ AdminMessageHandleResult CannedMessageModule::handleAdminMessageForModule(const
|
||||
void CannedMessageModule::handleGetCannedMessageModuleMessages(const MeshPacket &req, AdminMessage *response)
|
||||
{
|
||||
LOG_DEBUG("*** handleGetCannedMessageModuleMessages\n");
|
||||
assert(req.decoded.want_response);
|
||||
|
||||
response->which_payload_variant = AdminMessage_get_canned_message_module_messages_response_tag;
|
||||
strcpy(response->get_canned_message_module_messages_response, cannedMessageModuleConfig.messages);
|
||||
if(req.decoded.want_response) {
|
||||
response->which_payload_variant = AdminMessage_get_canned_message_module_messages_response_tag;
|
||||
strcpy(response->get_canned_message_module_messages_response, cannedMessageModuleConfig.messages);
|
||||
} // Don't send anything if not instructed to. Better than asserting.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,26 +42,22 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
if (!moduleConfig.external_notification.enabled) {
|
||||
return INT32_MAX; // we don't need this thread here...
|
||||
} else {
|
||||
#ifndef ARCH_PORTDUINO
|
||||
if ((nagCycleCutoff < millis()) && !rtttl::isPlaying()) {
|
||||
#else
|
||||
if (nagCycleCutoff < millis()) {
|
||||
#endif
|
||||
// let the song finish if we reach timeout
|
||||
nagCycleCutoff = UINT32_MAX;
|
||||
LOG_INFO("Turning off external notification: ");
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (getExternal(i)) {
|
||||
setExternalOff(i);
|
||||
externalTurnedOn[i] = 0;
|
||||
LOG_INFO("%d ", i);
|
||||
}
|
||||
setExternalOff(i);
|
||||
externalTurnedOn[i] = 0;
|
||||
LOG_INFO("%d ", i);
|
||||
}
|
||||
LOG_INFO("\n");
|
||||
isNagging = false;
|
||||
return INT32_MAX; // save cycles till we're needed again
|
||||
}
|
||||
|
||||
// If the output is turned on, turn it back off after the given period of time.
|
||||
if (nagCycleCutoff != UINT32_MAX) {
|
||||
if (isNagging) {
|
||||
if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms
|
||||
? moduleConfig.external_notification.output_ms
|
||||
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) {
|
||||
@@ -80,16 +76,14 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
}
|
||||
|
||||
// now let the PWM buzzer play
|
||||
#ifndef ARCH_PORTDUINO
|
||||
if (moduleConfig.external_notification.use_pwm) {
|
||||
if (rtttl::isPlaying()) {
|
||||
rtttl::play();
|
||||
} else if (nagCycleCutoff >= millis()) {
|
||||
} else if (isNagging && (nagCycleCutoff >= millis())) {
|
||||
// start the song again if we have time left
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
@@ -140,10 +134,9 @@ bool ExternalNotificationModule::getExternal(uint8_t index)
|
||||
}
|
||||
|
||||
void ExternalNotificationModule::stopNow() {
|
||||
#ifndef ARCH_PORTDUINO
|
||||
rtttl::stop();
|
||||
#endif
|
||||
nagCycleCutoff = 1; // small value
|
||||
isNagging = false;
|
||||
setIntervalFromNow(0);
|
||||
}
|
||||
|
||||
@@ -230,6 +223,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
|
||||
if (moduleConfig.external_notification.alert_bell) {
|
||||
if (containsBell) {
|
||||
LOG_INFO("externalNotificationModule - Notification Bell\n");
|
||||
isNagging = true;
|
||||
setExternalOn(0);
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
@@ -242,6 +236,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
|
||||
if (moduleConfig.external_notification.alert_bell_vibra) {
|
||||
if (containsBell) {
|
||||
LOG_INFO("externalNotificationModule - Notification Bell (Vibra)\n");
|
||||
isNagging = true;
|
||||
setExternalOn(1);
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
@@ -254,12 +249,11 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
|
||||
if (moduleConfig.external_notification.alert_bell_buzzer) {
|
||||
if (containsBell) {
|
||||
LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)\n");
|
||||
isNagging = true;
|
||||
if (!moduleConfig.external_notification.use_pwm) {
|
||||
setExternalOn(2);
|
||||
} else {
|
||||
#ifndef ARCH_PORTDUINO
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
#endif
|
||||
}
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
@@ -271,6 +265,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
|
||||
|
||||
if (moduleConfig.external_notification.alert_message) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module\n");
|
||||
isNagging = true;
|
||||
setExternalOn(0);
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
@@ -279,33 +274,33 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!moduleConfig.external_notification.use_pwm) {
|
||||
if (moduleConfig.external_notification.alert_message_vibra) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Vibra)\n");
|
||||
setExternalOn(1);
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_buzzer) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n");
|
||||
if (!moduleConfig.external_notification.use_pwm) {
|
||||
setExternalOn(2);
|
||||
} else {
|
||||
#ifndef ARCH_PORTDUINO
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
#endif
|
||||
}
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_vibra) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Vibra)\n");
|
||||
isNagging = true;
|
||||
setExternalOn(1);
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_buzzer) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n");
|
||||
isNagging = true;
|
||||
if (!moduleConfig.external_notification.use_pwm) {
|
||||
setExternalOn(2);
|
||||
} else {
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
}
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
}
|
||||
}
|
||||
|
||||
setIntervalFromNow(0); // run once so we know if we should do something
|
||||
}
|
||||
|
||||
@@ -352,10 +347,10 @@ AdminMessageHandleResult ExternalNotificationModule::handleAdminMessageForModule
|
||||
void ExternalNotificationModule::handleGetRingtone(const MeshPacket &req, AdminMessage *response)
|
||||
{
|
||||
LOG_INFO("*** handleGetRingtone\n");
|
||||
assert(req.decoded.want_response);
|
||||
|
||||
response->which_payload_variant = AdminMessage_get_ringtone_response_tag;
|
||||
strcpy(response->get_ringtone_response, rtttlConfig.ringtone);
|
||||
if(req.decoded.want_response) {
|
||||
response->which_payload_variant = AdminMessage_get_ringtone_response_tag;
|
||||
strcpy(response->get_ringtone_response, rtttlConfig.ringtone);
|
||||
} // Don't send anything if not instructed to. Better than asserting.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,18 @@
|
||||
#include "configuration.h"
|
||||
#ifndef ARCH_PORTDUINO
|
||||
#include <NonBlockingRtttl.h>
|
||||
#else
|
||||
// Noop class for portduino.
|
||||
class rtttl
|
||||
{
|
||||
public:
|
||||
explicit rtttl() {}
|
||||
static bool isPlaying() { return false; }
|
||||
static void play() {}
|
||||
static void begin(byte a, const char * b) {};
|
||||
static void stop() {}
|
||||
static bool done() { return true; }
|
||||
};
|
||||
#endif
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
@@ -39,6 +51,8 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
bool isNagging = false;
|
||||
|
||||
virtual AdminMessageHandleResult handleAdminMessageForModule(const MeshPacket &mp, AdminMessage *request, AdminMessage *response) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -65,8 +65,10 @@ int32_t NodeInfoModule::runOnce()
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
LOG_INFO("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
if (airTime->isTxAllowedAirUtil()) {
|
||||
LOG_INFO("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
}
|
||||
|
||||
return default_broadcast_interval_secs * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ int32_t PositionModule::runOnce()
|
||||
if (lastGpsSend == 0 || (now - lastGpsSend) >= intervalMs) {
|
||||
|
||||
// Only send packets if the channel is less than 40% utilized.
|
||||
if (airTime->channelUtilizationPercent() < max_channel_util_percent) {
|
||||
if (airTime->isTxAllowedChannelUtil()) {
|
||||
if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) {
|
||||
lastGpsSend = now;
|
||||
|
||||
@@ -158,14 +158,12 @@ int32_t PositionModule::runOnce()
|
||||
LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", node->position.timestamp, requestReplies);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("Channel utilization is >40 percent. Skipping this opportunity to send.\n");
|
||||
}
|
||||
|
||||
} else if (config.position.position_broadcast_smart_enabled) {
|
||||
|
||||
// Only send packets if the channel is less than 25% utilized.
|
||||
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
|
||||
NodeInfo *node2 = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
|
||||
@@ -208,8 +206,6 @@ int32_t PositionModule::runOnce()
|
||||
lastGpsSend = now;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("Channel utilization is >25 percent. Skipping this opportunity to send.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
SerialModule
|
||||
A simple interface to send messages over the mesh network by sending strings
|
||||
@@ -63,7 +61,8 @@ SerialModuleRadio *serialModuleRadio;
|
||||
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {}
|
||||
|
||||
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
|
||||
char serialBytes[Constants_DATA_PAYLOAD_LEN];
|
||||
size_t serialPayloadSize;
|
||||
|
||||
SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
|
||||
{
|
||||
@@ -205,15 +204,9 @@ int32_t SerialModule::runOnce()
|
||||
Serial2.printf("%s", outbuf);
|
||||
}
|
||||
} else {
|
||||
String serialString;
|
||||
|
||||
while (Serial2.available()) {
|
||||
serialString = Serial2.readString();
|
||||
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
|
||||
|
||||
serialPayloadSize = Serial2.readBytes(serialBytes, Constants_DATA_PAYLOAD_LEN);
|
||||
serialModuleRadio->sendPayload();
|
||||
|
||||
LOG_INFO("Received: %s\n", serialStringChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,14 +226,18 @@ MeshPacket *SerialModuleRadio::allocReply()
|
||||
|
||||
void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
Channel *ch = (boundChannel != NULL) ? &channels.getByName(boundChannel) : NULL;
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
if (ch != NULL) {
|
||||
p->channel = ch->index;
|
||||
}
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
p->want_ack = ACK;
|
||||
|
||||
p->decoded.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.payload.bytes, serialStringChar, p->decoded.payload.size);
|
||||
p->decoded.payload.size = serialPayloadSize; // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.payload.bytes, serialBytes, p->decoded.payload.size);
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
|
||||
int32_t DeviceTelemetryModule::runOnce()
|
||||
{
|
||||
#ifndef ARCH_PORTDUINO
|
||||
uint32_t now = millis();
|
||||
if ((lastSentToMesh == 0 ||
|
||||
(now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval)) &&
|
||||
airTime->channelUtilizationPercent() < max_channel_util_percent) {
|
||||
airTime->isTxAllowedChannelUtil() && airTime->isTxAllowedAirUtil()) {
|
||||
sendTelemetry();
|
||||
lastSentToMesh = now;
|
||||
} else if (service.isToPhoneQueueEmpty()) {
|
||||
@@ -26,7 +25,6 @@ int32_t DeviceTelemetryModule::runOnce()
|
||||
sendTelemetry(NODENUM_BROADCAST, true);
|
||||
}
|
||||
return sendToPhoneIntervalMs;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t)
|
||||
|
||||
@@ -107,7 +107,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
||||
uint32_t now = millis();
|
||||
if ((lastSentToMesh == 0 ||
|
||||
(now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval)) &&
|
||||
airTime->channelUtilizationPercent() < max_channel_util_percent) {
|
||||
airTime->isTxAllowedAirUtil()) {
|
||||
sendTelemetry();
|
||||
lastSentToMesh = now;
|
||||
} else if (service.isToPhoneQueueEmpty()) {
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include "Router.h"
|
||||
#include "FSCommon.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef OLED_RU
|
||||
#include "graphics/fonts/OLEDDisplayFontsRU.h"
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "gps/GeoCoord.h"
|
||||
#include <Arduino.h>
|
||||
#include <FSCommon.h>
|
||||
//#include <assert.h>
|
||||
|
||||
/*
|
||||
As a sender, I can send packets every n seconds. These packets include an incremented PacketID.
|
||||
@@ -74,10 +73,8 @@ int32_t RangeTestModule::runOnce()
|
||||
LOG_INFO("fixed_position() %d\n", config.position.fixed_position);
|
||||
|
||||
// Only send packets if the channel is less than 25% utilized.
|
||||
if (airTime->channelUtilizationPercent() < 25) {
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
rangeTestModuleRadio->sendPayload();
|
||||
} else {
|
||||
LOG_WARN("RangeTest - Channel utilization is >25 percent. Skipping this opportunity to send.\n");
|
||||
}
|
||||
|
||||
return (senderHeartbeat);
|
||||
|
||||
@@ -21,7 +21,7 @@ int32_t StoreForwardModule::runOnce()
|
||||
// Send out the message queue.
|
||||
if (this->busy) {
|
||||
// Only send packets if the channel is less than 25% utilized.
|
||||
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
|
||||
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
|
||||
// Tell the client we're done sending
|
||||
@@ -34,12 +34,10 @@ int32_t StoreForwardModule::runOnce()
|
||||
} else {
|
||||
this->packetHistoryTXQueue_index++;
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("*** Channel utilization is too high. Retrying later.\n");
|
||||
}
|
||||
LOG_DEBUG("*** SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
|
||||
|
||||
} else if ((millis() - lastHeartbeat > (heartbeatInterval * 1000)) && (airTime->channelUtilizationPercent() < polite_channel_util_percent)) {
|
||||
} else if ((millis() - lastHeartbeat > (heartbeatInterval * 1000)) && airTime->isTxAllowedChannelUtil(true)) {
|
||||
lastHeartbeat = millis();
|
||||
LOG_INFO("*** Sending heartbeat\n");
|
||||
StoreAndForward sf = StoreAndForward_init_zero;
|
||||
@@ -392,7 +390,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndFo
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
assert(0); // unexpected state
|
||||
}
|
||||
return true; // There's no need for others to look at this message.
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ String statusTopic = "msh/2/stat/";
|
||||
String cryptTopic = "msh/2/c/"; // msh/2/c/CHANNELID/NODEID
|
||||
String jsonTopic = "msh/2/json/"; // msh/2/json/CHANNELID/NODEID
|
||||
|
||||
static MemoryDynamic<ServiceEnvelope> staticMqttPool;
|
||||
|
||||
Allocator<ServiceEnvelope> &mqttPool = staticMqttPool;
|
||||
|
||||
void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||
{
|
||||
mqtt->onPublish(topic, payload, length);
|
||||
@@ -39,9 +43,20 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
|
||||
JSONValue *json_value = JSON::Parse(payloadStr);
|
||||
if (json_value != NULL) {
|
||||
LOG_INFO("JSON Received on MQTT, parsing..\n");
|
||||
|
||||
// check if it is a valid envelope
|
||||
JSONObject json;
|
||||
json = json_value->AsObject();
|
||||
|
||||
// parse the channel name from the topic string
|
||||
char *ptr = strtok(topic, "/");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ptr = strtok(NULL, "/");
|
||||
}
|
||||
LOG_DEBUG("Looking for Channel name: %s\n", ptr);
|
||||
Channel sendChannel = channels.getByName(ptr);
|
||||
LOG_DEBUG("Found Channel name: %s (Index %d)\n", channels.getGlobalId(sendChannel.settings.channel_num), sendChannel.settings.channel_num);
|
||||
|
||||
if ((json.find("sender") != json.end()) && (json.find("payload") != json.end()) && (json.find("type") != json.end()) && json["type"]->IsString() && (json["type"]->AsString().compare("sendtext") == 0)) {
|
||||
// this is a valid envelope
|
||||
if (json["payload"]->IsString() && json["type"]->IsString() && (json["sender"]->AsString().compare(owner.id) != 0)) {
|
||||
@@ -51,13 +66,18 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
|
||||
// construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
|
||||
MeshPacket *p = router->allocForSending();
|
||||
p->decoded.portnum = PortNum_TEXT_MESSAGE_APP;
|
||||
if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
|
||||
memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
|
||||
p->decoded.payload.size = jsonPayloadStr.length();
|
||||
MeshPacket *packet = packetPool.allocCopy(*p);
|
||||
service.sendToMesh(packet, RX_SRC_LOCAL);
|
||||
p->channel = sendChannel.settings.channel_num;
|
||||
if (sendChannel.settings.downlink_enabled) {
|
||||
if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
|
||||
memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
|
||||
p->decoded.payload.size = jsonPayloadStr.length();
|
||||
MeshPacket *packet = packetPool.allocCopy(*p);
|
||||
service.sendToMesh(packet, RX_SRC_LOCAL);
|
||||
} else {
|
||||
LOG_WARN("Received MQTT json payload too long, dropping\n");
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("Received MQTT json payload too long, dropping\n");
|
||||
LOG_WARN("Received MQTT json payload on channel %s, but downlink is disabled, dropping\n", sendChannel.settings.name);
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("JSON Ignoring downlink message we originally sent.\n");
|
||||
@@ -76,9 +96,13 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
|
||||
// construct protobuf data packet using POSITION, send it to the mesh
|
||||
MeshPacket *p = router->allocForSending();
|
||||
p->decoded.portnum = PortNum_POSITION_APP;
|
||||
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Position_msg, &pos); //make the Data protobuf from position
|
||||
service.sendToMesh(p, RX_SRC_LOCAL);
|
||||
|
||||
p->channel = sendChannel.settings.channel_num;
|
||||
if (sendChannel.settings.downlink_enabled) {
|
||||
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Position_msg, &pos); //make the Data protobuf from position
|
||||
service.sendToMesh(p, RX_SRC_LOCAL);
|
||||
} else {
|
||||
LOG_WARN("Received MQTT json payload on channel %s, but downlink is disabled, dropping\n", sendChannel.settings.name);
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("JSON Ignoring downlink message we originally sent.\n");
|
||||
}
|
||||
@@ -122,7 +146,7 @@ void mqttInit()
|
||||
new MQTT();
|
||||
}
|
||||
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient)
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
|
||||
{
|
||||
if(moduleConfig.mqtt.enabled) {
|
||||
|
||||
@@ -183,14 +207,17 @@ void MQTT::reconnect()
|
||||
|
||||
sendSubscriptions();
|
||||
} else {
|
||||
LOG_ERROR("Failed to contact MQTT server (%d/10)...\n",reconnectCount);
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
if (reconnectCount > 9) {
|
||||
LOG_ERROR("Failed to contact MQTT server (%d/5)...\n",reconnectCount + 1);
|
||||
if (reconnectCount >= 4) {
|
||||
needReconnect = true;
|
||||
wifiReconnect->setIntervalFromNow(1000);
|
||||
wifiReconnect->setIntervalFromNow(0);
|
||||
reconnectCount = 0;
|
||||
} else {
|
||||
reconnectCount++;
|
||||
}
|
||||
|
||||
#endif
|
||||
reconnectCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,9 +276,35 @@ int32_t MQTT::runOnce()
|
||||
if (!pubSub.loop()) {
|
||||
if (wantConnection) {
|
||||
reconnect();
|
||||
|
||||
// If we succeeded, start reading rapidly, else try again in 30 seconds (TCP connections are EXPENSIVE so try rarely)
|
||||
return pubSub.connected() ? 20 : 30000;
|
||||
|
||||
// If we succeeded, empty the queue one by one and start reading rapidly, else try again in 30 seconds (TCP connections are EXPENSIVE so try rarely)
|
||||
if (pubSub.connected()) {
|
||||
if (!mqttQueue.isEmpty()) {
|
||||
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
|
||||
ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
|
||||
static uint8_t bytes[MeshPacket_size + 64];
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &ServiceEnvelope_msg, env);
|
||||
|
||||
String topic = cryptTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
|
||||
|
||||
pubSub.publish(topic.c_str(), bytes, numBytes, false);
|
||||
|
||||
if (moduleConfig.mqtt.json_enabled) {
|
||||
// handle json topic
|
||||
auto jsonString = this->downstreamPacketToJson(env->packet);
|
||||
if (jsonString.length() != 0) {
|
||||
String topicJson = jsonTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
||||
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
}
|
||||
}
|
||||
mqttPool.release(env);
|
||||
}
|
||||
return 200;
|
||||
} else {
|
||||
return 30000;
|
||||
}
|
||||
} else
|
||||
return 5000; // If we don't want connection now, check again in 5 secs
|
||||
} else {
|
||||
@@ -273,17 +326,17 @@ void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex)
|
||||
if (ch.settings.uplink_enabled) {
|
||||
const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel
|
||||
|
||||
ServiceEnvelope env = ServiceEnvelope_init_default;
|
||||
env.channel_id = (char *)channelId;
|
||||
env.gateway_id = owner.id;
|
||||
env.packet = (MeshPacket *)∓
|
||||
ServiceEnvelope *env = mqttPool.allocZeroed();
|
||||
env->channel_id = (char *)channelId;
|
||||
env->gateway_id = owner.id;
|
||||
env->packet = (MeshPacket *)∓
|
||||
|
||||
// don't bother sending if not connected...
|
||||
if (pubSub.connected()) {
|
||||
|
||||
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
|
||||
static uint8_t bytes[MeshPacket_size + 64];
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &ServiceEnvelope_msg, &env);
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &ServiceEnvelope_msg, env);
|
||||
|
||||
String topic = cryptTopic + channelId + "/" + owner.id;
|
||||
LOG_DEBUG("publish %s, %u bytes\n", topic.c_str(), numBytes);
|
||||
@@ -299,7 +352,19 @@ void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex)
|
||||
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("MQTT not connected, queueing packet\n");
|
||||
if (mqttQueue.numFree() == 0) {
|
||||
LOG_WARN("NOTE: MQTT queue is full, discarding oldest\n");
|
||||
ServiceEnvelope *d = mqttQueue.dequeuePtr(0);
|
||||
if (d)
|
||||
mqttPool.release(d);
|
||||
}
|
||||
// make a copy of serviceEnvelope and queue it
|
||||
ServiceEnvelope *copied = mqttPool.allocCopy(*env);
|
||||
assert(mqttQueue.enqueue(copied, 0));
|
||||
}
|
||||
mqttPool.release(env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <EthernetClient.h>
|
||||
#endif
|
||||
|
||||
#define MAX_MQTT_QUEUE 16
|
||||
|
||||
/**
|
||||
* Our wrapper/singleton for sending/receiving MQTT "udp" packets. This object isolates the MQTT protocol implementation from
|
||||
* the two components that use it: MQTTPlugin and MQTTSimInterface.
|
||||
@@ -53,9 +55,10 @@ class MQTT : private concurrency::OSThread
|
||||
bool connected();
|
||||
|
||||
protected:
|
||||
PointerQueue<ServiceEnvelope> mqttQueue;
|
||||
|
||||
int reconnectCount = 0;
|
||||
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -42,19 +42,21 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 0) {
|
||||
uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
size_t nc_off = 0;
|
||||
|
||||
LOG_DEBUG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetId, numBytes);
|
||||
initNonce(fromNode, packetId);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
if (numBytes <= MAX_BLOCKSIZE) {
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
uint8_t stream_block[16];
|
||||
size_t nc_off = 0;
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
|
||||
|
||||
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes);
|
||||
assert(!res);
|
||||
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes);
|
||||
assert(!res);
|
||||
} else {
|
||||
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +172,9 @@ void cpuDeepSleep(uint64_t msecToWake)
|
||||
Serial1.end();
|
||||
#endif
|
||||
setBluetoothEnable(false);
|
||||
#ifdef RAK4630
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
#endif
|
||||
// FIXME, use system off mode with ram retention for key state?
|
||||
// FIXME, use non-init RAM per
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||
|
||||
@@ -50,20 +50,19 @@ class CrossPlatformCryptoEngine : public CryptoEngine
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 0) {
|
||||
//uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
//size_t nc_off = 0;
|
||||
|
||||
// LOG_DEBUG("ESP32 encrypt!\n");
|
||||
initNonce(fromNode, packetId);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
if (numBytes <= MAX_BLOCKSIZE) {
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
|
||||
|
||||
ctr->setIV(nonce, sizeof(nonce));
|
||||
ctr->setCounterSize(4);
|
||||
ctr->encrypt(bytes, scratch, numBytes);
|
||||
ctr->setIV(nonce, sizeof(nonce));
|
||||
ctr->setCounterSize(4);
|
||||
ctr->encrypt(bytes, scratch, numBytes);
|
||||
} else {
|
||||
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,12 +215,27 @@ void SimRadio::startReceive(MeshPacket *p) {
|
||||
handleReceiveInterrupt(p);
|
||||
}
|
||||
|
||||
QueueStatus SimRadio::getQueueStatus()
|
||||
{
|
||||
QueueStatus qs;
|
||||
|
||||
qs.res = qs.mesh_packet_id = 0;
|
||||
qs.free = txQueue.getFree();
|
||||
qs.maxlen = txQueue.getMaxLen();
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
void SimRadio::handleReceiveInterrupt(MeshPacket *p)
|
||||
{
|
||||
LOG_DEBUG("HANDLE RECEIVE INTERRUPT\n");
|
||||
uint32_t xmitMsec;
|
||||
assert(isReceiving);
|
||||
|
||||
if (!isReceiving) {
|
||||
LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
isReceiving = false;
|
||||
|
||||
// read the number of actually received bytes
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "RadioInterface.h"
|
||||
#include "MeshPacketQueue.h"
|
||||
#include "wifi/WiFiServerAPI.h"
|
||||
#include "api/WiFiServerAPI.h"
|
||||
|
||||
#include <RadioLib.h>
|
||||
|
||||
@@ -45,6 +45,9 @@ class SimRadio : public RadioInterface
|
||||
*/
|
||||
virtual void startReceive(MeshPacket *p);
|
||||
|
||||
QueueStatus getQueueStatus() override;
|
||||
|
||||
|
||||
protected:
|
||||
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
|
||||
bool isReceiving = false;
|
||||
|
||||
@@ -163,7 +163,7 @@ static void waitEnterSleep()
|
||||
}
|
||||
|
||||
// Code that still needs to be moved into notifyObservers
|
||||
Serial.flush(); // send all our characters before we stop cpu clock
|
||||
console->flush(); // send all our characters before we stop cpu clock
|
||||
setBluetoothEnable(false); // has to be off before calling light sleep
|
||||
|
||||
notifySleep.notifyObservers(NULL);
|
||||
|
||||
17
variants/betafpv_2400_tx_micro/platformio.ini
Normal file
17
variants/betafpv_2400_tx_micro/platformio.ini
Normal file
@@ -0,0 +1,17 @@
|
||||
[env:betafpv_2400_tx_micro]
|
||||
extends = esp32_base
|
||||
board = esp32doit-devkit-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-D BETAFPV_2400_TX
|
||||
-D VTABLES_IN_FLASH=1
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||
-O2
|
||||
-I variants/betafpv_2400_tx_micro
|
||||
board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
upload_port = /dev/ttyUSB0
|
||||
upload_speed = 460800
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
makuna/NeoPixelBus@^2.7.1
|
||||
34
variants/betafpv_2400_tx_micro/variant.h
Normal file
34
variants/betafpv_2400_tx_micro/variant.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//https://betafpv.com/products/elrs-micro-tx-module
|
||||
#include <NeoPixelBus.h>
|
||||
|
||||
// 0.96" OLED
|
||||
#define I2C_SDA 22
|
||||
#define I2C_SCL 32
|
||||
|
||||
// NO GPS
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
|
||||
#define RF95_SCK 18
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 23
|
||||
#define RF95_NSS 5
|
||||
#define RF95_FAN_EN 17
|
||||
|
||||
#define LED_PIN 16 // This is a LED_WS2812 not a standard LED
|
||||
|
||||
#define BUTTON_PIN 25
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#undef EXT_NOTIFY_OUT
|
||||
|
||||
// SX128X 2.4 Ghz LoRa module
|
||||
#define USE_SX1280
|
||||
#define LORA_RESET 14
|
||||
#define SX128X_CS 5
|
||||
#define SX128X_DIO1 4
|
||||
#define SX128X_BUSY 21
|
||||
#define SX128X_TXEN 26
|
||||
#define SX128X_RXEN 27
|
||||
#define SX128X_RESET LORA_RESET
|
||||
#define SX128X_MAX_POWER 13
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
// GPS
|
||||
#undef GPS_RX_PIN
|
||||
#define GPS_RX_PIN 15
|
||||
//#undef GPS_TX_PIN
|
||||
//#define GPS_TX_PIN 12 // not connected
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 12
|
||||
#define GPS_TX_PIN 15
|
||||
#define GPS_UBLOX
|
||||
|
||||
#define BUTTON_PIN 39 // The middle button GPIO on the T-Beam
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#define LED_PIN LED
|
||||
|
||||
#define HAS_SCREEN 0
|
||||
#define HAS_GPS 0
|
||||
|
||||
#define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
extends = nrf52840_base
|
||||
board = wiscore_rak4631
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631 -D RAK_4631
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> +<mesh/eth/> +<mqtt/>
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> +<mesh/eth/> +<mesh/api/> +<mqtt/>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 0
|
||||
build = 11
|
||||
build = 13
|
||||
|
||||
Reference in New Issue
Block a user