diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 46f946530..9ee2c37b5 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -8,7 +8,7 @@ build_flags = ${arduino_base.build_flags} -Wno-unused-variable -Isrc/platform/nrf52 build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - lib_ignore = BluetoothOTA diff --git a/arch/rp2040/rp2040.ini b/arch/rp2040/rp2040.ini index cf898a60f..f30a94d3d 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2040/rp2040.ini @@ -12,7 +12,7 @@ build_flags = -D__PLAT_RP2040__ # -D _POSIX_THREADS build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - lib_ignore = BluetoothOTA lib_deps = diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini index 3fc7583ad..a38fb65e8 100644 --- a/arch/stm32/stm32wl5e.ini +++ b/arch/stm32/stm32wl5e.ini @@ -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} - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - lib_deps = ${env.lib_deps} https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b diff --git a/protobufs b/protobufs index 6048ecd2f..e00b5ba7d 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 6048ecd2f8433005bb73ab4505386a7bbdb78c86 +Subproject commit e00b5ba7d06053d820f1123351881fc4fa9270d1 diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 73d5d92b3..2541aa8b5 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -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()); diff --git a/src/FSCommon.h b/src/FSCommon.h index e304a455b..f4c1372a1 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -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 diff --git a/src/Observer.h b/src/Observer.h index ccc7f37b6..4c09101fb 100644 --- a/src/Observer.h +++ b/src/Observer.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include template class Observable; diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index e7f305e19..74b44dd86 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -3,7 +3,6 @@ #include "RTC.h" #include "NodeDB.h" #include "concurrency/OSThread.h" -// #include "wifi/WiFiServerAPI.h" #include #include #include @@ -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"); +} diff --git a/src/RedirectablePrint.h b/src/RedirectablePrint.h index 8fabd4a72..7aaeb4a47 100644 --- a/src/RedirectablePrint.h +++ b/src/RedirectablePrint.h @@ -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 diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 0b6b9ee24..b66eae455 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -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() { diff --git a/src/SerialConsole.h b/src/SerialConsole.h index d1f2abac8..f5e2077ba 100644 --- a/src/SerialConsole.h +++ b/src/SerialConsole.h @@ -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 diff --git a/src/airtime.cpp b/src/airtime.cpp index c72a21d54..0c3fe81fc 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -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) { diff --git a/src/airtime.h b/src/airtime.h index 3f38f39f8..e454b35ad 100644 --- a/src/airtime.h +++ b/src/airtime.h @@ -4,6 +4,7 @@ #include "configuration.h" #include #include +#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 diff --git a/src/concurrency/NotifiedWorkerThread.cpp b/src/concurrency/NotifiedWorkerThread.cpp index ac6428f07..ccdc8fa2b 100644 --- a/src/concurrency/NotifiedWorkerThread.cpp +++ b/src/concurrency/NotifiedWorkerThread.cpp @@ -1,7 +1,6 @@ #include "configuration.h" #include "NotifiedWorkerThread.h" #include "main.h" -#include namespace concurrency { diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 7eec13228..e5791223d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -3,7 +3,6 @@ #include "RTC.h" #include "configuration.h" #include "sleep.h" -#include // If we have a serial GPS port it will not be null #ifdef GPS_SERIAL_NUM diff --git a/src/main.cpp b/src/main.cpp index af7f28b50..2631e68df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 diff --git a/src/memtest.cpp b/src/memtest.cpp deleted file mode 100644 index 20a4bfa53..000000000 --- a/src/memtest.cpp +++ /dev/null @@ -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 - -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++; -} diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 97f2c1448..337fa66e0 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -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]; } } diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp index 0d2246428..d8f84c487 100644 --- a/src/mesh/InterfacesTemplates.cpp +++ b/src/mesh/InterfacesTemplates.cpp @@ -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; template class SX126xInterface; template class SX126xInterface; template class SX128xInterface; + +#if HAS_ETHERNET +#include "api/ethServerAPI.h" +template class ServerAPI; +template class APIServerPort; +#endif + +#if HAS_WIFI +#include "api/WiFiServerAPI.h" +template class ServerAPI; +template class APIServerPort; +#endif \ No newline at end of file diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 51af89df8..9af381a0e 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -1,5 +1,6 @@ #include "configuration.h" #include "MeshPacketQueue.h" +#include #include diff --git a/src/mesh/MeshPacketQueue.h b/src/mesh/MeshPacketQueue.h index 8c93b452e..51dc34cb5 100644 --- a/src/mesh/MeshPacketQueue.h +++ b/src/mesh/MeshPacketQueue.h @@ -2,7 +2,6 @@ #include "MeshTypes.h" -#include #include @@ -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(); diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index c300104a4..9a6fa984c 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -3,7 +3,6 @@ #include #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 staticQueueStatusPool; + +Allocator &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(); -} \ No newline at end of file +} diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index c3b3c95d9..31b63bd3f 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -14,6 +14,8 @@ #include "../platform/portduino/SimRadio.h" #endif +extern Allocator &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 toPhoneQueue; + // keep list of QueueStatus packets to be send to the phone + PointerQueue 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; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index e995e0b22..c19beadca 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1,5 +1,4 @@ #include "configuration.h" -#include #include "Channels.h" #include "CryptoEngine.h" diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 897ddfe8a..3f74e672d 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -6,7 +6,6 @@ #include "PowerFSM.h" #include "RadioInterface.h" #include "configuration.h" -#include #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; diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index cbac5f688..aa190ae37 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -42,6 +42,9 @@ class PhoneAPI : public Observer // 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 // 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 // FIXME, we shouldn't be inheriting private: void releasePhonePacket(); + void releaseQueueStatusPhonePacket(); + /// begin a new connection void handleStartConfig(); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7c0f238a0..2e44b0ada 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -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 diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index f50c0ae77..59c63add2 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -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; } diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 53eaeca60..eb17dbe37 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -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); diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 16495c2f4..628ea863f 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -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 */ diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 0a8497463..74487588b 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -39,6 +39,8 @@ static MemoryDynamic staticPool; Allocator &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); diff --git a/src/mesh/Router.h b/src/mesh/Router.h index f7748bb2b..5a9cc0702 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -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(); diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 15b808852..0e033eeb6 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -70,7 +70,7 @@ bool SX126xInterface::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 diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index cc6d299c8..0fa77575f 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -25,7 +25,12 @@ bool SX128xInterface::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); diff --git a/src/mesh/SinglePortModule.h b/src/mesh/SinglePortModule.h index 57db73221..2e587cb89 100644 --- a/src/mesh/SinglePortModule.h +++ b/src/mesh/SinglePortModule.h @@ -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 */ diff --git a/src/mesh/api/ServerAPI.cpp b/src/mesh/api/ServerAPI.cpp new file mode 100644 index 000000000..e35455063 --- /dev/null +++ b/src/mesh/api/ServerAPI.cpp @@ -0,0 +1,67 @@ +#include "ServerAPI.h" +#include "configuration.h" +#include + +template +ServerAPI::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client) +{ + LOG_INFO("Incoming wifi connection\n"); +} + +template +ServerAPI::~ServerAPI() +{ + client.stop(); +} + +template +void ServerAPI::close() +{ + client.stop(); // drop tcp connection + StreamAPI::close(); +} + +/// Check the current underlying physical link to see if the client is currently connected +template +bool ServerAPI::checkIsConnected() +{ + return client.connected(); +} + +template +int32_t ServerAPI::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 +APIServerPort::APIServerPort(int port) : U(port), concurrency::OSThread("ApiServer") {} + +template +void APIServerPort::init() +{ + U::begin(); +} + +template +int32_t APIServerPort::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 +} diff --git a/src/mesh/wifi/WiFiServerAPI.h b/src/mesh/api/ServerAPI.h similarity index 72% rename from src/mesh/wifi/WiFiServerAPI.h rename to src/mesh/api/ServerAPI.h index 812408818..174aa774f 100644 --- a/src/mesh/wifi/WiFiServerAPI.h +++ b/src/mesh/api/ServerAPI.h @@ -1,21 +1,21 @@ #pragma once #include "StreamAPI.h" -#include /** * 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 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 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); diff --git a/src/mesh/api/WiFiServerAPI.cpp b/src/mesh/api/WiFiServerAPI.cpp new file mode 100644 index 000000000..5f86dbe85 --- /dev/null +++ b/src/mesh/api/WiFiServerAPI.cpp @@ -0,0 +1,25 @@ +#include "configuration.h" +#include + +#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 diff --git a/src/mesh/api/WiFiServerAPI.h b/src/mesh/api/WiFiServerAPI.h new file mode 100644 index 000000000..279ee0f3f --- /dev/null +++ b/src/mesh/api/WiFiServerAPI.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ServerAPI.h" +#include + +/** + * 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 +{ + public: + explicit WiFiServerAPI(WiFiClient &_client); +}; + +/** + * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed + */ +class WiFiServerPort : public APIServerPort +{ + public: + explicit WiFiServerPort(int port); +}; + +void initApiServer(int port=4403); diff --git a/src/mesh/api/ethServerAPI.cpp b/src/mesh/api/ethServerAPI.cpp new file mode 100644 index 000000000..3badcdfde --- /dev/null +++ b/src/mesh/api/ethServerAPI.cpp @@ -0,0 +1,27 @@ +#include "configuration.h" +#include + +#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 \ No newline at end of file diff --git a/src/mesh/api/ethServerAPI.h b/src/mesh/api/ethServerAPI.h new file mode 100644 index 000000000..36f363f70 --- /dev/null +++ b/src/mesh/api/ethServerAPI.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ServerAPI.h" +#include + +/** + * 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 +{ + public: + explicit ethServerAPI(EthernetClient &_client); +}; + +/** + * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed + */ +class ethServerPort : public APIServerPort +{ + public: + explicit ethServerPort(int port); +}; + +void initApiServer(int port=4403); diff --git a/src/mesh/compression/unishox2.c b/src/mesh/compression/unishox2.c index e1f32ae05..2d6c60d75 100644 --- a/src/mesh/compression/unishox2.c +++ b/src/mesh/compression/unishox2.c @@ -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); } diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index 29f55ebee..dce04b191 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -5,7 +5,7 @@ #include #include #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"); diff --git a/src/mesh/eth/ethServerAPI.cpp b/src/mesh/eth/ethServerAPI.cpp deleted file mode 100644 index d91b798a0..000000000 --- a/src/mesh/eth/ethServerAPI.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "ethServerAPI.h" -#include "configuration.h" -#include - -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 -} diff --git a/src/mesh/eth/ethServerAPI.h b/src/mesh/eth/ethServerAPI.h deleted file mode 100644 index 962841c80..000000000 --- a/src/mesh/eth/ethServerAPI.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "StreamAPI.h" -#include - -/** - * 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); diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index d2b9dfcae..1e4aa633c 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -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" */ diff --git a/src/mesh/generated/mesh.pb.c b/src/mesh/generated/mesh.pb.c index bd749b6cb..29594c612 100644 --- a/src/mesh/generated/mesh.pb.c +++ b/src/mesh/generated/mesh.pb.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) diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 31e7817a0..984ae33ee 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -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" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 52b551590..bd4e90832 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -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" */ diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index ec34f9927..733e4c1f1 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -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 @@ -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, diff --git a/src/mesh/wifi/WiFiServerAPI.cpp b/src/mesh/wifi/WiFiServerAPI.cpp deleted file mode 100644 index 136dfadb7..000000000 --- a/src/mesh/wifi/WiFiServerAPI.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "WiFiServerAPI.h" -#include "configuration.h" -#include - -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 -} diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index c2f4f1b13..f1cbc576d 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -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. } diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index e0014f98c..a8f112c94 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -347,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. } diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index d8047c5f0..5dc05cd95 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -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; -} \ No newline at end of file +} diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 6680eaba5..4d119043c 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -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"); } } diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index c02b7219b..5d02e3ba8 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -7,8 +7,6 @@ #include "configuration.h" #include -#include - /* SerialModule A simple interface to send messages over the mesh network by sending strings diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 109d8b5b1..5f96661e5 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -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) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 3bf303157..2a29405d3 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -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()) { diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index b38644340..bdef12e08 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -8,8 +8,6 @@ #include "Router.h" #include "FSCommon.h" -#include - #ifdef OLED_RU #include "graphics/fonts/OLEDDisplayFontsRU.h" #endif diff --git a/src/modules/esp32/RangeTestModule.cpp b/src/modules/esp32/RangeTestModule.cpp index 398af9071..52a18def5 100644 --- a/src/modules/esp32/RangeTestModule.cpp +++ b/src/modules/esp32/RangeTestModule.cpp @@ -9,7 +9,6 @@ #include "gps/GeoCoord.h" #include #include -//#include /* 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); diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index ecb2ab978..b5e0b1e65 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -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. } diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 49ad586dd..3a5d3b364 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -39,9 +39,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 +62,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 +92,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"); } @@ -183,14 +203,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 +272,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 20; + } else { + return 30000; + } } else return 5000; // If we don't want connection now, check again in 5 secs } else { diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 33fbbb8eb..e3fb2b626 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -13,6 +13,8 @@ #include #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. diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 93eb76dbd..f8cb04704 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -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); + } } } diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 9a942a606..e3096b0ce 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -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 diff --git a/src/platform/portduino/CrossPlatformCryptoEngine.cpp b/src/platform/portduino/CrossPlatformCryptoEngine.cpp index 7bd021a42..4c6d5f178 100644 --- a/src/platform/portduino/CrossPlatformCryptoEngine.cpp +++ b/src/platform/portduino/CrossPlatformCryptoEngine.cpp @@ -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); + } } } diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index 87800de22..ce96d02cc 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -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 diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index a71cf22f8..c4336a22b 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -2,7 +2,7 @@ #include "RadioInterface.h" #include "MeshPacketQueue.h" -#include "wifi/WiFiServerAPI.h" +#include "api/WiFiServerAPI.h" #include @@ -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; diff --git a/src/sleep.cpp b/src/sleep.cpp index c85eec3ff..5eb62a1c5 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -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); diff --git a/variants/betafpv_2400_tx_micro/platformio.ini b/variants/betafpv_2400_tx_micro/platformio.ini new file mode 100644 index 000000000..e04f4efc2 --- /dev/null +++ b/variants/betafpv_2400_tx_micro/platformio.ini @@ -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 diff --git a/variants/betafpv_2400_tx_micro/variant.h b/variants/betafpv_2400_tx_micro/variant.h new file mode 100644 index 000000000..d4af5dac4 --- /dev/null +++ b/variants/betafpv_2400_tx_micro/variant.h @@ -0,0 +1,34 @@ +//https://betafpv.com/products/elrs-micro-tx-module +#include + +// 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 diff --git a/variants/diy/v1/variant.h b/variants/diy/v1/variant.h index 3c0878cd1..456daa708 100644 --- a/variants/diy/v1/variant.h +++ b/variants/diy/v1/variant.h @@ -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 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index e08b54dae..a0928605f 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -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> + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + + lib_deps = ${nrf52840_base.lib_deps} ${networking_base.lib_deps}