diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 43ba66477..d69aa43eb 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,6 +4,11 @@ You probably don't care about this section - skip to the next one. ## 1.2 cleanup & multichannel support: +nastybug +try again on esp32 +emittx thinks it emitted but client sees nothing. works again later +segger logs have errors in formatting that should be impossible (because not going through serial, try stalling on segger) + * DONE call RouterPlugin for *all* packets - not just Router packets * DONE generate channel hash from the name of the channel+the psk (not just one or the other) * DONE send a hint that can be used to select which channel to try and hash against with each message @@ -42,7 +47,8 @@ You probably don't care about this section - skip to the next one. * DONE add gui in android app for setting region * DONE clean up python channel usage * DONE use bindToChannel to limit admin access for remote nodes -* move channels and radio config out of device settings +* DONE move channels and radio config out of device settings +* test remote info and remote settings changes * make python tests more exhaustive * pick default random admin key * exclude admin channels from URL? diff --git a/platformio.ini b/platformio.ini index 742bc6db6..286976311 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,13 +45,6 @@ build_flags = -Wno-missing-field-initializers -DUSE_THREAD_NAMES -DTINYGPS_OPTION_NO_CUSTOM_FIELDS -; leave this commented out to avoid breaking Windows -;upload_port = /dev/ttyUSB0 -;monitor_port = /dev/ttyUSB0 - -;upload_port = /dev/cu.SLAB_USBtoUART -;monitor_port = /dev/cu.SLAB_USBtoUART - ; the default is esptool ; upload_protocol = esp-prog @@ -74,7 +67,7 @@ lib_deps = https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306 https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce 1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib - https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39 + https://github.com/meshtastic/arduino-fsm.git#55c47b6cded91645aff05a27b6e5821d8d0f64be https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79 @@ -118,6 +111,13 @@ lib_ignore = segger_rtt platform_packages = framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56 +; leave this commented out to avoid breaking Windows +upload_port = /dev/ttyUSB0 +;monitor_port = /dev/ttyUSB0 + +;upload_port = /dev/cu.SLAB_USBtoUART +;monitor_port = /dev/cu.SLAB_USBtoUART + ; customize the partition table ; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables board_build.partitions = partition-table.csv @@ -206,7 +206,8 @@ debug_port = :2331 # Note: the ARGUMENTS MUST BE on multiple lines. Otherwise platformio/commands/debug/helpers.py misparses everything into the "executable" # attribute and leaves "arguments" empty # /home/kevinh/.platformio/packages/tool-jlink/JLinkGDBServerCLExe -debug_server = +# This doesn't work yet, so not using for now +disabled_debug_server = /usr/bin/JLinkGDBServerCLExe -singlerun -if diff --git a/src/memtest.cpp b/src/memtest.cpp new file mode 100644 index 000000000..939bbc02b --- /dev/null +++ b/src/memtest.cpp @@ -0,0 +1,318 @@ +/* + * 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, uint32_t _end, bool doRead = true, bool doWrite = true) +{ + volatile uint32_t *addr; + volatile uint32_t *start = (volatile uint32_t *)_start; + volatile uint32_t *end = (volatile uint32_t *)_end; + uint32_t pattern = 0; + uint32_t val; + uint32_t readback; + uint32_t incr; + int rcode = 0; + incr = 1; + + //DEBUG_MSG("memtest read=%d, write=%d\n", doRead, doWrite); + + if (doWrite) { + //DEBUG_MSG("writing\n"); + for (addr = start, val = pattern; addr < end; addr++) { + *addr = val; + val += incr; + } + } + + if (doRead) { + //DEBUG_MSG("reading\n"); + for (addr = start, val = pattern; addr < end; addr++) { + readback = *addr; + if (readback != val) { + DEBUG_MSG("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((uint32_t)testBuf, ((uint32_t)testBuf) + TESTBUF_LEN, iter % 2 == 1, iter % 2 == 0) > 0) + assert(0); // FIXME report error better + + iter++; +} \ No newline at end of file diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 68a63116e..361cad26b 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "MeshService.h" #include "NodeDB.h" +#include "plugins/RoutingPlugin.h" #include std::vector *MeshPlugin::plugins; @@ -52,7 +53,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) assert(ch.has_settings); /// Is the channel this packet arrived on acceptable? (security check) - bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0); + bool rxChannelOk = true || !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0); /// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious) bool wantsPacket = rxChannelOk && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp); @@ -84,10 +85,17 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) pi.currentRequest = NULL; } - if (currentReply) { - DEBUG_MSG("Sending response\n"); - service.sendToMesh(currentReply); - currentReply = NULL; + if (mp.decoded.want_response && toUs) { + if (currentReply) { + DEBUG_MSG("Sending response\n"); + service.sendToMesh(currentReply); + currentReply = NULL; + } + else { + // No one wanted to reply to this requst, tell the requster that happened + DEBUG_MSG("No one responded, send a nak\n"); + routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel); + } } if (!pluginFound) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 49dc69927..0ee235292 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -153,6 +153,9 @@ void printPacket(const char *prefix, const MeshPacket *p) if (s.dest != 0) DEBUG_MSG(" dest=%08x", s.dest); + if(s.request_id) + DEBUG_MSG(" requestId=%0x", s.request_id); + /* now inside Data and therefore kinda opaque if (s.which_ackVariant == SubPacket_success_id_tag) DEBUG_MSG(" successId=%08x", s.ackVariant.success_id); diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 962aabeb7..4d4845949 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -38,7 +38,7 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p) DEBUG_MSG("generating implicit ack\n"); // NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be // marked as wantAck - sendAckNak(Routing_Error_NONE, getFrom(p), p->id); + sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel); } } @@ -65,9 +65,9 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c) // - not DSR routing) if (p->want_ack) { if (MeshPlugin::currentReply) - DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack"); + DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack\n"); else - sendAckNak(Routing_Error_NONE, getFrom(p), p->id); + sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel); } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error @@ -166,7 +166,7 @@ int32_t ReliableRouter::doRetransmissions() if (p.numRetransmissions == 0) { DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to, p.packet->id); - sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id); + sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel); // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which // allows the DSR version to still be able to look at the PendingPacket stopRetransmission(it->first); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 8f566dffe..52b28eb55 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -103,15 +103,15 @@ MeshPacket *Router::allocForSending() /** * Send an ack or a nak packet back towards whoever sent idFrom */ -void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom) +void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) { - routingPlugin->sendAckNak(err, to, idFrom); + routingPlugin->sendAckNak(err, to, idFrom, chIndex); } void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) { DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err); - sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id); + sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel); packetPool.release(p); } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 6e217017b..3600af361 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -6,6 +6,7 @@ #include "PointerQueue.h" #include "RadioInterface.h" #include "concurrency/OSThread.h" +#include "Channels.h" /** * A mesh aware router that supports multiple interfaces. @@ -106,7 +107,7 @@ class Router : protected concurrency::OSThread /** * Send an ack or a nak packet back towards whoever sent idFrom */ - void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom); + void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); private: /** diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index b7df1079e..10487e40d 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -37,7 +37,8 @@ typedef enum _Routing_Error { Routing_Error_NO_INTERFACE = 4, Routing_Error_MAX_RETRANSMIT = 5, Routing_Error_NO_CHANNEL = 6, - Routing_Error_TOO_LARGE = 7 + Routing_Error_TOO_LARGE = 7, + Routing_Error_NO_RESPONSE = 8 } Routing_Error; typedef enum _MeshPacket_Priority { @@ -182,8 +183,8 @@ typedef struct _ToRadio { #define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1)) #define _Routing_Error_MIN Routing_Error_NONE -#define _Routing_Error_MAX Routing_Error_TOO_LARGE -#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_TOO_LARGE+1)) +#define _Routing_Error_MAX Routing_Error_NO_RESPONSE +#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1)) #define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET #define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index 875b06236..060b1e635 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -66,6 +66,8 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag break; default: + // Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages + DEBUG_MSG("Ignoring nonrelevant admin %d\n", r->which_variant); break; } return false; // Let others look at this message also if they want diff --git a/src/plugins/RoutingPlugin.cpp b/src/plugins/RoutingPlugin.cpp index f6c2ea03a..4fe0b88d5 100644 --- a/src/plugins/RoutingPlugin.cpp +++ b/src/plugins/RoutingPlugin.cpp @@ -35,7 +35,7 @@ MeshPacket *RoutingPlugin::allocReply() return NULL; } -void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom) +void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) { Routing c = Routing_init_default; @@ -47,6 +47,7 @@ void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom) p->hop_limit = 0; // Assume just immediate neighbors for now p->to = to; p->decoded.request_id = idFrom; + p->channel = chIndex; DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); router->sendLocal(p); // we sometimes send directly to the local node diff --git a/src/plugins/RoutingPlugin.h b/src/plugins/RoutingPlugin.h index a848bc9c4..ff659de2a 100644 --- a/src/plugins/RoutingPlugin.h +++ b/src/plugins/RoutingPlugin.h @@ -1,5 +1,6 @@ #pragma once #include "ProtobufPlugin.h" +#include "Channels.h" /** * Routing plugin for router control messages @@ -12,6 +13,8 @@ class RoutingPlugin : public ProtobufPlugin */ RoutingPlugin(); + void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + protected: friend class Router; @@ -27,8 +30,6 @@ class RoutingPlugin : public ProtobufPlugin /// Override wantPacket to say we want to see all packets, not just those for our port number virtual bool wantPacket(const MeshPacket *p) { return true; } - - void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom); }; extern RoutingPlugin *routingPlugin; \ No newline at end of file