Merge pull request #314 from geeksville/master

my bug queue
This commit is contained in:
Kevin Hester 2020-08-12 11:11:01 -07:00 committed by GitHub
commit 5311e44660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 332 additions and 129 deletions

View File

@ -30,6 +30,8 @@ We currently support three models of radios.
- 3D printable cases
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
- Laser-cut cases
- [T-Beam V1](https://www.thingiverse.com/thing:4552771)
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
- version 2.1

View File

@ -1,11 +1,45 @@
#!/bin/bash
#!/bin/sh
set -e
# Usage info
show_help() {
cat << EOF
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-f FILENAME]
Flash image file to device, but first erasing and writing system information"
FILENAME=$1
-h Display this help and exit
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
-f FILENAME The .bin file to flash. Custom to your device type and region.
EOF
}
echo "Trying to flash $FILENAME, but first erasing and writing system information"
esptool.py --baud 921600 erase_flash
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
esptool.py --baud 921600 write_flash 0x10000 $FILENAME
while getopts ":h:p:f:" opt; do
case "${opt}" in
h)
show_help
exit 0
;;
p) export ESPTOOL_PORT=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
*)
echo "Invalid flag."
show_help >&2
exit 1
;;
esac
done
shift "$((OPTIND-1))"
if [ -f "${FILENAME}" ]; then
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
esptool.py --baud 921600 erase_flash
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
else
echo "Invalid file: ${FILENAME}"
show_help
fi
exit 0

View File

@ -1,8 +1,43 @@
#!/bin/bash
#!/bin/sh
set -e
# Usage info
show_help() {
cat << EOF
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] -f FILENAME
Flash image file to device, leave existing system intact."
FILENAME=$1
-h Display this help and exit
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
-f FILENAME The .bin file to flash. Custom to your device type and region.
EOF
}
echo "Trying to update $FILENAME"
esptool.py --baud 921600 write_flash 0x10000 $FILENAME
while getopts ":h:p:f:" opt; do
case "${opt}" in
h)
show_help
exit 0
;;
p) export ESPTOOL_PORT=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
*)
echo "Invalid flag."
show_help >&2
exit 1
;;
esac
done
shift "$((OPTIND-1))"
if [ -f "${FILENAME}" ]; then
echo "Trying to flash update ${FILENAME}."
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
else
echo "Invalid file: ${FILENAME}"
show_help
fi
exit 0

View File

@ -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](<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/
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.

2
proto

@ -1 +1 @@
Subproject commit 0523977d1f6c378cf78859044e2edbdba45150fa
Subproject commit c715e506df9ab1a76293d1c20dd5c904b009e1ea

View File

@ -25,13 +25,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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 */
@ -43,10 +43,14 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
static uint32_t targetFramerate = IDLE_FRAMERATE;
static char btPIN[16] = "888888";
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
// 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};
uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 };
// Threshold values for the GPS lock accuracy bar display
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];
#ifdef SHOW_REDRAWS
static bool heartbeat = false;
@ -116,7 +120,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
assert(mp.decoded.which_payload == SubPacket_data_tag);
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf);
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
}
/// Draw a series of fields in a column, wrapping to multiple colums if needed
@ -204,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);
}
@ -386,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++) {
@ -402,13 +400,13 @@ 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++) {
rosePoints[i]->rotate(myHeading);
rosePoints[i]->scale(COMPASS_DIAM);
rosePoints[i]->scale(-1 * COMPASS_DIAM);
rosePoints[i]->translate(compassX, compassY);
}
drawLine(display, N1, N3);
@ -436,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);
}
}
@ -473,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;
@ -495,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);
}
@ -594,6 +588,11 @@ void Screen::setup()
dispdev.flipScreenVertically();
#endif
// Get our hardware ID
uint8_t dmac[6];
getMacAddr(dmac);
sprintf(ourId, "%02x%02x", dmac[4], dmac[5]);
// Turn on the display.
handleSetOn(true);
@ -770,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())
@ -783,8 +782,13 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
}
// Draw the channel name
display->drawString(x, y + FONT_HEIGHT, channelStr);
// Draw our hardware ID to assist with bluetooth pairing
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT, 8, 8, imgInfo);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT, ourId);
// Draw any log messages
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
@ -810,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;

View File

@ -6,11 +6,13 @@ const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
const uint8_t imgSatellite[] PROGMEM = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C };
const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22 };
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF };
#include "img/icon.xbm"

View File

@ -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)

View File

@ -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
@ -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()
@ -93,7 +120,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");

View File

@ -1,12 +1,12 @@
#pragma once
#include "Observer.h"
#include <Arduino.h>
#include <assert.h>
#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();

View File

@ -115,22 +115,28 @@ 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
// 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;
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,
channelSettings.tx_power);
power);
}
ErrorCode SimRadio::send(MeshPacket *p)

View File

@ -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).

View File

@ -1,5 +1,6 @@
#include "RadioLibInterface.h"
#include "MeshTypes.h"
#include "NodeDB.h"
#include "mesh-pb-constants.h"
#include <configuration.h>
#include <pb_decode.h>
@ -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;
}
}

View File

@ -14,10 +14,12 @@ bool SX1262Interface::init()
{
RadioLibInterface::init();
#ifdef SX1262_RXEN // set not rx or tx mode
#ifdef SX1262_RXEN // set not rx or tx mode
digitalWrite(SX1262_RXEN, LOW); // Set low before becoming an output
pinMode(SX1262_RXEN, OUTPUT);
#endif
#ifdef SX1262_TXEN
digitalWrite(SX1262_TXEN, LOW);
pinMode(SX1262_TXEN, OUTPUT);
#endif

View File

@ -128,7 +128,25 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define PIN_WIRE_SDA (32 + 2)
#define PIN_WIRE_SCL (32)
#define GPS_I2C_ADDR FIXME
// CUSTOM GPIOs the SX1262
#define SX1262_CS (10)
#define SX1262_DIO1 (20)
#define SX1262_DIO2 (26)
#define SX1262_BUSY (31) // Supposed to be P0.18 but because of reworks, now on P0.31 (18)
#define SX1262_RESET (17)
// #define SX1262_ANT_SW (32 + 10)
#define SX1262_RXEN (22)
#define SX1262_TXEN (24)
#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
// ERC12864-10 LCD
#define ERC12864_CS (32 + 4)
#define ERC12864_RESET (32 + 6)
#define ERC12864_CD (32 + 9)
// L80 GPS
#define L80_PPS (28)
#define L80_RESET (29)
#ifdef __cplusplus
}