diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f0d994df..5f17d9176 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,5 +22,15 @@ jobs: - name: Install extra python tools run: | pip install -U adafruit-nrfutil - - name: Build - run: platformio run -e tbeam -e heltec -e lora-relay-v1 -e linux + - name: Install libs needed for linux build + run: | + sudo apt install -y libpsocksxx-dev + - name: Build for tbeam + run: platformio run -e tbeam + - name: Build for heltec + run: platformio run -e heltec + - name: Build for lora-relay-v1 + run: platformio run -e lora-relay-v1 + - name: Build for linux + run: platformio run -e linux + diff --git a/README.md b/README.md index bd9143565..5c33d0f45 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is the device side code for the [meshtastic.org](https://www.meshtastic.org ![Continuous Integration](https://github.com/meshtastic/Meshtastic-esp32/workflows/Continuous%20Integration/badge.svg) -Meshtastic™ is a project that lets you use +Meshtastic® is a project that lets you use inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat. diff --git a/bin/build-all.sh b/bin/build-all.sh index caa7c59af..ecb96e7a1 100755 --- a/bin/build-all.sh +++ b/bin/build-all.sh @@ -14,8 +14,6 @@ BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7" # FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine BOARDS_NRF52="lora-relay-v1" -NUM_JOBS=2 || true - OUTDIR=release/latest # We keep all old builds (and their map files in the archive dir) @@ -51,7 +49,7 @@ function do_build() { basename=universal/firmware-$BOARD-$VERSION fi - pio run --jobs $NUM_JOBS --environment $BOARD # -v + pio run --environment $BOARD # -v SRCELF=.pio/build/$BOARD/firmware.elf cp $SRCELF $OUTDIR/elfs/$basename.elf diff --git a/docs/README.md b/docs/README.md index 2018f387b..982bb4f67 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,13 +1,13 @@ # What is Meshtastic? -Meshtastic™ is a project that lets you use +Meshtastic® is a project that lets you use inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat. The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required. Note: Questions after reading this? See our new [forum](https://meshtastic.discourse.group/). -### Uses +## Uses - Outdoor sports where cellular coverage is limited. (Hiking, Skiing, Boating, Paragliding, Gliders etc..) - Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...) @@ -17,7 +17,7 @@ Note: Questions after reading this? See our new [forum](https://meshtastic.disco [![Youtube video demo](desk-video-screenshot.png)](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo") -### Features +## Features Not all of these features are fully implemented yet - see **important** disclaimers below. But they should be in by the time we decide to call this project beta (three months?) diff --git a/proto b/proto index c9ad10d7e..106f4bfde 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit c9ad10d7e2f7f465dc477e98d6ec9d3b7059336c +Subproject commit 106f4bfdebe277ab0b86d2b8c950ab78a35b0654 diff --git a/src/main.cpp b/src/main.cpp index aa7b191d9..bb3ec4820 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -490,6 +490,8 @@ void setup() DEBUG_MSG("Warning: Failed to find RF95 radio\n"); delete rIf; rIf = NULL; + } else { + DEBUG_MSG("Radio init succeeded, using RF95 radio\n"); } } #endif @@ -501,6 +503,8 @@ void setup() DEBUG_MSG("Warning: Failed to find SX1262 radio\n"); delete rIf; rIf = NULL; + } else { + DEBUG_MSG("Radio init succeeded, using SX1262 radio\n"); } } #endif @@ -512,6 +516,8 @@ void setup() DEBUG_MSG("Warning: Failed to find simulated radio\n"); delete rIf; rIf = NULL; + } else { + DEBUG_MSG("Using SIMULATED radio!\n"); } } #endif diff --git a/src/mesh/DSRRouter.cpp b/src/mesh/DSRRouter.cpp index d8ca542d7..ad41afdde 100644 --- a/src/mesh/DSRRouter.cpp +++ b/src/mesh/DSRRouter.cpp @@ -105,7 +105,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p) // packets until ack arrives) // FIXME, if we don't get a route reply at all (or a route error), timeout and generate a routeerror TIMEOUT on our own... break; - case SubPacket_route_error_tag: + case SubPacket_error_reason_tag: removeRoute(p->decoded.dest); // FIXME: if any pending packets were waiting on this route, delete them @@ -131,7 +131,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p) assert(p->decoded.source); // I think this is guaranteed by now // FIXME - what if the current packet _is_ a route error packet? - sendRouteError(p, RouteError_NO_ROUTE); + sendRouteError(p, ErrorReason_NO_ROUTE); } // FIXME, stop local processing of this packet @@ -145,7 +145,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p) if (pending && pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node - sendRouteError(p, RouteError_GOT_NAK); + sendRouteError(p, ErrorReason_GOT_NAK); } } } @@ -230,7 +230,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p) /** * Send a route error packet towards whoever originally sent this message */ -void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err) +void DSRRouter::sendRouteError(const MeshPacket *p, ErrorReason err) { DEBUG_MSG("FIXME not implemented sendRouteError\n"); } diff --git a/src/mesh/DSRRouter.h b/src/mesh/DSRRouter.h index 3bc461571..02c0f2775 100644 --- a/src/mesh/DSRRouter.h +++ b/src/mesh/DSRRouter.h @@ -70,7 +70,7 @@ class DSRRouter : public ReliableRouter /** * Send a route error packet towards whoever originally sent this message */ - void sendRouteError(const MeshPacket *p, RouteError err); + void sendRouteError(const MeshPacket *p, ErrorReason err); /** make a copy of p, start discovery, but only if we don't * already a discovery in progress for that node number. Caller has already scheduled this message for retransmission diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 93402c127..6060b1f03 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -34,7 +34,7 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p) // the original sending process. if (stopRetransmission(p->from, p->id)) { DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n"); - sendAckNak(true, p->from, p->id); + sendAckNak(ErrorReason_NONE, p->from, p->id); } } @@ -60,7 +60,7 @@ void ReliableRouter::sniffReceived(const MeshPacket *p) if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability // - not DSR routing) if (p->want_ack) { - sendAckNak(true, p->from, p->id); + sendAckNak(ErrorReason_NONE, p->from, p->id); } // If the payload is valid, look for ack/nak @@ -84,27 +84,6 @@ void ReliableRouter::sniffReceived(const MeshPacket *p) FloodingRouter::sniffReceived(p); } -/** - * Send an ack or a nak packet back towards whoever sent idFrom - */ -void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom) -{ - auto p = allocForSending(); - p->hop_limit = 0; // Assume just immediate neighbors for now - p->to = to; - DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=0x%x,id=0x%x\n", isAck, to, idFrom, p->id); - - if (isAck) { - p->decoded.ack.success_id = idFrom; - p->decoded.which_ack = SubPacket_success_id_tag; - } else { - p->decoded.ack.fail_id = idFrom; - p->decoded.which_ack = SubPacket_fail_id_tag; - } - - sendLocal(p); // we sometimes send directly to the local node -} - #define NUM_RETRANSMISSIONS 3 PendingPacket::PendingPacket(MeshPacket *p) @@ -176,7 +155,7 @@ int32_t ReliableRouter::doRetransmissions() if (p.numRetransmissions == 0) { DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to, p.packet->id); - sendAckNak(false, p.packet->from, p.packet->id); + sendAckNak(ErrorReason_MAX_RETRANSMIT, p.packet->from, p.packet->id); // 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/ReliableRouter.h b/src/mesh/ReliableRouter.h index 91dd248a8..62d555389 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -109,10 +109,6 @@ class ReliableRouter : public FloodingRouter PendingPacket *startRetransmission(MeshPacket *p); private: - /** - * Send an ack or a nak packet back towards whoever sent idFrom - */ - void sendAckNak(bool isAck, NodeNum to, PacketId idFrom); /** * Stop any retransmissions we are doing of the specified node/packet ID pair diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 458a338fe..c3a345522 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -99,6 +99,31 @@ MeshPacket *Router::allocForSending() return p; } +/** + * Send an ack or a nak packet back towards whoever sent idFrom + */ +void Router::sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom) +{ + auto p = allocForSending(); + p->hop_limit = 0; // Assume just immediate neighbors for now + p->to = to; + DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + + if (!err) { + p->decoded.ack.success_id = idFrom; + p->decoded.which_ack = SubPacket_success_id_tag; + } else { + p->decoded.ack.fail_id = idFrom; + p->decoded.which_ack = SubPacket_fail_id_tag; + + // Also send back the error reason + p->decoded.which_payload = SubPacket_error_reason_tag; + p->decoded.error_reason = err; + } + + sendLocal(p); // we sometimes send directly to the local node +} + ErrorCode Router::sendLocal(MeshPacket *p) { // No need to deliver externally if the destination is the local node @@ -106,15 +131,24 @@ ErrorCode Router::sendLocal(MeshPacket *p) printPacket("Enqueuing local", p); fromRadioQueue.enqueue(p); return ERRNO_OK; - } + } else if (!iface) { + // We must be sending to remote nodes also, fail if no interface found - // If we are sending a broadcast, we also treat it as if we just received it ourself - // this allows local apps (and PCs) to see broadcasts sourced locally - if (p->to == NODENUM_BROADCAST) { - handleReceived(p); - } + // ERROR! no radio found, report failure back to the client and drop the packet + DEBUG_MSG("Error: No interface, returning NAK and dropping packet.\n"); + sendAckNak(ErrorReason_NO_INTERFACE, p->from, p->id); + packetPool.release(p); - return send(p); + return ERRNO_NO_INTERFACES; + } else { + // If we are sending a broadcast, we also treat it as if we just received it ourself + // this allows local apps (and PCs) to see broadcasts sourced locally + if (p->to == NODENUM_BROADCAST) { + handleReceived(p); + } + + return send(p); + } } /** @@ -154,14 +188,15 @@ ErrorCode Router::send(MeshPacket *p) p->which_payload = MeshPacket_encrypted_tag; } - if (iface) { - // DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); - return iface->send(p); - } else { + assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside) + // if (iface) { + // DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); + return iface->send(p); + /* } else { DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); packetPool.release(p); return ERRNO_NO_INTERFACES; - } + } */ } /** diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 0035f2ac5..dfc44dfa4 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -48,7 +48,8 @@ class Router : protected concurrency::OSThread virtual int32_t runOnce(); /** - * Works like send, but if we are sending to the local node, we directly put the message in the receive queue + * Works like send, but if we are sending to the local node, we directly put the message in the receive queue. + * This is the primary method used for sending packets, because it handles both the remote and local cases. * * NOTE: This method will free the provided packet (even if we return an error code) */ @@ -92,6 +93,11 @@ class Router : protected concurrency::OSThread */ bool perhapsDecode(MeshPacket *p); + /** + * Send an ack or a nak packet back towards whoever sent idFrom + */ + void sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom); + private: /** * Called from loop() diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index a13d7de56..6bf3a1c01 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -11,12 +11,14 @@ #endif /* Enum definitions */ -typedef enum _RouteError { - RouteError_NONE = 0, - RouteError_NO_ROUTE = 1, - RouteError_GOT_NAK = 2, - RouteError_TIMEOUT = 3 -} RouteError; +typedef enum _ErrorReason { + ErrorReason_NONE = 0, + ErrorReason_NO_ROUTE = 1, + ErrorReason_GOT_NAK = 2, + ErrorReason_TIMEOUT = 3, + ErrorReason_NO_INTERFACE = 4, + ErrorReason_MAX_RETRANSMIT = 5 +} ErrorReason; typedef enum _Constants { Constants_Unused = 0, @@ -230,7 +232,7 @@ typedef struct _SubPacket { User user; RouteDiscovery route_request; RouteDiscovery route_reply; - RouteError route_error; + ErrorReason error_reason; }; uint32_t original_id; bool want_response; @@ -288,9 +290,9 @@ typedef struct _ToRadio { /* Helper constants for enums */ -#define _RouteError_MIN RouteError_NONE -#define _RouteError_MAX RouteError_TIMEOUT -#define _RouteError_ARRAYSIZE ((RouteError)(RouteError_TIMEOUT+1)) +#define _ErrorReason_MIN ErrorReason_NONE +#define _ErrorReason_MAX ErrorReason_MAX_RETRANSMIT +#define _ErrorReason_ARRAYSIZE ((ErrorReason)(ErrorReason_MAX_RETRANSMIT+1)) #define _Constants_MIN Constants_Unused #define _Constants_MAX Constants_DATA_PAYLOAD_LEN @@ -450,7 +452,7 @@ extern "C" { #define SubPacket_user_tag 4 #define SubPacket_route_request_tag 6 #define SubPacket_route_reply_tag 7 -#define SubPacket_route_error_tag 13 +#define SubPacket_error_reason_tag 13 #define SubPacket_original_id_tag 2 #define SubPacket_want_response_tag 5 #define SubPacket_dest_tag 9 @@ -517,7 +519,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \ X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \ X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \ -X(a, STATIC, ONEOF, UENUM, (payload,route_error,route_error), 13) \ +X(a, STATIC, ONEOF, UENUM, (payload,error_reason,error_reason), 13) \ X(a, STATIC, SINGULAR, UINT32, original_id, 2) \ X(a, STATIC, SINGULAR, BOOL, want_response, 5) \ X(a, STATIC, SINGULAR, UINT32, dest, 9) \ diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index 99df1222d..15572ca17 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -19,7 +19,7 @@ typedef enum _PortNum { PortNum_REPLY_APP = 32, PortNum_IP_TUNNEL_APP = 33, PortNum_SERIAL_APP = 64, - PortNum_STORE_REQUEST_APP = 65, + PortNum_STORE_FORWARD_APP = 65, PortNum_PRIVATE_APP = 256, PortNum_ATAK_FORWARDER = 257 } PortNum; diff --git a/version.properties b/version.properties index dd592ccf0..5ff3c45fa 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 1 -build = 34 +build = 42