Merge pull request #689 from geeksville/dev

Dev
This commit is contained in:
Kevin Hester 2021-02-14 13:48:39 +08:00 committed by GitHub
commit c0fbfccf43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 168 additions and 84 deletions

2
proto

@ -1 +1 @@
Subproject commit b1aed06442025624841b2288fac273d9bc41c438 Subproject commit 0cadaed3953f66cf1edc99d0fa53e4fd5ebf56d6

View File

@ -9,6 +9,7 @@ enum class Cmd {
SET_OFF, SET_OFF,
ON_PRESS, ON_PRESS,
START_BLUETOOTH_PIN_SCREEN, START_BLUETOOTH_PIN_SCREEN,
START_FIRMWARE_UPDATE_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN, STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN, STOP_BOOT_SCREEN,
PRINT, PRINT,

View File

@ -7,6 +7,8 @@
#include "configuration.h" #include "configuration.h"
#include "nimble/BluetoothUtil.h" #include "nimble/BluetoothUtil.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "../graphics/Screen.h"
#include "../main.h"
#include <CRC32.h> #include <CRC32.h>
#include <Update.h> #include <Update.h>
@ -47,8 +49,9 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
// void stopMeshBluetoothService(); // void stopMeshBluetoothService();
// stopMeshBluetoothService(); // stopMeshBluetoothService();
screen->startFirmwareUpdateScreen();
if (RadioLibInterface::instance) if (RadioLibInterface::instance)
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
// writing flash - shut the radio off during updates // writing flash - shut the radio off during updates
} }
} }

View File

@ -164,6 +164,20 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
display->drawString(64 + x, 48 + y, buf); display->drawString(64 + x, 48 + y, buf);
} }
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(64 + x, y, "Updating");
display->setFont(FONT_SMALL);
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait...");
//display->setFont(FONT_LARGE);
//display->drawString(64 + x, 26 + y, btPIN);
}
/// Draw the last text message we received /// Draw the last text message we received
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@ -798,6 +812,9 @@ int32_t Screen::runOnce()
case Cmd::START_BLUETOOTH_PIN_SCREEN: case Cmd::START_BLUETOOTH_PIN_SCREEN:
handleStartBluetoothPinScreen(cmd.bluetooth_pin); handleStartBluetoothPinScreen(cmd.bluetooth_pin);
break; break;
case Cmd::START_FIRMWARE_UPDATE_SCREEN:
handleStartFirmwareUpdateScreen();
break;
case Cmd::STOP_BLUETOOTH_PIN_SCREEN: case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
case Cmd::STOP_BOOT_SCREEN: case Cmd::STOP_BOOT_SCREEN:
setFrames(); setFrames();
@ -928,6 +945,18 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
setFastFramerate(); setFastFramerate();
} }
void Screen::handleStartFirmwareUpdateScreen()
{
DEBUG_MSG("showing firmware screen\n");
showingNormalScreen = false;
static FrameCallback btFrames[] = {drawFrameFirmware};
ui.disableAllIndicators();
ui.setFrames(btFrames, 1);
setFastFramerate();
}
void Screen::blink() void Screen::blink()
{ {
setFastFramerate(); setFastFramerate();

View File

@ -128,6 +128,14 @@ class Screen : public concurrency::OSThread
enqueueCmd(cmd); enqueueCmd(cmd);
} }
void startFirmwareUpdateScreen()
{
ScreenCmd cmd;
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
enqueueCmd(cmd);
}
/// Stops showing the bluetooth PIN screen. /// Stops showing the bluetooth PIN screen.
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); } void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
@ -233,6 +241,7 @@ class Screen : public concurrency::OSThread
void handleOnPress(); void handleOnPress();
void handleStartBluetoothPinScreen(uint32_t pin); void handleStartBluetoothPinScreen(uint32_t pin);
void handlePrint(const char *text); void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen();
/// Rebuilds our list of frames (screens) to default ones. /// Rebuilds our list of frames (screens) to default ones.
void setFrames(); void setFrames();

View File

@ -51,22 +51,7 @@ MeshService service;
#include "Router.h" #include "Router.h"
static int32_t sendOwnerCb()
{
static uint32_t currentGeneration;
// If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
}
static concurrency::Periodic *sendOwnerPeriod;
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
{ {
@ -75,9 +60,6 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
void MeshService::init() void MeshService::init()
{ {
sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb);
sendOwnerPeriod->setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
// moved much earlier in boot (called from setup()) // moved much earlier in boot (called from setup())
// nodeDB.init(); // nodeDB.init();
@ -178,18 +160,6 @@ void MeshService::sendToMesh(MeshPacket *p)
{ {
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
// devices can get time.
if (p->which_payloadVariant == MeshPacket_decoded_tag && p->decoded.which_payloadVariant == SubPacket_position_tag &&
p->decoded.position.time) {
if (getRTCQuality() < RTCQualityGPS) {
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
p->decoded.position.time = 0;
} else
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
}
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
router->sendLocal(p); router->sendLocal(p);
} }
@ -221,7 +191,7 @@ NodeInfo *MeshService::refreshMyNodeInfo() {
Position &position = node->position; Position &position = node->position;
// 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)
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid. position.time = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
position.battery_level = powerStatus->getBatteryChargePercent(); position.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(position.battery_level); updateBatteryLevel(position.battery_level);
@ -244,7 +214,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
else { else {
// The GPS has lost lock, if we are fixed position we should just keep using // The GPS has lost lock, if we are fixed position we should just keep using
// the old position // the old position
if(!radioConfig.preferences.fixed_position) { if(radioConfig.preferences.fixed_position) {
DEBUG_MSG("WARNING: Using fixed position\n"); DEBUG_MSG("WARNING: Using fixed position\n");
} else { } else {
// throw away old position // throw away old position
@ -254,27 +224,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
} }
} }
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.time, pos.latitude_i, pos.battery_level);
// Update our current position in the local DB // Update our current position in the local DB
nodeDB.updatePosition(nodeDB.getNodeNum(), pos); nodeDB.updatePosition(nodeDB.getNodeNum(), pos);
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend;
uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
lastGpsSend = now;
static uint32_t currentGeneration;
// If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
assert(positionPlugin);
positionPlugin->sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
return 0; return 0;
} }

View File

@ -13,6 +13,7 @@ typedef uint32_t PacketId; // A packet sequence number
#define ERRNO_OK 0 #define ERRNO_OK 0
#define ERRNO_NO_INTERFACES 33 #define ERRNO_NO_INTERFACES 33
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
#define ERRNO_DISABLED 34 // the itnerface is disabled
/** /**
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket. * the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.

View File

@ -54,6 +54,8 @@ class RadioInterface
uint32_t shortPacketMsec; uint32_t shortPacketMsec;
protected: protected:
bool disabled = false;
float bw = 125; float bw = 125;
uint8_t sf = 9; uint8_t sf = 9;
uint8_t cr = 7; uint8_t cr = 7;
@ -98,6 +100,9 @@ class RadioInterface
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() { return true; } virtual bool sleep() { return true; }
/// Disable this interface (while disabled, no packets can be sent or received)
void disable() { disabled = true; sleep(); }
/** /**
* Send a packet (possibly by enquing in a private fifo). This routine will * Send a packet (possibly by enquing in a private fifo). This routine will
* later free() the packet to pool. This routine is not allowed to stall. * later free() the packet to pool. This routine is not allowed to stall.

View File

@ -2,8 +2,8 @@
#include "MeshTypes.h" #include "MeshTypes.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "SPILock.h" #include "SPILock.h"
#include "mesh-pb-constants.h"
#include "error.h" #include "error.h"
#include "mesh-pb-constants.h"
#include <configuration.h> #include <configuration.h>
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
@ -68,7 +68,6 @@ bool RadioLibInterface::canSendImmediately()
bool busyTx = sendingPacket != NULL; bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving(); bool busyRx = isReceiving && isActivelyReceiving();
if (busyTx || busyRx) { if (busyTx || busyRx) {
if (busyTx) if (busyTx)
DEBUG_MSG("Can not send yet, busyTx\n"); DEBUG_MSG("Can not send yet, busyTx\n");
@ -94,6 +93,11 @@ bool RadioLibInterface::canSendImmediately()
/// bluetooth comms code. If the txmit queue is empty it might return an error /// bluetooth comms code. If the txmit queue is empty it might return an error
ErrorCode RadioLibInterface::send(MeshPacket *p) ErrorCode RadioLibInterface::send(MeshPacket *p)
{ {
if (disabled) {
packetPool.release(p);
return ERRNO_DISABLED;
}
// Sometimes when testing it is useful to be able to never turn on the xmitter // Sometimes when testing it is useful to be able to never turn on the xmitter
#ifndef LORA_DISABLE_SENDING #ifndef LORA_DISABLE_SENDING
printPacket("enqueuing for send", p); printPacket("enqueuing for send", p);
@ -119,7 +123,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
return res; return res;
#else #else
packetPool.release(p); packetPool.release(p);
return ERRNO_UNKNOWN; return ERRNO_DISABLED;
#endif #endif
} }
@ -133,17 +137,17 @@ bool RadioLibInterface::canSleep()
} }
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) { bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
{
auto p = txQueue.remove(from, id); auto p = txQueue.remove(from, id);
if (p) if (p)
packetPool.release(p); // free the packet we just removed packetPool.release(p); // free the packet we just removed
bool result = (p != NULL); bool result = (p != NULL);
DEBUG_MSG("cancelSending id=0x%x, removed=%d", id, result); DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result);
return result; return result;
} }
/** radio helper thread callback. /** radio helper thread callback.
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
@ -296,6 +300,10 @@ void RadioLibInterface::handleReceiveInterrupt()
void RadioLibInterface::startSend(MeshPacket *txp) void RadioLibInterface::startSend(MeshPacket *txp)
{ {
printPacket("Starting low level send", txp); printPacket("Starting low level send", txp);
if (disabled) {
DEBUG_MSG("startSend is dropping tx packet because we are disabled\n");
packetPool.release(txp);
} else {
setStandby(); // Cancel any already in process receives setStandby(); // Cancel any already in process receives
configHardwareForSend(); // must be after setStandby configHardwareForSend(); // must be after setStandby
@ -308,3 +316,4 @@ void RadioLibInterface::startSend(MeshPacket *txp)
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrTxLevel0); enableInterrupt(isrTxLevel0);
} }
}

View File

@ -48,3 +48,26 @@ MeshPacket *NodeInfoPlugin::allocReply()
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
return allocDataProtobuf(u); return allocDataProtobuf(u);
} }
NodeInfoPlugin::NodeInfoPlugin()
: ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoPlugin")
{
setIntervalFromNow(30 *
1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
}
int32_t NodeInfoPlugin::runOnce()
{
static uint32_t currentGeneration;
// If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
return getPref_position_broadcast_secs() * 1000;
}

View File

@ -4,16 +4,17 @@
/** /**
* NodeInfo plugin for sending/receiving NodeInfos into the mesh * NodeInfo plugin for sending/receiving NodeInfos into the mesh
*/ */
class NodeInfoPlugin : public ProtobufPlugin<User> class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThread
{ {
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher /// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0; PacketId prevPacketId = 0;
uint32_t currentGeneration = 0;
public: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output
*/ */
NodeInfoPlugin() : ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields) {} NodeInfoPlugin();
/** /**
* Send our NodeInfo into the mesh * Send our NodeInfo into the mesh
@ -30,6 +31,9 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked /** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */ * so that subclasses can (optionally) send a response back to the original sender. */
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
/** Does our periodic broadcast */
virtual int32_t runOnce();
}; };
extern NodeInfoPlugin *nodeInfoPlugin; extern NodeInfoPlugin *nodeInfoPlugin;

View File

@ -7,6 +7,14 @@
PositionPlugin *positionPlugin; PositionPlugin *positionPlugin;
PositionPlugin::PositionPlugin()
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin")
{
setIntervalFromNow(60 *
1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
}
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p) bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
{ {
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us // FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
@ -32,7 +40,18 @@ MeshPacket *PositionPlugin::allocReply()
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
assert(node->has_position); assert(node->has_position);
return allocDataProtobuf(node->position); Position p = node->position;
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
// devices can get time.
if (getRTCQuality() < RTCQualityGPS) {
DEBUG_MSG("Stripping time %u from position send\n", p.time);
p.time = 0;
} else
DEBUG_MSG("Providing time to mesh %u\n", p.time);
return allocDataProtobuf(p);
} }
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
@ -50,3 +69,21 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
service.sendToMesh(p); service.sendToMesh(p);
} }
int32_t PositionPlugin::runOnce()
{
// We limit our GPS broadcasts to a max rate
uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) {
lastGpsSend = now;
// If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
return 5000; // to save power only wake for our callback occasionally
}

View File

@ -1,19 +1,26 @@
#pragma once #pragma once
#include "ProtobufPlugin.h" #include "ProtobufPlugin.h"
#include "concurrency/OSThread.h"
/** /**
* Position plugin for sending/receiving positions into the mesh * Position plugin for sending/receiving positions into the mesh
*/ */
class PositionPlugin : public ProtobufPlugin<Position> class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OSThread
{ {
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher /// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0; PacketId prevPacketId = 0;
/// We limit our GPS broadcasts to a max rate
uint32_t lastGpsSend = 0;
/// We force a rebroadcast if the radio settings change
uint32_t currentGeneration = 0;
public: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output
*/ */
PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields) {} PositionPlugin();
/** /**
* Send our position into the mesh * Send our position into the mesh
@ -31,6 +38,9 @@ class PositionPlugin : public ProtobufPlugin<Position>
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked /** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */ * so that subclasses can (optionally) send a response back to the original sender. */
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
/** Does our periodic broadcast */
virtual int32_t runOnce();
}; };
extern PositionPlugin *positionPlugin; extern PositionPlugin *positionPlugin;

View File

@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 1 major = 1
minor = 1 minor = 1
build = 46 build = 47