From da46d4ca0ecbd664d90a59c69d47216af50275d1 Mon Sep 17 00:00:00 2001 From: Dafeman <47490997+Dafeman@users.noreply.github.com> Date: Thu, 6 Aug 2020 19:44:42 +1200 Subject: [PATCH 01/10] Update build-instructions.md --- docs/software/build-instructions.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/software/build-instructions.md b/docs/software/build-instructions.md index 5074fbaf1..5b8d269e3 100644 --- a/docs/software/build-instructions.md +++ b/docs/software/build-instructions.md @@ -1,9 +1,19 @@ # Build instructions -This project uses the simple PlatformIO build system. You can use the IDE, but for brevity -in these instructions I describe use of their command line tool. +This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode. -1. Purchase a suitable radio (see above) +## GUI +1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information). +2. Install [PlatformIO](https://platformio.org/platformio-ide). +3. Click the PlatformIO icon on the side bar. ![platformio icon](https://user-images.githubusercontent.com/47490997/89482668-77c7ea00-d7ee-11ea-8785-5faf8ff99800.png) +4. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). ![image](https://user-images.githubusercontent.com/47490997/89483047-4c91ca80-d7ef-11ea-91f4-1d53d4e8acd9.png) +5. Select a file location to save the repo. +6. Once loaded, open the `platformio.ini` file. +7. At the line `default_envs` you can change it to the board type you are building for ie. `ttgo-lora32-v2, ttgo-lora32-v1, tbeam, heltec` (boards are listed further down in the file). +8. Click the PlatformIO icon on the side bar. Under `Project Tasks` you can now build or upload. + +## Command Line +1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information). 2. Install [PlatformIO](https://platformio.org/platformio-ide) 3. Download this git repo and cd into it 4. Run `git submodule update --init --recursive` to pull in dependencies this project needs. From 8b1fb39ce167c160fad2f101f87a54150ae6350a Mon Sep 17 00:00:00 2001 From: Dafeman <47490997+Dafeman@users.noreply.github.com> Date: Thu, 6 Aug 2020 22:06:38 +1200 Subject: [PATCH 02/10] Update build-instructions.md --- docs/software/build-instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/software/build-instructions.md b/docs/software/build-instructions.md index 5b8d269e3..2989d173f 100644 --- a/docs/software/build-instructions.md +++ b/docs/software/build-instructions.md @@ -9,7 +9,7 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension 4. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). ![image](https://user-images.githubusercontent.com/47490997/89483047-4c91ca80-d7ef-11ea-91f4-1d53d4e8acd9.png) 5. Select a file location to save the repo. 6. Once loaded, open the `platformio.ini` file. -7. At the line `default_envs` you can change it to the board type you are building for ie. `ttgo-lora32-v2, ttgo-lora32-v1, tbeam, heltec` (boards are listed further down in the file). +7. At the line `default_envs` you can change it to the board type you are building for ie. `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7` (boards are listed further down in the file). 8. Click the PlatformIO icon on the side bar. Under `Project Tasks` you can now build or upload. ## Command Line @@ -19,7 +19,7 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension 4. Run `git submodule update --init --recursive` to pull in dependencies this project needs. 5. If you are outside the USA, edit [platformio.ini](/platformio.ini) to set the correct frequency range for your country. The line you need to change starts with `hw_version` and instructions are provided above that line. Options are provided for `EU433`, `EU835`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries. 6. Plug the radio into your USB port -7. Type `pio run --environment XXX -t upload` (This command will fetch dependencies, build the project and install it on the board via USB). For XXX, use the board type you have (either `tbeam`, `heltec`, `ttgo-lora32-v1`, `ttgo-lora32-v2`). +7. Type `pio run --environment XXX -t upload` (This command will fetch dependencies, build the project and install it on the board via USB). For XXX, use the board type you have (either `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7`). 8. Platform IO also installs a very nice VisualStudio Code based IDE, see their [tutorial](https://docs.platformio.org/en/latest/tutorials/espressif32/arduino_debugging_unit_testing.html) if you'd like to use it. ## Decoding stack traces From 05531b26841f7a391d2bbd2e9860a1b21caa4e9b Mon Sep 17 00:00:00 2001 From: Dafeman <47490997+Dafeman@users.noreply.github.com> Date: Sun, 9 Aug 2020 14:07:42 +1200 Subject: [PATCH 03/10] Update build-instructions.md --- docs/software/build-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/software/build-instructions.md b/docs/software/build-instructions.md index 2989d173f..c5962cef1 100644 --- a/docs/software/build-instructions.md +++ b/docs/software/build-instructions.md @@ -15,7 +15,7 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension ## Command Line 1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information). 2. Install [PlatformIO](https://platformio.org/platformio-ide) -3. Download this git repo and cd into it +3. Download this git repo and cd into it. `git clone https://github.com/meshtastic/Meshtastic-device.git` and `cd Meshtastic-device` 4. Run `git submodule update --init --recursive` to pull in dependencies this project needs. 5. If you are outside the USA, edit [platformio.ini](/platformio.ini) to set the correct frequency range for your country. The line you need to change starts with `hw_version` and instructions are provided above that line. Options are provided for `EU433`, `EU835`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries. 6. Plug the radio into your USB port From dec88a368b3592514a4cb9f70245c585ac9cf843 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 11 Aug 2020 17:34:49 -0700 Subject: [PATCH 04/10] First attempt at better protocol docs. Bug #308 @cyclomies thank you for the prodding and help. I'm happy to add more detail, can you insert a few questions inline? Then I'll answer and hopefully that will be enough to be useful for others. --- docs/software/mesh-alg.md | 90 ++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/docs/software/mesh-alg.md b/docs/software/mesh-alg.md index f9427cd21..2020dc534 100644 --- a/docs/software/mesh-alg.md +++ b/docs/software/mesh-alg.md @@ -1,5 +1,75 @@ # Mesh broadcast algorithm +## Current algorithm + +The routing protocol for Meshtastic is really quite simple (and suboptimal). It is heavily influenced by the mesh routing algorithm used in [Radiohead](https://www.airspayce.com/mikem/arduino/RadioHead/) (which was used in very early versions of this project). It has four conceptual layers. + +### A note about protocol buffers + +Because we want our devices to work across various vendors and implementations, we use [Protocol Buffers](https://github.com/meshtastic/Meshtastic-protobufs) pervasively. For information on how the protocol buffers are used wrt API clients see [sw-design](sw-design.md), for purposes of this document you mostly only +need to consider the MeshPacket and Subpacket message types. + +### Layer 1: Non reliable zero hop messaging + +This layer is conventional non-reliable lora packet transmission. The transmitted packet has the following representation on the ether: + +- A 32 bit LORA preamble (to allow receiving radios to synchronize clocks and start framing). We use a longer than minimum (8 bit) preamble to maximize the amount of time the LORA receivers can stay asleep, which dramatically lowers power consumption. + +After the preamble the 16 byte packet header is transmitted. This header is described directly by the PacketHeader class in the C++ source code. But indirectly it matches the first portion of the "MeshPacket" protobuf definition. But notably: this portion of the packet is sent directly as the following 16 bytes (rather than using the protobuf encoding). We do this to both save airtime and to allow receiving radio hardware the option of filtering packets before even waking the main CPU. + +- to (4 bytes): the unique NodeId of the destination (or 0xffffffff for NodeNum_BROADCAST) +- from (4 bytes): the unique NodeId of the sender) +- id (4 bytes): the unique (wrt the sending node only) packet ID number for this packet. We use a large (32 bit) packet ID to ensure there is enough unique state to protect any encrypted payload from attack. +- flags (4 bytes): Only a few bits are are currently used - 3 bits for for the "HopLimit" (see below) and 1 bit for "WantAck" + +After the packet header the actual packet is placed onto the the wire. These bytes are merely the encrypted packed protobuf encoding of the SubPacket protobuf. A full description of our encryption is available in [crypto](crypto.md). It is worth noting that only this SubPacket is encrypted, headers are not. Which leaves open the option of eventually allowing nodes to route packets without knowing the keys used to encrypt. + +NodeIds are constructed from the bottom four bytes of the macaddr of the bluetooth address. Because the OUI is assigned by the IEEE and we currently only support a few CPU manufacturers, the upper byte is defacto guaranteed unique for each vendor. The bottom 3 bytes are guaranteed unique by that vendor. + +To prevent collisions all transmitters will listen before attempting to send. If they hear some other node transmitting, they will reattempt transmission in x milliseconds. This retransmission delay is random between FIXME and FIXME (these two numbers are currently hardwired, but really should be scaled based on expected packet transmission time at current channel settings). + +### Layer 2: Reliable zero hop messaging + +This layer adds reliable messaging between the node and its immediate neighbors (only). + +The default messaging provided by layer-1 is extended by setting the "want-ack" flag in the MeshPacket protobuf. If want-ack is set the following documentation from mesh.proto applies: + +"""This packet is being sent as a reliable message, we would prefer it to arrive +at the destination. We would like to receive a ack packet in response. + +Broadcasts messages treat this flag specially: Since acks for broadcasts would +rapidly flood the channel, the normal ack behavior is suppressed. Instead, +the original sender listens to see if at least one node is rebroadcasting this +packet (because naive flooding algorithm). If it hears that the odds (given +typical LoRa topologies) the odds are very high that every node should +eventually receive the message. So FloodingRouter.cpp generates an implicit +ack which is delivered to the original sender. If after some time we don't +hear anyone rebroadcast our packet, we will timeout and retransmit, using the +regular resend logic.""" + +If a transmitting node does not receive an ACK (or a NAK) packet within FIXME milliseconds, it will use layer-1 to attempt a retransmission of the sent packet. A reliable packet (at this 'zero hop' level) will be resent a maximum of three times. If no ack or nak has been received by then the local node will internally generate a nak (either for local consumption or use by higher layers of the protocol). + +### Layer 3: (Naive) flooding for multi-hop messaging + +Given our use-case for the initial release, most of our protocol is built around [flooding](). The implementation is currently 'naive' - i.e. it doesn't try to optimize flooding other than abandoning retransmission once we've seen a nearby receiver has acked the packet. Therefore, for each source packet up to N retransmissions might occur (if there are N nodes in the mesh). + +Each node in the mesh, if it sees a packet on the ether with HopLimit set to a value other than zero, it will decrement that HopLimit and attempt retransmission on behalf of the original sending node. + +### Layer 4: DSR for multi-hop unicast messaging + +This layer is not yet fully implemented (and not yet used). But eventually (if we stay with our own transport rather than switching to QMesh or Reticulum) +we will use conventional DSR for unicast messaging. Currently (even when not requiring 'broadcasts') we send any multi-hop unicasts as 'broadcasts' so that we can +leverage our (functional) flooding implementation. This is suboptimal but it is a very rare use-case, because the odds are high that most nodes (given our small networks and 'hiking' use case) are within a very small number of hops. When any node witnesses an ack for a packet, it will realize that it can abandon its own +broadcast attempt for that packet. + +## Misc notes on remaining tasks + +This section is currently poorly formatted, it is mostly a mere set of todo lists and notes for @geeksville during his initial development. After release 1.0 ideas for future optimization include: + +- Make flood-routing less naive (because we have GPS and radio signal strength as heuristics to avoid redundant retransmissions) +- If nodes have been user marked as 'routers', preferentially do flooding via those nodes +- Fully implement DSR to improve unicast efficiency (or switch to QMesh/Reticulum as these projects mature) + great source of papers and class notes: http://www.cs.jhu.edu/~cs647/ flood routing improvements @@ -146,23 +216,3 @@ look into the literature for this idea specifically. build the most recent version of reality, and if some nodes are too far, then nodes closer in will eventually forward their changes to the distributed db. - construct non ambigious rules for who broadcasts to request db updates. ideally the algorithm should nicely realize node X can see most other nodes, so they should just listen to all those nodes and minimize the # of broadcasts. the distributed picture of nodes rssi could be useful here? - possibly view the BLE protocol to the radio the same way - just a process of reconverging the node/msgdb database. - -# Old notes - -FIXME, merge into the above: - -good description of batman protocol: https://www.open-mesh.org/projects/open-mesh/wiki/BATMANConcept - -interesting paper on lora mesh: https://portal.research.lu.se/portal/files/45735775/paper.pdf -It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728 -https://en.wikipedia.org/wiki/Dynamic_Source_Routing - -broadcast solution: -Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent) -Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set - -compare to db sync algorithm? - -what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies. - -If Y were to die, at least the neighbor nodes of Y would have their last known position of Y. From d7cf7e2eb4e0517805bd4a710af6ba0962a9d72f Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 10:42:25 -0700 Subject: [PATCH 05/10] Allow advanced users to set arbitrary spreadfactor/codingrate/bandwidth --- proto | 2 +- src/mesh/NodeDB.cpp | 4 +-- src/mesh/RadioInterface.cpp | 12 ++++--- src/mesh/RadioInterface.h | 15 +++------ src/mesh/RadioLibInterface.cpp | 59 +++++++++++++++++++++------------- 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/proto b/proto index 0523977d1..c715e506d 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 0523977d1f6c378cf78859044e2edbdba45150fa +Subproject commit c715e506df9ab1a76293d1c20dd5c904b009e1ea diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 92f47adfe..206d00239 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -29,7 +29,7 @@ DeviceState versions used to be defined in the .proto file but really only this #define here. */ -#define DEVICESTATE_CUR_VER 10 +#define DEVICESTATE_CUR_VER 11 #define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER #ifndef NO_ESP32 @@ -93,7 +93,7 @@ void NodeDB::resetRadioConfig() // so incompatible radios can talk together channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range - channelSettings.tx_power = 23; + channelSettings.tx_power = 0; // default memcpy(&channelSettings.psk.bytes, &defaultpsk, sizeof(channelSettings.psk)); channelSettings.psk.size = sizeof(defaultpsk); strcpy(channelSettings.name, "Default"); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index ad255d621..4967954e1 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -115,22 +115,26 @@ unsigned long hash(char *str) return hash; } +#define POWER_DEFAULT 17 + /** * Pull our channel settings etc... from protobufs to the dumb interface settings */ void RadioInterface::applyModemConfig() { // Set up default configuration - // No Sync Words in LORA mode. - modemConfig = (ModemConfigChoice)channelSettings.modem_config; + // No Sync Words in LORA mode + + power = channelSettings.tx_power; + if (power == 0) + power = POWER_DEFAULT; // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM int channel_num = hash(channelSettings.name) % NUM_CHANNELS; freq = CH0 + CH_SPACING * channel_num; - power = channelSettings.tx_power; DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num, - channelSettings.tx_power); + power); } ErrorCode SimRadio::send(MeshPacket *p) diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 389515199..b8cba208d 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -1,10 +1,10 @@ #pragma once +#include "../concurrency/NotifiedWorkerThread.h" #include "MemoryPool.h" #include "MeshTypes.h" #include "Observer.h" #include "PointerQueue.h" -#include "../concurrency/NotifiedWorkerThread.h" #include "mesh.pb.h" #define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission @@ -31,13 +31,6 @@ typedef struct { uint8_t flags; } PacketHeader; -typedef enum { - Bw125Cr45Sf128 = 0, ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range - Bw500Cr45Sf128, ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range - Bw31_25Cr48Sf512, ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range - Bw125Cr48Sf4096, ///< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range -} ModemConfigChoice; - /** * Basic operations all radio chipsets must implement. * @@ -72,9 +65,7 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread void deliverToReceiver(MeshPacket *p); public: - float freq = 915.0; // FIXME, init all these params from user setings - int8_t power = 17; - ModemConfigChoice modemConfig; + float freq = 915.0; /** pool is the pool we will alloc our rx packets from * rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool @@ -116,6 +107,8 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread virtual bool reconfigure() = 0; protected: + int8_t power = 17; // Set by applyModemConfig() + /*** * given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the * PacketHeader & payload). diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 4551edd2e..bfaefb1f2 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -1,5 +1,6 @@ #include "RadioLibInterface.h" #include "MeshTypes.h" +#include "NodeDB.h" #include "mesh-pb-constants.h" #include #include @@ -64,29 +65,41 @@ void RadioLibInterface::applyModemConfig() { RadioInterface::applyModemConfig(); - switch (modemConfig) { - case Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range - bw = 125; - cr = 5; - sf = 7; - break; - case Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range - bw = 500; - cr = 5; - sf = 7; - break; - case Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range - bw = 31.25; - cr = 8; - sf = 9; - break; - case Bw125Cr48Sf4096: - bw = 125; - cr = 8; - sf = 12; - break; - default: - assert(0); // Unknown enum + if (channelSettings.spread_factor == 0) { + switch (channelSettings.modem_config) { + case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium + ///< range + bw = 125; + cr = 5; + sf = 7; + break; + case ChannelSettings_ModemConfig_Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short + ///< range + bw = 500; + cr = 5; + sf = 7; + break; + case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long + ///< range + bw = 31.25; + cr = 8; + sf = 9; + break; + case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: + bw = 125; + cr = 8; + sf = 12; + break; + default: + assert(0); // Unknown enum + } + } else { + sf = channelSettings.spread_factor; + cr = channelSettings.coding_rate; + bw = channelSettings.bandwidth; + + if (bw == 31) // This parameter is not an integer + bw = 31.25; } } From 178958c1657d6d31b7e8446adfcc13014ebe7391 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 10:46:44 -0700 Subject: [PATCH 06/10] allow advanced users to specify channel numbers if they wish --- src/mesh/RadioInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 4967954e1..69e335ed4 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -129,8 +129,8 @@ void RadioInterface::applyModemConfig() if (power == 0) power = POWER_DEFAULT; - // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM - int channel_num = hash(channelSettings.name) % NUM_CHANNELS; + // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name + int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % NUM_CHANNELS; freq = CH0 + CH_SPACING * channel_num; DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num, From 55dafcbecb9b394fd769b0705e22b16122b7d44a Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 11:04:03 -0700 Subject: [PATCH 07/10] fix #269 see below /** * Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs. * The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they their nodes * aren't talking to each other. * * This string is of the form "#name-XY". * * Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together. * Y is not yet used but should eventually indicate 'speed/range' of the link * * This function will also need to be implemented in GUI apps that talk to the radio. * * https://github.com/meshtastic/Meshtastic-device/issues/269 */ const char *getChannelName(); --- src/graphics/Screen.cpp | 73 ++++++++++++++++--------------------- src/main.cpp | 2 +- src/mesh/NodeDB.cpp | 27 ++++++++++++++ src/mesh/NodeDB.h | 24 ++++++++++-- src/mesh/RadioInterface.cpp | 2 + 5 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 25576fb6a..ca0995c65 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -25,13 +25,13 @@ along with this program. If not, see . #include "GPS.h" #include "MeshService.h" #include "NodeDB.h" +#include "Screen.h" +#include "configs.h" #include "configuration.h" #include "graphics/images.h" #include "main.h" #include "mesh-pb-constants.h" -#include "Screen.h" #include "utils.h" -#include "configs.h" using namespace meshtastic; /** @todo remove */ @@ -44,10 +44,10 @@ static uint32_t targetFramerate = IDLE_FRAMERATE; static char btPIN[16] = "888888"; // This image definition is here instead of images.h because it's modified dynamically by the drawBattery function -uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; +uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C}; // Threshold values for the GPS lock accuracy bar display -uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 }; +uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100}; // Stores the last 4 of our hardware ID, to make finding the device for pairing easier static char ourId[5]; @@ -208,37 +208,32 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no // Draw GPS status summary static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) { - if (!gps->getIsConnected()) - { + if (!gps->getIsConnected()) { display->drawString(x, y - 2, "No GPS"); return; } display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty); - if (!gps->getHasLock()) - { + if (!gps->getHasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; - } - else - { + } else { char satsString[3]; - uint8_t bar[2] = { 0 }; + uint8_t bar[2] = {0}; - //Draw DOP signal bars - for(int i = 0; i < 5; i++) - { + // Draw DOP signal bars + for (int i = 0; i < 5; i++) { if (gps->getDOP() <= dopThresholds[i]) bar[0] = ~((1 << (5 - i)) - 1); else bar[0] = 0b10000000; - //bar[1] = bar[0]; + // bar[1] = bar[0]; display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar); } - //Draw satellite image + // Draw satellite image display->drawFastImage(x + 24, y, 8, 8, imgSatellite); - //Draw the number of satellites + // Draw the number of satellites sprintf(satsString, "%d", gps->getNumSatellites()); display->drawString(x + 34, y - 2, satsString); } @@ -390,7 +385,6 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY); - Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow}; for (int i = 0; i < 4; i++) { @@ -406,8 +400,8 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp // Draw the compass heading static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) { - Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f); - Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f); + Point N1(-0.04f, -0.65f), N2(0.04f, -0.65f); + Point N3(-0.04f, -0.55f), N4(0.04f, -0.55f); Point *rosePoints[] = {&N1, &N2, &N3, &N4}; for (int i = 0; i < 4; i++) { @@ -440,8 +434,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ displayedNodeNum = n->num; // We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes - if(sinceLastSeen(n) > 120) - { + if (sinceLastSeen(n) > 120) { service.sendNetworkPing(displayedNodeNum, true); } } @@ -477,14 +470,12 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; bool hasNodeHeading = false; - if(ourNode && hasPosition(ourNode)) - { + if (ourNode && hasPosition(ourNode)) { Position &op = ourNode->position; float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); drawCompassHeading(display, compassX, compassY, myHeading); - if(hasPosition(node)) - { + if (hasPosition(node)) { // display direction toward node hasNodeHeading = true; Position &p = node->position; @@ -499,16 +490,15 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); headingRadian = bearingToOther - myHeading; drawNodeHeading(display, compassX, compassY, headingRadian); - } + } } - if(!hasNodeHeading) + if (!hasNodeHeading) // direction to node is unknown so display question mark // Debug info for gps lock errors // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); - // Must be after distStr is populated drawColumns(display, x, y, fields); } @@ -779,7 +769,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 char channelStr[20]; { concurrency::LockGuard guard(&lock); - snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str()); + snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str()); // Display power status if (powerStatus->getHasBattery()) @@ -824,18 +814,17 @@ void Screen::adjustBrightness() dispdev.setBrightness(brightness); } -int Screen::handleStatusUpdate(const meshtastic::Status *arg) +int Screen::handleStatusUpdate(const meshtastic::Status *arg) { - //DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); - switch(arg->getStatusType()) - { - case STATUS_TYPE_NODE: - if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) - setFrames(); - prevFrame = -1; - nodeDB.updateGUI = false; - nodeDB.updateTextMessage = false; - break; + // DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); + switch (arg->getStatusType()) { + case STATUS_TYPE_NODE: + if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) + setFrames(); + prevFrame = -1; + nodeDB.updateGUI = false; + nodeDB.updateTextMessage = false; + break; } setPeriod(1); // Update the screen right away return 0; diff --git a/src/main.cpp b/src/main.cpp index 3d5dd6975..b8ee00219 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -364,7 +364,7 @@ void loop() #endif // Update the screen last, after we've figured out what to show. - screen.debug_info()->setChannelNameStatus(channelSettings.name); + screen.debug_info()->setChannelNameStatus(getChannelName()); // screen.debug()->setPowerStatus(powerStatus); // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 206d00239..1b7a1cd84 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -66,6 +66,33 @@ static uint8_t ourMacAddr[6]; */ NodeNum displayedNodeNum; +/** + * Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs. + * The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they +their nodes + * aren't talking to each other. + * + * This string is of the form "#name-XY". + * + * Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together. + * Y is not yet used but should eventually indicate 'speed/range' of the link + * + * This function will also need to be implemented in GUI apps that talk to the radio. + * + * https://github.com/meshtastic/Meshtastic-device/issues/269 + */ +const char *getChannelName() +{ + static char buf[32]; + + uint8_t code = 0; + for (int i = 0; i < sizeof(channelSettings.psk.size); i++) + code ^= channelSettings.psk.bytes[i]; + + snprintf(buf, sizeof(buf), "#%s-%c", channelSettings.name, 'A' + (code % 26)); + return buf; +} + NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {} void NodeDB::resetRadioConfig() diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index f8bbb4d69..bd22f730f 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -1,12 +1,12 @@ #pragma once +#include "Observer.h" #include #include -#include "Observer.h" #include "MeshTypes.h" -#include "mesh-pb-constants.h" #include "NodeStatus.h" +#include "mesh-pb-constants.h" extern DeviceState devicestate; extern MyNodeInfo &myNodeInfo; @@ -95,7 +95,8 @@ class NodeDB NodeInfo *getOrCreateNode(NodeNum n); /// Notify observers of changes to the DB - void notifyObservers(bool forceUpdate = false) { + void notifyObservers(bool forceUpdate = false) + { // Notify observers of the current node state const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate); newStatus.notifyObservers(&status); @@ -115,3 +116,20 @@ class NodeDB extern NodeNum displayedNodeNum; extern NodeDB nodeDB; + +/** + * Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs. + * The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they +their nodes + * aren't talking to each other. + * + * This string is of the form "#name-XY". + * + * Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together. + * Y is not yet used but should eventually indicate 'speed/range' of the link + * + * This function will also need to be implemented in GUI apps that talk to the radio. + * + * https://github.com/meshtastic/Meshtastic-device/issues/269 + */ +const char *getChannelName(); \ No newline at end of file diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 69e335ed4..26be9de3f 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -115,6 +115,8 @@ unsigned long hash(char *str) return hash; } + + #define POWER_DEFAULT 17 /** From 9b25818a50060f52196c01f9987ad0d083a32b8f Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 15:51:57 -0700 Subject: [PATCH 08/10] fix #249: report battery levels even if no GPS lock @professr I noticed you added a "newStatus" observable to the GPS class. Do you remember why you didn't remove the old GPS status (which seemed to be dumber). Is it just because you didn't want to risk breaking MeshService? (I assume) In this change I removed the old Observable and all seems well (just using newStatus everywhere). --- src/gps/GPS.h | 3 ++- src/gps/NEMAGPS.cpp | 4 ---- src/gps/UBloxGPS.cpp | 1 - src/mesh/MeshService.cpp | 8 +++++--- src/mesh/MeshService.h | 6 ++++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 46b4a5c05..409c6da9d 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -25,7 +25,7 @@ void readFromRTC(); * * When new data is available it will notify observers. */ -class GPS : public Observable +class GPS { protected: bool hasValidLocation = false; // default to false, until we complete our first read @@ -48,6 +48,7 @@ class GPS : public Observable virtual ~GPS() {} + /** We will notify this observable anytime GPS state has changed meaningfully */ Observable newStatus; /** diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index 1645a1656..a1d848ad8 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -66,10 +66,6 @@ void NEMAGPS::loop() // expect gps pos lat=37.520825, lon=-122.309162, alt=158 DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5); - - hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 - if (hasValidLocation) - notifyObservers(NULL); } // Notify any status instances that are observing us diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 0ed506f64..c7a8501c1 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -160,7 +160,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s { if (hasValidLocation) { wantNewLocation = false; - notifyObservers(NULL); // ublox.powerOff(); } } else // we didn't get a location update, go back to sleep and hope the characters show up diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 38babd904..f5d0ac173 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -68,7 +68,8 @@ void MeshService::init() sendOwnerPeriod.setup(); nodeDB.init(); - gpsObserver.observe(gps); + assert(gps); + gpsObserver.observe(&gps->newStatus); packetReceivedObserver.observe(&router.notifyPacketReceived); } @@ -283,9 +284,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) sendToMesh(p); } -int MeshService::onGPSChanged(void *unused) +int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { - // DEBUG_MSG("got gps notify\n"); // Update our local node info with our position (even if we don't decide to update anyone else) MeshPacket *p = router.allocForSending(); @@ -305,6 +305,8 @@ int MeshService::onGPSChanged(void *unused) pos.battery_level = powerStatus->getBatteryChargePercent(); updateBatteryLevel(pos.battery_level); + // DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); + // We limit our GPS broadcasts to a max rate static uint32_t lastGpsSend; uint32_t now = timing::millis(); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index f6e688e19..7e8810778 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -4,6 +4,7 @@ #include #include +#include "GPSStatus.h" #include "MemoryPool.h" #include "MeshRadio.h" #include "MeshTypes.h" @@ -17,7 +18,8 @@ */ class MeshService { - CallbackObserver gpsObserver = CallbackObserver(this, &MeshService::onGPSChanged); + CallbackObserver gpsObserver = + CallbackObserver(this, &MeshService::onGPSChanged); CallbackObserver packetReceivedObserver = CallbackObserver(this, &MeshService::handleFromRadio); @@ -85,7 +87,7 @@ class MeshService /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// returns 0 to allow futher processing - int onGPSChanged(void *arg); + int onGPSChanged(const meshtastic::GPSStatus *arg); /// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs /// to keep the packet around it makes a copy From 6a402b13fa5afea1924af9b01a241ff4638da361 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 17:03:36 -0700 Subject: [PATCH 09/10] Add battery sensing (mostly) for TBEAM0.7 However, disabled until someone with suitable hardware can test and report back. @slavino and @tschundler would you be willing to try it with your boards? You'll need to uncomment the following line in configuration.h // #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage --- platformio.ini | 6 +- src/Power.cpp | 154 ++++++++++++++++++++++++++++++++------------ src/configuration.h | 4 +- src/main.cpp | 2 - src/power.h | 22 +++---- 5 files changed, 131 insertions(+), 57 deletions(-) diff --git a/platformio.ini b/platformio.ini index f3a4db2a6..1b11e1cbc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = tbeam +default_envs = tbeam [common] ; common is not currently used @@ -69,7 +69,8 @@ lib_deps = https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f https://github.com/meshtastic/TinyGPSPlus.git - + https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460 + ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] platform = espressif32 @@ -99,7 +100,6 @@ extends = esp32_base board = ttgo-t-beam lib_deps = ${env.lib_deps} - https://github.com/meshtastic/AXP202X_Library.git build_flags = ${esp32_base.build_flags} -D TBEAM_V10 diff --git a/src/Power.cpp b/src/Power.cpp index 23332698b..7bfa42d29 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -1,27 +1,92 @@ #include "power.h" #include "PowerFSM.h" #include "main.h" -#include "utils.h" #include "sleep.h" - -#ifdef TBEAM_V10 +#include "utils.h" // FIXME. nasty hack cleanup how we load axp192 #undef AXP192_SLAVE_ADDRESS #include "axp20x.h" + +#ifdef TBEAM_V10 AXP20X_Class axp; +#endif + bool pmu_irq = false; Power *power; -bool Power::setup() +/** + * If this board has a battery level sensor, set this to a valid implementation + */ +static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor + +/** + * A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input + */ +class AnalogBatteryLevel : public HasBatteryLevel { + /** + * Battery state of charge, from 0 to 100 or -1 for unknown + * + * FIXME - use a lipo lookup table, the current % full is super wrong + */ + virtual int getBattPercentage() + { + float v = getBattVoltage(); - axp192Init(); - concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device - setPeriod(1); + if (v < 2.1) + return -1; - return axp192_found; + return 100 * (getBattVoltage() - 3.27) / (4.2 - 3.27); + } + + /** + * The raw voltage of the battery or NAN if unknown + */ + virtual float getBattVoltage() + { + return +#ifdef BATTERY_PIN + analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0); +#else + NAN; +#endif + } + + /** + * return true if there is a battery installed in this unit + */ + virtual bool isBatteryConnect() { return true; } +} analogLevel; + +bool Power::analogInit() +{ +#ifdef BATTERY_PIN + DEBUG_MSG("Using analog input for battery level\n"); + adcAttachPin(BATTERY_PIN); + // adcStart(BATTERY_PIN); + analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution. + batteryLevel = &analogLevel; + return true; +#else + return false; +#endif +} + +bool Power::setup() +{ + bool found = axp192Init(); + + if (!found) { + found = analogInit(); + } + if (found) { + concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device + setPeriod(1); + } + + return found; } /// Reads power status to powerStatus singleton. @@ -29,42 +94,45 @@ bool Power::setup() // TODO(girts): move this and other axp stuff to power.h/power.cpp. void Power::readPowerStatus() { - bool hasBattery = axp.isBatteryConnect(); - int batteryVoltageMv = 0; - uint8_t batteryChargePercent = 0; - if (hasBattery) { - batteryVoltageMv = axp.getBattVoltage(); - // If the AXP192 returns a valid battery percentage, use it - if (axp.getBattPercentage() >= 0) { - batteryChargePercent = axp.getBattPercentage(); - } else { - // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error - // In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h - batteryChargePercent = clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100); + if (batteryLevel) { + bool hasBattery = batteryLevel->isBatteryConnect(); + int batteryVoltageMv = 0; + uint8_t batteryChargePercent = 0; + if (hasBattery) { + batteryVoltageMv = batteryLevel->getBattVoltage(); + // If the AXP192 returns a valid battery percentage, use it + if (batteryLevel->getBattPercentage() >= 0) { + batteryChargePercent = batteryLevel->getBattPercentage(); + } else { + // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error + // In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in + // power.h + batteryChargePercent = + clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), + 0, 100); + } } + + // Notify any status instances that are observing us + const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus( + hasBattery, batteryLevel->isVBUSPlug(), batteryLevel->isChargeing(), batteryVoltageMv, batteryChargePercent); + newStatus.notifyObservers(&powerStatus); + + // If we have a battery at all and it is less than 10% full, force deep sleep + if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && batteryLevel->getBattVoltage() < MIN_BAT_MILLIVOLTS) + powerFSM.trigger(EVENT_LOW_BATTERY); } - - // Notify any status instances that are observing us - const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(hasBattery, axp.isVBUSPlug(), axp.isChargeing(), batteryVoltageMv, batteryChargePercent); - newStatus.notifyObservers(&powerStatus); - - // If we have a battery at all and it is less than 10% full, force deep sleep - if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && - axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) - powerFSM.trigger(EVENT_LOW_BATTERY); } -void Power::doTask() +void Power::doTask() { readPowerStatus(); - + // Only read once every 20 seconds once the power status for the app has been initialized - if(statusHandler && statusHandler->isInitialized()) + if (statusHandler && statusHandler->isInitialized()) setPeriod(1000 * 20); } -#endif // TBEAM_V10 -#ifdef AXP192_SLAVE_ADDRESS /** * Init the power manager chip * @@ -74,10 +142,13 @@ void Power::doTask() 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS */ -void Power::axp192Init() +bool Power::axp192Init() { +#ifdef TBEAM_V10 if (axp192_found) { if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { + batteryLevel = &axp; + DEBUG_MSG("AXP192 Begin PASS\n"); // axp.setChgLEDMode(LED_BLINK_4HZ); @@ -135,12 +206,16 @@ void Power::axp192Init() } else { DEBUG_MSG("AXP192 not found\n"); } -} + + return axp192_found; +#else + return false; #endif +} -void Power::loop() + +void Power::loop() { - #ifdef PMU_IRQ if (pmu_irq) { pmu_irq = false; @@ -174,6 +249,5 @@ void Power::loop() axp.clearIRQ(); } -#endif // T_BEAM_V10 - +#endif } diff --git a/src/configuration.h b/src/configuration.h index 89e8ef81d..613f20d87 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -167,7 +167,6 @@ along with this program. If not, see . // Leave undefined to disable our PMU IRQ handler #define PMU_IRQ 35 - #define AXP192_SLAVE_ADDRESS 0x34 #elif defined(TBEAM_V07) @@ -180,6 +179,7 @@ along with this program. If not, see . #define I2C_SCL 22 #define BUTTON_PIN 39 +// #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage #ifndef USE_JTAG #define RF95_RESET 23 @@ -278,6 +278,8 @@ along with this program. If not, see . #define GPS_RX_PIN 36 #define GPS_TX_PIN 39 +// #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage + #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 diff --git a/src/main.cpp b/src/main.cpp index b8ee00219..3cc4097d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,13 +210,11 @@ void setup() esp32Setup(); #endif -#ifdef TBEAM_V10 // Currently only the tbeam has a PMU power = new Power(); power->setup(); power->setStatusHandler(powerStatus); powerStatus->observe(&power->newStatus); -#endif #ifdef NRF52_SERIES nrf52Setup(); diff --git a/src/power.h b/src/power.h index dd6b8ce87..a779089ad 100644 --- a/src/power.h +++ b/src/power.h @@ -1,6 +1,6 @@ #pragma once -#include "concurrency/PeriodicTask.h" #include "PowerStatus.h" +#include "concurrency/PeriodicTask.h" /** * Per @spattinson @@ -18,23 +18,23 @@ class Power : public concurrency::PeriodicTask { - public: - + public: Observable newStatus; void readPowerStatus(); void loop(); virtual bool setup(); virtual void doTask(); - void setStatusHandler(meshtastic::PowerStatus *handler) - { - statusHandler = handler; - } - - protected: - meshtastic::PowerStatus *statusHandler; - virtual void axp192Init(); + void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } + protected: + meshtastic::PowerStatus *statusHandler; + + /// Setup a axp192, return true if found + bool axp192Init(); + + /// Setup a simple ADC input based battery sensor + bool analogInit(); }; extern Power *power; \ No newline at end of file From 8c7aa07c7000fda9042fc62ffca0b73547198891 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 17:10:59 -0700 Subject: [PATCH 10/10] Only do AXP debugging on ESP32 targets --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 1b11e1cbc..56efa7483 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,7 +32,6 @@ board_build.partitions = partition-table.csv ; note: we add src to our include search path so that lmic_project_config can override ; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map - -DAXP_DEBUG_PORT=Serial -DHW_VERSION_${sysenv.COUNTRY} -DAPP_VERSION=${sysenv.APP_VERSION} -DHW_VERSION=${sysenv.HW_VERSION} @@ -81,6 +80,7 @@ debug_init_break = tbreak setup build_flags = ${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue -lnimble -std=c++11 -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -DAXP_DEBUG_PORT=Serial # Hmm - this doesn't work yet # board_build.ldscript = linker/esp32.extram.bss.ld lib_ignore = segger_rtt