mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-17 18:53:30 +00:00
commit
029b2f3139
@ -1,9 +1,19 @@
|
|||||||
# Build instructions
|
# Build instructions
|
||||||
|
|
||||||
This project uses the simple PlatformIO build system. You can use the IDE, but for brevity
|
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode.
|
||||||
in these instructions I describe use of their command line tool.
|
|
||||||
|
|
||||||
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. 
|
||||||
|
4. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). 
|
||||||
|
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. `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
|
||||||
|
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
|
||||||
2. Install [PlatformIO](https://platformio.org/platformio-ide)
|
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:
|
||||||
|
|
||||||
@ -11,11 +21,10 @@ in these instructions I describe use of their command line tool.
|
|||||||
git clone https://github.com/meshtastic/Meshtastic-device.git
|
git clone https://github.com/meshtastic/Meshtastic-device.git
|
||||||
cd Meshtastic-device
|
cd Meshtastic-device
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Run `git submodule update --init --recursive` to pull in dependencies this project needs.
|
4. Run `git submodule update --init --recursive` to pull in dependencies this project needs.
|
||||||
5. If you are outside the USA, run "export COUNTRY=EU865" (or whatever) to set the correct frequency range for your country. Options are provided for `EU433`, `EU865`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries.
|
5. If you are outside the USA, run "export COUNTRY=EU865" (or whatever) to set the correct frequency range for your country. Options are provided for `EU433`, `EU865`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries.
|
||||||
6. Plug the radio into your USB port
|
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.
|
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
|
## Decoding stack traces
|
||||||
|
@ -1,5 +1,75 @@
|
|||||||
# Mesh broadcast algorithm
|
# 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](<https://en.wikipedia.org/wiki/Flooding_(computer_networking)>). 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/
|
great source of papers and class notes: http://www.cs.jhu.edu/~cs647/
|
||||||
|
|
||||||
flood routing improvements
|
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.
|
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?
|
- 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.
|
- 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.
|
|
||||||
|
@ -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
|
; 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
|
; 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
|
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}
|
-DHW_VERSION_${sysenv.COUNTRY}
|
||||||
-DAPP_VERSION=${sysenv.APP_VERSION}
|
-DAPP_VERSION=${sysenv.APP_VERSION}
|
||||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||||
@ -69,6 +68,7 @@ lib_deps =
|
|||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
||||||
https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f
|
https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f
|
||||||
https://github.com/meshtastic/TinyGPSPlus.git
|
https://github.com/meshtastic/TinyGPSPlus.git
|
||||||
|
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||||
|
|
||||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||||
[esp32_base]
|
[esp32_base]
|
||||||
@ -80,6 +80,7 @@ debug_init_break = tbreak setup
|
|||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue -lnimble -std=c++11
|
${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
|
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||||
|
-DAXP_DEBUG_PORT=Serial
|
||||||
# Hmm - this doesn't work yet
|
# Hmm - this doesn't work yet
|
||||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore = segger_rtt
|
lib_ignore = segger_rtt
|
||||||
@ -99,7 +100,6 @@ extends = esp32_base
|
|||||||
board = ttgo-t-beam
|
board = ttgo-t-beam
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
https://github.com/meshtastic/AXP202X_Library.git
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TBEAM_V10
|
${esp32_base.build_flags} -D TBEAM_V10
|
||||||
|
|
||||||
|
2
proto
2
proto
@ -1 +1 @@
|
|||||||
Subproject commit 0523977d1f6c378cf78859044e2edbdba45150fa
|
Subproject commit c715e506df9ab1a76293d1c20dd5c904b009e1ea
|
116
src/Power.cpp
116
src/Power.cpp
@ -1,27 +1,92 @@
|
|||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "utils.h"
|
||||||
#ifdef TBEAM_V10
|
|
||||||
|
|
||||||
// FIXME. nasty hack cleanup how we load axp192
|
// FIXME. nasty hack cleanup how we load axp192
|
||||||
#undef AXP192_SLAVE_ADDRESS
|
#undef AXP192_SLAVE_ADDRESS
|
||||||
#include "axp20x.h"
|
#include "axp20x.h"
|
||||||
|
|
||||||
|
#ifdef TBEAM_V10
|
||||||
AXP20X_Class axp;
|
AXP20X_Class axp;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool pmu_irq = false;
|
bool pmu_irq = false;
|
||||||
|
|
||||||
Power *power;
|
Power *power;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
if (v < 2.1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
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 Power::setup()
|
||||||
{
|
{
|
||||||
|
bool found = axp192Init();
|
||||||
|
|
||||||
axp192Init();
|
if (!found) {
|
||||||
|
found = analogInit();
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||||
setPeriod(1);
|
setPeriod(1);
|
||||||
|
}
|
||||||
|
|
||||||
return axp192_found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads power status to powerStatus singleton.
|
/// Reads power status to powerStatus singleton.
|
||||||
@ -29,30 +94,35 @@ bool Power::setup()
|
|||||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||||
void Power::readPowerStatus()
|
void Power::readPowerStatus()
|
||||||
{
|
{
|
||||||
bool hasBattery = axp.isBatteryConnect();
|
if (batteryLevel) {
|
||||||
|
bool hasBattery = batteryLevel->isBatteryConnect();
|
||||||
int batteryVoltageMv = 0;
|
int batteryVoltageMv = 0;
|
||||||
uint8_t batteryChargePercent = 0;
|
uint8_t batteryChargePercent = 0;
|
||||||
if (hasBattery) {
|
if (hasBattery) {
|
||||||
batteryVoltageMv = axp.getBattVoltage();
|
batteryVoltageMv = batteryLevel->getBattVoltage();
|
||||||
// If the AXP192 returns a valid battery percentage, use it
|
// If the AXP192 returns a valid battery percentage, use it
|
||||||
if (axp.getBattPercentage() >= 0) {
|
if (batteryLevel->getBattPercentage() >= 0) {
|
||||||
batteryChargePercent = axp.getBattPercentage();
|
batteryChargePercent = batteryLevel->getBattPercentage();
|
||||||
} else {
|
} else {
|
||||||
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
// 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
|
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in
|
||||||
batteryChargePercent = clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
|
// 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
|
// Notify any status instances that are observing us
|
||||||
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(hasBattery, axp.isVBUSPlug(), axp.isChargeing(), batteryVoltageMv, batteryChargePercent);
|
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(
|
||||||
|
hasBattery, batteryLevel->isVBUSPlug(), batteryLevel->isChargeing(), batteryVoltageMv, batteryChargePercent);
|
||||||
newStatus.notifyObservers(&powerStatus);
|
newStatus.notifyObservers(&powerStatus);
|
||||||
|
|
||||||
// If we have a battery at all and it is less than 10% full, force deep sleep
|
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||||
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() &&
|
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && batteryLevel->getBattVoltage() < MIN_BAT_MILLIVOLTS)
|
||||||
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS)
|
|
||||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Power::doTask()
|
void Power::doTask()
|
||||||
{
|
{
|
||||||
@ -62,9 +132,7 @@ void Power::doTask()
|
|||||||
if (statusHandler && statusHandler->isInitialized())
|
if (statusHandler && statusHandler->isInitialized())
|
||||||
setPeriod(1000 * 20);
|
setPeriod(1000 * 20);
|
||||||
}
|
}
|
||||||
#endif // TBEAM_V10
|
|
||||||
|
|
||||||
#ifdef AXP192_SLAVE_ADDRESS
|
|
||||||
/**
|
/**
|
||||||
* Init the power manager chip
|
* 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
|
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
|
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||||
*/
|
*/
|
||||||
void Power::axp192Init()
|
bool Power::axp192Init()
|
||||||
{
|
{
|
||||||
|
#ifdef TBEAM_V10
|
||||||
if (axp192_found) {
|
if (axp192_found) {
|
||||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||||
|
batteryLevel = &axp;
|
||||||
|
|
||||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||||
|
|
||||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||||
@ -135,12 +206,16 @@ void Power::axp192Init()
|
|||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("AXP192 not found\n");
|
DEBUG_MSG("AXP192 not found\n");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return axp192_found;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Power::loop()
|
void Power::loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef PMU_IRQ
|
#ifdef PMU_IRQ
|
||||||
if (pmu_irq) {
|
if (pmu_irq) {
|
||||||
pmu_irq = false;
|
pmu_irq = false;
|
||||||
@ -174,6 +249,5 @@ void Power::loop()
|
|||||||
axp.clearIRQ();
|
axp.clearIRQ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // T_BEAM_V10
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
// Leave undefined to disable our PMU IRQ handler
|
// Leave undefined to disable our PMU IRQ handler
|
||||||
#define PMU_IRQ 35
|
#define PMU_IRQ 35
|
||||||
|
|
||||||
#define AXP192_SLAVE_ADDRESS 0x34
|
#define AXP192_SLAVE_ADDRESS 0x34
|
||||||
|
|
||||||
#elif defined(TBEAM_V07)
|
#elif defined(TBEAM_V07)
|
||||||
@ -180,6 +179,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define I2C_SCL 22
|
#define I2C_SCL 22
|
||||||
|
|
||||||
#define BUTTON_PIN 39
|
#define BUTTON_PIN 39
|
||||||
|
// #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||||
|
|
||||||
#ifndef USE_JTAG
|
#ifndef USE_JTAG
|
||||||
#define RF95_RESET 23
|
#define RF95_RESET 23
|
||||||
@ -278,6 +278,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define GPS_RX_PIN 36
|
#define GPS_RX_PIN 36
|
||||||
#define GPS_TX_PIN 39
|
#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_SDA 21 // I2C pins for this board
|
||||||
#define I2C_SCL 22
|
#define I2C_SCL 22
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ void readFromRTC();
|
|||||||
*
|
*
|
||||||
* When new data is available it will notify observers.
|
* When new data is available it will notify observers.
|
||||||
*/
|
*/
|
||||||
class GPS : public Observable<void *>
|
class GPS
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
bool hasValidLocation = false; // default to false, until we complete our first read
|
bool hasValidLocation = false; // default to false, until we complete our first read
|
||||||
@ -48,6 +48,7 @@ class GPS : public Observable<void *>
|
|||||||
|
|
||||||
virtual ~GPS() {}
|
virtual ~GPS() {}
|
||||||
|
|
||||||
|
/** We will notify this observable anytime GPS state has changed meaningfully */
|
||||||
Observable<const meshtastic::GPSStatus *> newStatus;
|
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,10 +66,6 @@ void NEMAGPS::loop()
|
|||||||
|
|
||||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
// 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);
|
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
|
// Notify any status instances that are observing us
|
||||||
|
@ -160,7 +160,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
{
|
{
|
||||||
if (hasValidLocation) {
|
if (hasValidLocation) {
|
||||||
wantNewLocation = false;
|
wantNewLocation = false;
|
||||||
notifyObservers(NULL);
|
|
||||||
// ublox.powerOff();
|
// ublox.powerOff();
|
||||||
}
|
}
|
||||||
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
||||||
|
@ -25,13 +25,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
|
#include "Screen.h"
|
||||||
|
#include "configs.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "Screen.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "configs.h"
|
|
||||||
|
|
||||||
using namespace meshtastic; /** @todo remove */
|
using namespace meshtastic; /** @todo remove */
|
||||||
|
|
||||||
@ -208,25 +208,20 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no
|
|||||||
// Draw GPS status summary
|
// Draw GPS status summary
|
||||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
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");
|
display->drawString(x, y - 2, "No GPS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||||
if (!gps->getHasLock())
|
if (!gps->getHasLock()) {
|
||||||
{
|
|
||||||
display->drawString(x + 8, y - 2, "No sats");
|
display->drawString(x + 8, y - 2, "No sats");
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
char satsString[3];
|
char satsString[3];
|
||||||
uint8_t bar[2] = {0};
|
uint8_t bar[2] = {0};
|
||||||
|
|
||||||
// Draw DOP signal bars
|
// Draw DOP signal bars
|
||||||
for(int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++) {
|
||||||
{
|
|
||||||
if (gps->getDOP() <= dopThresholds[i])
|
if (gps->getDOP() <= dopThresholds[i])
|
||||||
bar[0] = ~((1 << (5 - i)) - 1);
|
bar[0] = ~((1 << (5 - i)) - 1);
|
||||||
else
|
else
|
||||||
@ -390,7 +385,6 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp
|
|||||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||||
|
|
||||||
|
|
||||||
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
|
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
@ -440,8 +434,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
displayedNodeNum = n->num;
|
displayedNodeNum = n->num;
|
||||||
|
|
||||||
// We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes
|
// 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);
|
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;
|
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
|
||||||
bool hasNodeHeading = false;
|
bool hasNodeHeading = false;
|
||||||
|
|
||||||
if(ourNode && hasPosition(ourNode))
|
if (ourNode && hasPosition(ourNode)) {
|
||||||
{
|
|
||||||
Position &op = ourNode->position;
|
Position &op = ourNode->position;
|
||||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
drawCompassHeading(display, compassX, compassY, myHeading);
|
drawCompassHeading(display, compassX, compassY, myHeading);
|
||||||
|
|
||||||
if(hasPosition(node))
|
if (hasPosition(node)) {
|
||||||
{
|
|
||||||
// display direction toward node
|
// display direction toward node
|
||||||
hasNodeHeading = true;
|
hasNodeHeading = true;
|
||||||
Position &p = node->position;
|
Position &p = node->position;
|
||||||
@ -508,7 +499,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
||||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
|
|
||||||
|
|
||||||
// Must be after distStr is populated
|
// Must be after distStr is populated
|
||||||
drawColumns(display, x, y, fields);
|
drawColumns(display, x, y, fields);
|
||||||
}
|
}
|
||||||
@ -779,7 +769,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
char channelStr[20];
|
char channelStr[20];
|
||||||
{
|
{
|
||||||
concurrency::LockGuard guard(&lock);
|
concurrency::LockGuard guard(&lock);
|
||||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||||
|
|
||||||
// Display power status
|
// Display power status
|
||||||
if (powerStatus->getHasBattery())
|
if (powerStatus->getHasBattery())
|
||||||
@ -827,8 +817,7 @@ void Screen::adjustBrightness()
|
|||||||
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||||
{
|
{
|
||||||
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||||
switch(arg->getStatusType())
|
switch (arg->getStatusType()) {
|
||||||
{
|
|
||||||
case STATUS_TYPE_NODE:
|
case STATUS_TYPE_NODE:
|
||||||
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||||
setFrames();
|
setFrames();
|
||||||
|
@ -210,13 +210,11 @@ void setup()
|
|||||||
esp32Setup();
|
esp32Setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TBEAM_V10
|
|
||||||
// Currently only the tbeam has a PMU
|
// Currently only the tbeam has a PMU
|
||||||
power = new Power();
|
power = new Power();
|
||||||
power->setup();
|
power->setup();
|
||||||
power->setStatusHandler(powerStatus);
|
power->setStatusHandler(powerStatus);
|
||||||
powerStatus->observe(&power->newStatus);
|
powerStatus->observe(&power->newStatus);
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NRF52_SERIES
|
#ifdef NRF52_SERIES
|
||||||
nrf52Setup();
|
nrf52Setup();
|
||||||
@ -364,7 +362,7 @@ void loop()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update the screen last, after we've figured out what to show.
|
// 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);
|
// 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)
|
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
||||||
|
@ -68,7 +68,8 @@ void MeshService::init()
|
|||||||
sendOwnerPeriod.setup();
|
sendOwnerPeriod.setup();
|
||||||
nodeDB.init();
|
nodeDB.init();
|
||||||
|
|
||||||
gpsObserver.observe(gps);
|
assert(gps);
|
||||||
|
gpsObserver.observe(&gps->newStatus);
|
||||||
packetReceivedObserver.observe(&router.notifyPacketReceived);
|
packetReceivedObserver.observe(&router.notifyPacketReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,9 +284,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||||||
sendToMesh(p);
|
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)
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
MeshPacket *p = router.allocForSending();
|
MeshPacket *p = router.allocForSending();
|
||||||
@ -305,6 +305,8 @@ int MeshService::onGPSChanged(void *unused)
|
|||||||
pos.battery_level = powerStatus->getBatteryChargePercent();
|
pos.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
updateBatteryLevel(pos.battery_level);
|
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
|
// We limit our GPS broadcasts to a max rate
|
||||||
static uint32_t lastGpsSend;
|
static uint32_t lastGpsSend;
|
||||||
uint32_t now = timing::millis();
|
uint32_t now = timing::millis();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "GPSStatus.h"
|
||||||
#include "MemoryPool.h"
|
#include "MemoryPool.h"
|
||||||
#include "MeshRadio.h"
|
#include "MeshRadio.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
@ -17,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
class MeshService
|
class MeshService
|
||||||
{
|
{
|
||||||
CallbackObserver<MeshService, void *> gpsObserver = CallbackObserver<MeshService, void *>(this, &MeshService::onGPSChanged);
|
CallbackObserver<MeshService, const meshtastic::GPSStatus *> gpsObserver =
|
||||||
|
CallbackObserver<MeshService, const meshtastic::GPSStatus *>(this, &MeshService::onGPSChanged);
|
||||||
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
|
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
|
||||||
CallbackObserver<MeshService, const MeshPacket *>(this, &MeshService::handleFromRadio);
|
CallbackObserver<MeshService, const MeshPacket *>(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
|
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
|
||||||
/// returns 0 to allow futher processing
|
/// 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
|
/// 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
|
/// to keep the packet around it makes a copy
|
||||||
|
@ -29,7 +29,7 @@ DeviceState versions used to be defined in the .proto file but really only this
|
|||||||
#define here.
|
#define here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DEVICESTATE_CUR_VER 10
|
#define DEVICESTATE_CUR_VER 11
|
||||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
@ -66,6 +66,33 @@ static uint8_t ourMacAddr[6];
|
|||||||
*/
|
*/
|
||||||
NodeNum displayedNodeNum;
|
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) {}
|
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||||
|
|
||||||
void NodeDB::resetRadioConfig()
|
void NodeDB::resetRadioConfig()
|
||||||
@ -93,7 +120,7 @@ void NodeDB::resetRadioConfig()
|
|||||||
// so incompatible radios can talk together
|
// so incompatible radios can talk together
|
||||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
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));
|
memcpy(&channelSettings.psk.bytes, &defaultpsk, sizeof(channelSettings.psk));
|
||||||
channelSettings.psk.size = sizeof(defaultpsk);
|
channelSettings.psk.size = sizeof(defaultpsk);
|
||||||
strcpy(channelSettings.name, "Default");
|
strcpy(channelSettings.name, "Default");
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Observer.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "Observer.h"
|
|
||||||
|
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "mesh-pb-constants.h"
|
|
||||||
#include "NodeStatus.h"
|
#include "NodeStatus.h"
|
||||||
|
#include "mesh-pb-constants.h"
|
||||||
|
|
||||||
extern DeviceState devicestate;
|
extern DeviceState devicestate;
|
||||||
extern MyNodeInfo &myNodeInfo;
|
extern MyNodeInfo &myNodeInfo;
|
||||||
@ -95,7 +95,8 @@ class NodeDB
|
|||||||
NodeInfo *getOrCreateNode(NodeNum n);
|
NodeInfo *getOrCreateNode(NodeNum n);
|
||||||
|
|
||||||
/// Notify observers of changes to the DB
|
/// Notify observers of changes to the DB
|
||||||
void notifyObservers(bool forceUpdate = false) {
|
void notifyObservers(bool forceUpdate = false)
|
||||||
|
{
|
||||||
// Notify observers of the current node state
|
// Notify observers of the current node state
|
||||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
||||||
newStatus.notifyObservers(&status);
|
newStatus.notifyObservers(&status);
|
||||||
@ -115,3 +116,20 @@ class NodeDB
|
|||||||
extern NodeNum displayedNodeNum;
|
extern NodeNum displayedNodeNum;
|
||||||
|
|
||||||
extern NodeDB nodeDB;
|
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();
|
@ -115,22 +115,28 @@ unsigned long hash(char *str)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define POWER_DEFAULT 17
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pull our channel settings etc... from protobufs to the dumb interface settings
|
* Pull our channel settings etc... from protobufs to the dumb interface settings
|
||||||
*/
|
*/
|
||||||
void RadioInterface::applyModemConfig()
|
void RadioInterface::applyModemConfig()
|
||||||
{
|
{
|
||||||
// Set up default configuration
|
// Set up default configuration
|
||||||
// No Sync Words in LORA mode.
|
// No Sync Words in LORA mode
|
||||||
modemConfig = (ModemConfigChoice)channelSettings.modem_config;
|
|
||||||
|
|
||||||
// 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;
|
power = channelSettings.tx_power;
|
||||||
|
if (power == 0)
|
||||||
|
power = POWER_DEFAULT;
|
||||||
|
|
||||||
|
// 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,
|
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)
|
ErrorCode SimRadio::send(MeshPacket *p)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../concurrency/NotifiedWorkerThread.h"
|
||||||
#include "MemoryPool.h"
|
#include "MemoryPool.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
#include "../concurrency/NotifiedWorkerThread.h"
|
|
||||||
#include "mesh.pb.h"
|
#include "mesh.pb.h"
|
||||||
|
|
||||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||||
@ -31,13 +31,6 @@ typedef struct {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} PacketHeader;
|
} 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.
|
* Basic operations all radio chipsets must implement.
|
||||||
*
|
*
|
||||||
@ -72,9 +65,7 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread
|
|||||||
void deliverToReceiver(MeshPacket *p);
|
void deliverToReceiver(MeshPacket *p);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float freq = 915.0; // FIXME, init all these params from user setings
|
float freq = 915.0;
|
||||||
int8_t power = 17;
|
|
||||||
ModemConfigChoice modemConfig;
|
|
||||||
|
|
||||||
/** pool is the pool we will alloc our rx packets from
|
/** 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
|
* 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;
|
virtual bool reconfigure() = 0;
|
||||||
|
|
||||||
protected:
|
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
|
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the
|
||||||
* PacketHeader & payload).
|
* PacketHeader & payload).
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "RadioLibInterface.h"
|
#include "RadioLibInterface.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include <configuration.h>
|
#include <configuration.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
@ -64,23 +65,27 @@ void RadioLibInterface::applyModemConfig()
|
|||||||
{
|
{
|
||||||
RadioInterface::applyModemConfig();
|
RadioInterface::applyModemConfig();
|
||||||
|
|
||||||
switch (modemConfig) {
|
if (channelSettings.spread_factor == 0) {
|
||||||
case Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
|
switch (channelSettings.modem_config) {
|
||||||
|
case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium
|
||||||
|
///< range
|
||||||
bw = 125;
|
bw = 125;
|
||||||
cr = 5;
|
cr = 5;
|
||||||
sf = 7;
|
sf = 7;
|
||||||
break;
|
break;
|
||||||
case Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
|
case ChannelSettings_ModemConfig_Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short
|
||||||
|
///< range
|
||||||
bw = 500;
|
bw = 500;
|
||||||
cr = 5;
|
cr = 5;
|
||||||
sf = 7;
|
sf = 7;
|
||||||
break;
|
break;
|
||||||
case Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
|
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long
|
||||||
|
///< range
|
||||||
bw = 31.25;
|
bw = 31.25;
|
||||||
cr = 8;
|
cr = 8;
|
||||||
sf = 9;
|
sf = 9;
|
||||||
break;
|
break;
|
||||||
case Bw125Cr48Sf4096:
|
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
|
||||||
bw = 125;
|
bw = 125;
|
||||||
cr = 8;
|
cr = 8;
|
||||||
sf = 12;
|
sf = 12;
|
||||||
@ -88,6 +93,14 @@ void RadioLibInterface::applyModemConfig()
|
|||||||
default:
|
default:
|
||||||
assert(0); // Unknown enum
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
|
14
src/power.h
14
src/power.h
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "concurrency/PeriodicTask.h"
|
|
||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
|
#include "concurrency/PeriodicTask.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per @spattinson
|
* Per @spattinson
|
||||||
@ -19,22 +19,22 @@ class Power : public concurrency::PeriodicTask
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Observable<const meshtastic::PowerStatus *> newStatus;
|
Observable<const meshtastic::PowerStatus *> newStatus;
|
||||||
|
|
||||||
void readPowerStatus();
|
void readPowerStatus();
|
||||||
void loop();
|
void loop();
|
||||||
virtual bool setup();
|
virtual bool setup();
|
||||||
virtual void doTask();
|
virtual void doTask();
|
||||||
void setStatusHandler(meshtastic::PowerStatus *handler)
|
void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; }
|
||||||
{
|
|
||||||
statusHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
meshtastic::PowerStatus *statusHandler;
|
meshtastic::PowerStatus *statusHandler;
|
||||||
virtual void axp192Init();
|
|
||||||
|
|
||||||
|
/// Setup a axp192, return true if found
|
||||||
|
bool axp192Init();
|
||||||
|
|
||||||
|
/// Setup a simple ADC input based battery sensor
|
||||||
|
bool analogInit();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Power *power;
|
extern Power *power;
|
Loading…
Reference in New Issue
Block a user