Merge pull request #512 from geeksville/dev

Dev
This commit is contained in:
Kevin Hester 2020-11-07 04:19:36 -08:00 committed by GitHub
commit 21751da5a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 179 additions and 97 deletions

View File

@ -1,3 +1,3 @@
export VERSION=1.1.7 export VERSION=1.1.8

View File

@ -1,7 +1,7 @@
{ {
"build": { "build": {
"arduino": { "arduino": {
"ldscript": "nrf52840_s140_v6.ld" "ldscript": "nrf52840_s113_v7.ld"
}, },
"core": "nRF5", "core": "nRF5",
"cpu": "cortex-m4", "cpu": "cortex-m4",
@ -16,9 +16,9 @@
"name": "adafruit" "name": "adafruit"
}, },
"softdevice": { "softdevice": {
"sd_flags": "-DS140", "sd_flags": "-DS113",
"sd_name": "s140", "sd_name": "s113",
"sd_version": "6.1.1", "sd_version": "7.2.0",
"sd_fwid": "0x00B6" "sd_fwid": "0x00B6"
}, },
"bootloader": { "bootloader": {

View File

@ -2,15 +2,11 @@
You probably don't care about this section - skip to the next one. You probably don't care about this section - skip to the next one.
Threading tasks: For high speed/lots of devices/short range tasks:
- Use https://github.com/ivanseidel/ArduinoThread? rather than full coroutines - When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1.
- clean up main loop() This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range.
- check that we are mostly asleep, show which thread is causing us to wake - fix timeouts/delays to be based on packet length at current radio settings
-
- use tickless idle on nrf52, and sleep X msec or until an interrupt occurs or the cooperative scheduling changes. https://devzone.nordicsemi.com/f/nordic-q-a/12363/nrf52-freertos-power-consumption-tickless-idle
- BAD IDEA: use vTaskDelay and https://www.freertos.org/xTaskAbortDelay.html if scheduling changes. (define INCLUDE_xTaskAbortDelay on ESP32 and NRF52 - seems impossible to find?)
- GOOD IDEA: use xSemaphoreTake to take a semaphore using a timeout. Expect semaphore to not be set, but set it to indicate scheduling has changed.
Nimble tasks: Nimble tasks:

View File

@ -14,6 +14,13 @@ Notes here on using that driver: https://www.linuxquestions.org/questions/linux-
Or if **absolutely** necessary could bitbang: https://www.cnx-software.com/2018/02/16/wch-ch341-usb-to-serial-chip-gets-linux-driver-to-control-gpios-over-usb/ Or if **absolutely** necessary could bitbang: https://www.cnx-software.com/2018/02/16/wch-ch341-usb-to-serial-chip-gets-linux-driver-to-control-gpios-over-usb/
## Portduino tasks
* How to access spi devices via ioctl (spidev): https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md#:~:text=Troubleshooting-,Overview,bus)%2C%20UARTs%2C%20etc.
* access gpio via libgpiod?
* Use dkms to distribute driver?
* echo 100 > /sys/module/spi_ch341_usb/parameters/poll_period
## Task list ## Task list
* Port meshtastic to build (under platformio) for a poxix target. spec: no screen, no gpios, sim network interface, posix threads, posix semaphores & queues, IO to the console only * Port meshtastic to build (under platformio) for a poxix target. spec: no screen, no gpios, sim network interface, posix threads, posix semaphores & queues, IO to the console only

View File

@ -251,13 +251,11 @@ void PowerFSM_setup()
#ifndef NRF52_SERIES #ifndef NRF52_SERIES
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
lowPowerState = &stateDARK;
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout"); powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout"); powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout"); powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
#else
lowPowerState = &stateDARK;
#endif #endif
auto meshSds = getPref_mesh_sds_timeout_secs(); auto meshSds = getPref_mesh_sds_timeout_secs();

View File

@ -9,6 +9,7 @@
#include "utils.h" #include "utils.h"
#include <nvs.h> #include <nvs.h>
#include <nvs_flash.h> #include <nvs_flash.h>
#include <driver/rtc_io.h>
void getMacAddr(uint8_t *dmac) void getMacAddr(uint8_t *dmac)
{ {
@ -87,3 +88,57 @@ void esp32Loop()
// for debug printing // for debug printing
// radio.radioIf.canSleep(); // radio.radioIf.canSleep();
} }
void cpuDeepSleep(uint64_t msecToWake)
{
/*
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
If an external circuit drives this pin in deep sleep mode, current consumption may
increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
some current will flow through these external and internal resistors, increasing deep
sleep current above the minimal possible value.
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
13,
/* 14, */ /* 15, */
#endif
/* 25, */ 26, /* 27, */
32, 33, 34, 35,
36, 37
/* 38, 39 */};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
uint64_t gpioMask = (1ULL << BUTTON_PIN);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}

View File

@ -25,9 +25,15 @@ uint8_t GPS::i2cAddress = 0;
GPS *gps; GPS *gps;
/// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once.
static bool didSerialInit;
bool GPS::setupGPS() bool GPS::setupGPS()
{ {
if (_serial_gps) { if (_serial_gps && !didSerialInit) {
didSerialInit = true;
#ifdef GPS_RX_PIN #ifdef GPS_RX_PIN
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else #else
@ -59,8 +65,10 @@ bool GPS::setup()
setAwake(true); // Wake GPS power before doing any init setAwake(true); // Wake GPS power before doing any init
bool ok = setupGPS(); bool ok = setupGPS();
if (ok) if (ok) {
notifySleepObserver.observe(&notifySleep); notifySleepObserver.observe(&notifySleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
}
return ok; return ok;
} }
@ -275,7 +283,19 @@ void GPS::forceWake(bool on)
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareSleep(void *unused) int GPS::prepareSleep(void *unused)
{ {
DEBUG_MSG("GPS prepare sleep!\n");
forceWake(false); forceWake(false);
return 0; return 0;
} }
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareDeepSleep(void *unused)
{
DEBUG_MSG("GPS deep sleep!\n");
// For deep sleep we also want abandon any lock attempts (because we want minimum power)
setAwake(false);
return 0;
}

View File

@ -30,6 +30,7 @@ class GPS : private concurrency::OSThread
uint8_t numSatellites = 0; uint8_t numSatellites = 0;
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep); CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
public: public:
/** If !NULL we will use this serial port to construct our GPS */ /** If !NULL we will use this serial port to construct our GPS */
@ -115,6 +116,10 @@ class GPS : private concurrency::OSThread
/// always returns 0 to indicate okay to sleep /// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused); int prepareSleep(void *unused);
/// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareDeepSleep(void *unused);
/** /**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode * Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
* *

View File

@ -125,6 +125,12 @@ bool RadioInterface::init()
return true; return true;
} }
int RadioInterface::notifyDeepSleepCb(void *unused)
{
sleep();
return 0;
}
/** hash a string into an integer /** hash a string into an integer
* *
* djb2 by Dan Bernstein. * djb2 by Dan Bernstein.

View File

@ -48,7 +48,7 @@ class RadioInterface
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::preflightSleepCb); CallbackObserver<RadioInterface, void *>(this, &RadioInterface::preflightSleepCb);
CallbackObserver<RadioInterface, void *> notifyDeepSleepObserver = CallbackObserver<RadioInterface, void *> notifyDeepSleepObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::notifyDeepSleepDb); CallbackObserver<RadioInterface, void *>(this, &RadioInterface::notifyDeepSleepCb);
protected: protected:
MeshPacket *sendingPacket = NULL; // The packet we are currently sending MeshPacket *sendingPacket = NULL; // The packet we are currently sending
@ -136,11 +136,7 @@ class RadioInterface
/// Return 0 if sleep is okay /// Return 0 if sleep is okay
int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; } int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; }
int notifyDeepSleepDb(void *unused = NULL) int notifyDeepSleepCb(void *unused = NULL);
{
sleep();
return 0;
}
int reloadConfig(void *unused) int reloadConfig(void *unused)
{ {

View File

@ -236,20 +236,25 @@ void RadioLibInterface::startTransmitTimer(bool withDelay)
void RadioLibInterface::handleTransmitInterrupt() void RadioLibInterface::handleTransmitInterrupt()
{ {
// DEBUG_MSG("handling lora TX interrupt\n"); // DEBUG_MSG("handling lora TX interrupt\n");
assert(sendingPacket); // Were we sending? - FIXME, this was null coming out of light sleep due to RF95 ISR! // This can be null if we forced the device to enter standby mode. In that case
// ignore the transmit interrupt
if(sendingPacket)
completeSending(); completeSending();
} }
void RadioLibInterface::completeSending() void RadioLibInterface::completeSending()
{ {
if (sendingPacket) { // We are careful to clear sending packet before calling printPacket because
// that can take a long time
auto p = sendingPacket;
sendingPacket = NULL;
if (p) {
txGood++; txGood++;
printPacket("Completed sending", sendingPacket); printPacket("Completed sending", p);
// We are done sending that packet, release it // We are done sending that packet, release it
packetPool.release(sendingPacket); packetPool.release(p);
sendingPacket = NULL;
// DEBUG_MSG("Done with send\n"); // DEBUG_MSG("Done with send\n");
} }
} }

View File

@ -201,9 +201,21 @@ bool SX1262Interface::isActivelyReceiving()
bool SX1262Interface::sleep() bool SX1262Interface::sleep()
{ {
// put chipset into sleep mode // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
disableInterrupt(); DEBUG_MSG("sx1262 entering sleep mode (FIXME, don't keep config)\n");
lora.sleep(); setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
// FIXME - this isn't correct
// lora.setTCXO(0);
// put chipset into sleep mode (we've already disabled interrupts by now)
bool keepConfig = true;
lora.sleep(keepConfig); // Note: we do not keep the config, full reinit will be needed
#ifdef SX1262_POWER_EN
digitalWrite(SX1262_POWER_EN, LOW);
#endif
return true; return true;
} }

View File

@ -202,6 +202,13 @@ void setupMeshService(void)
// FIXME, turn off soft device access for debugging // FIXME, turn off soft device access for debugging
static bool isSoftDeviceAllowed = true; static bool isSoftDeviceAllowed = true;
void NRF52Bluetooth::shutdown()
{
// Shutdown bluetooth for minimum power draw
DEBUG_MSG("Disable NRF52 bluetooth\n");
Bluefruit.Advertising.stop();
}
void NRF52Bluetooth::setup() void NRF52Bluetooth::setup()
{ {
// Initialise the Bluefruit module // Initialise the Bluefruit module

View File

@ -4,5 +4,6 @@ class NRF52Bluetooth
{ {
public: public:
void setup(); void setup();
void shutdown();
}; };

View File

@ -49,7 +49,7 @@ void getMacAddr(uint8_t *dmac)
NRF52Bluetooth *nrf52Bluetooth; NRF52Bluetooth *nrf52Bluetooth;
static bool bleOn = false; static bool bleOn = false;
static const bool enableBle = true; // Set to false for easier debugging static const bool enableBle = false; // Set to false for easier debugging
void setBluetoothEnable(bool on) void setBluetoothEnable(bool on)
{ {
@ -64,7 +64,8 @@ void setBluetoothEnable(bool on)
} }
} }
} else { } else {
DEBUG_MSG("FIXME: implement BLE disable\n"); if(nrf52Bluetooth)
nrf52Bluetooth->shutdown();
} }
bleOn = on; bleOn = on;
} }
@ -110,3 +111,21 @@ void nrf52Setup()
DEBUG_MSG("FIXME, call randomSeed\n"); DEBUG_MSG("FIXME, call randomSeed\n");
// ::printf("TESTING PRINTF\n"); // ::printf("TESTING PRINTF\n");
} }
void cpuDeepSleep(uint64_t msecToWake)
{
DEBUG_MSG("FIXME: implement NRF52 deep sleep enter actions\n");
// FIXME, configure RTC to wake us
// FIXME, power down SPI, I2C, RAMs
// FIXME, use system off mode with ram retention for key state?
// FIXME, use non-init RAM per https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
while(1) {
delay(5000);
DEBUG_MSG(".");
}
// FIXME, after wake power up SPI, I2C, RAMs, reinit LORA
DEBUG_MSG("FIXME: implement NRF52 deep sleep wake actions\n");
}

View File

@ -1,6 +1,7 @@
#include "CryptoEngine.h" #include "CryptoEngine.h"
#include "target_specific.h" #include "target_specific.h"
#include <Utility.h> #include <Utility.h>
#include "sleep.h"
// FIXME - move getMacAddr/setBluetoothEnable into a HALPlatform class // FIXME - move getMacAddr/setBluetoothEnable into a HALPlatform class
@ -26,6 +27,10 @@ void setBluetoothEnable(bool on)
notImplemented("setBluetoothEnable"); notImplemented("setBluetoothEnable");
} }
void cpuDeepSleep(uint64_t msecs) {
notImplemented("cpuDeepSleep");
}
// FIXME - implement real crypto for linux // FIXME - implement real crypto for linux
CryptoEngine *crypto = new CryptoEngine(); CryptoEngine *crypto = new CryptoEngine();

View File

@ -144,17 +144,20 @@ void doDeepSleep(uint64_t msecToWake)
{ {
DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000); DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000);
#ifndef NO_ESP32
// not using wifi yet, but once we are this is needed to shutoff the radio hw // not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop(); // esp_wifi_stop();
waitEnterSleep(); waitEnterSleep();
notifySleep.notifyObservers(NULL); // also tell the regular sleep handlers
notifyDeepSleep.notifyObservers(NULL); notifyDeepSleep.notifyObservers(NULL);
screen->setOn(false); // datasheet says this will draw only 10ua screen->setOn(false); // datasheet says this will draw only 10ua
nodeDB.saveToDisk(); nodeDB.saveToDisk();
// Kill GPS power completely (even if previously we just had it in sleep mode)
setGPSPower(false);
setLed(false);
#ifdef RESET_OLED #ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif #endif
@ -163,11 +166,6 @@ void doDeepSleep(uint64_t msecToWake)
digitalWrite(VEXT_ENABLE, 1); // turn off the display power digitalWrite(VEXT_ENABLE, 1); // turn off the display power
#endif #endif
// Kill GPS power completely (even if previously we just had it in sleep mode)
setGPSPower(false);
setLed(false);
#ifdef TBEAM_V10 #ifdef TBEAM_V10
if (axp192_found) { if (axp192_found) {
// Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep. // Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep.
@ -185,57 +183,7 @@ void doDeepSleep(uint64_t msecToWake)
} }
#endif #endif
/* cpuDeepSleep(msecToWake);
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
If an external circuit drives this pin in deep sleep mode, current consumption may
increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
some current will flow through these external and internal resistors, increasing deep
sleep current above the minimal possible value.
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
13,
/* 14, */ /* 15, */
#endif
/* 25, */ 26, /* 27, */
32, 33, 34, 35,
36, 37
/* 38, 39 */};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
uint64_t gpioMask = (1ULL << BUTTON_PIN);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
#endif
} }
#ifndef NO_ESP32 #ifndef NO_ESP32

View File

@ -4,7 +4,8 @@
#include "Observer.h" #include "Observer.h"
#include "configuration.h" #include "configuration.h"
void doDeepSleep(uint64_t msecToWake); void doDeepSleep(uint64_t msecToWake), cpuDeepSleep(uint64_t msecToWake);
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "esp_sleep.h" #include "esp_sleep.h"
esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake); esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake);

View File

@ -205,6 +205,7 @@ External serial flash WP25R1635FZUIL0
#define PIN_EINK_MOSI (0 + 29) // also called SDI #define PIN_EINK_MOSI (0 + 29) // also called SDI
// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON // Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON
// FIXME - I think this is actually just the board power enable - it enables power to the CPU also
#define PIN_EINK_PWR_ON (0 + 12) #define PIN_EINK_PWR_ON (0 + 12)
#define HAS_EINK #define HAS_EINK
@ -242,7 +243,7 @@ External serial flash WP25R1635FZUIL0
#define PIN_SPI_SCK (0 + 19) #define PIN_SPI_SCK (0 + 19)
// To debug via the segger JLINK console rather than the CDC-ACM serial device // To debug via the segger JLINK console rather than the CDC-ACM serial device
// #define USE_SEGGER #define USE_SEGGER
#ifdef __cplusplus #ifdef __cplusplus
} }