don't turn bluetooth back on every time we exit light sleep

This commit is contained in:
geeksville 2020-02-21 08:09:07 -08:00
parent 7a745c9e65
commit 132e54ecc5
7 changed files with 74 additions and 150 deletions

View File

@ -2,7 +2,9 @@
Items to complete before the first alpha release. Items to complete before the first alpha release.
* implement CustomRF95::canSleep
* document rules for sleep wrt lora/bluetooth/screen/gps. also: if I have text messages (only) for the phone, then give a few seconds in the hopes BLE can get it across before we have to go back to sleep. * document rules for sleep wrt lora/bluetooth/screen/gps. also: if I have text messages (only) for the phone, then give a few seconds in the hopes BLE can get it across before we have to go back to sleep.
* wake from light sleep as needed for our next scheduled periodic task (needed for gps position broadcasts etc)
* if the phone doesn't read fromradio mailbox within X seconds, assume the phone is gone and we can stop queing location msgs * if the phone doesn't read fromradio mailbox within X seconds, assume the phone is gone and we can stop queing location msgs
for it (because it will redownload the nodedb when it comes back) for it (because it will redownload the nodedb when it comes back)
* don't enter light sleep while the screen is on * don't enter light sleep while the screen is on

View File

@ -18,6 +18,20 @@ CustomRF95::CustomRF95(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &
{ {
} }
bool CustomRF95::canSleep()
{
return (_mode == RHModeIdle || _mode == RHModeRx) && txQueue.isEmpty(); // FIXME - also check if we have started receiving
}
bool CustomRF95::sleep()
{
// we no longer care about interrupts from this device
prepareDeepSleep();
// FIXME - leave the device state in rx mode instead
return RH_RF95::sleep();
}
bool CustomRF95::init() bool CustomRF95::init()
{ {
bool ok = RH_RF95::init(); bool ok = RH_RF95::init();

View File

@ -26,6 +26,16 @@ public:
*/ */
CustomRF95(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest); CustomRF95(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
* This method must be used before putting the CPU into deep or light sleep.
*/
bool canSleep();
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool 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 because it is called from /// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// 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

View File

@ -109,138 +109,15 @@ void MeshRadio::reloadConfig()
rf95.setModeRx(); rf95.setModeRx();
} }
void MeshRadio::sleep()
{
// we no longer care about interrupts from this device
rf95.prepareDeepSleep();
// FIXME - leave the device state in rx mode instead
rf95.sleep();
}
ErrorCode MeshRadio::send(MeshPacket *p) ErrorCode MeshRadio::send(MeshPacket *p)
{ {
#if 1
return rf95.send(p); return rf95.send(p);
#else
DEBUG_MSG("enquing packet for send from=0x%x, to=0x%x\n", p->from, p->to);
return txQueue.enqueue(p, 0); // nowait
#endif
} }
#if 0
ErrorCode MeshRadio::sendTo(NodeNum dest, const uint8_t *buf, size_t len)
{
// We must do this before each send, because we might have just changed our nodenum
manager.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor time.
assert(len <= 251); // Make sure we don't overflow the tiny max packet size
uint32_t start = millis();
// Note: we don't use sendToWait here because we don't want to wait and for the time being don't require
// reliable delivery
// return manager.sendtoWait((uint8_t *) buf, len, dest);
ErrorCode res = manager.sendto((uint8_t *)buf, len, dest) ? ERRNO_OK : ERRNO_UNKNOWN;
// FIXME, we have to wait for sending to complete before freeing the buffer, otherwise it might get wiped
// instead just have the radiohead layer understand queues.
if (res == ERRNO_OK)
manager.waitPacketSent();
DEBUG_MSG("mesh sendTo %d bytes to 0x%x (%lu msecs)\n", len, dest, millis() - start);
return res;
}
/// enqueue a received packet in rxDest
void MeshRadio::handleReceive(MeshPacket *mp)
{
int res = rxDest.enqueue(mp, 0); // NOWAIT - fixme, if queue is full, delete older messages
assert(res == pdTRUE);
}
#endif
void MeshRadio::loop() void MeshRadio::loop()
{ {
// FIXME read from radio with recvfromAckTimeout // Currently does nothing, since we do it all in ISRs now
#if 0
static int16_t packetnum = 0; // packet counter, we increment per xmission
char radiopacket[20] = "Hello World # ";
sprintf(radiopacket, "hello %d", packetnum++);
assert(sendTo(NODENUM_BROADCAST, (uint8_t *)radiopacket, sizeof(radiopacket)) == ERRNO_OK);
#endif
#if 0
/// A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
#define MAX_RHPACKETLEN 251
static uint8_t radiobuf[MAX_RHPACKETLEN];
uint8_t rxlen;
uint8_t srcaddr, destaddr, id, flags;
// Poll to see if we've received a packet
// if (manager.recvfromAckTimeout(radiobuf, &rxlen, 0, &srcaddr, &destaddr, &id, &flags))
// prefill rxlen with the max length we can accept - very important
rxlen = (uint8_t) MAX_RHPACKETLEN;
if (manager.recvfrom(radiobuf, &rxlen, &srcaddr, &destaddr, &id, &flags))
{
// We received a packet
int32_t freqerr = rf95.frequencyError(), snr = rf95.lastSNR();
DEBUG_MSG("Received packet from mesh src=0x%x,dest=0x%x,id=%d,len=%d rxGood=%d,rxBad=%d,freqErr=%d,snr=%d\n",
srcaddr, destaddr, id, rxlen, rf95.rxGood(), rf95.rxBad(), freqerr, snr);
MeshPacket *mp = pool.allocZeroed();
SubPacket *p = &mp->payload;
mp->from = srcaddr;
mp->to = destaddr;
// If we already have an entry in the DB for this nodenum, goahead and hide the snr/freqerr info there.
// Note: we can't create it at this point, because it might be a bogus User node allocation. But odds are we will
// already have a record we can hide this debugging info in.
NodeInfo *info = nodeDB.getNode(mp->from);
if (info)
{
info->snr = snr;
info->frequency_error = freqerr;
}
if (!pb_decode_from_bytes(radiobuf, rxlen, SubPacket_fields, p))
{
pool.release(mp);
}
else
{
// parsing was successful, queue for our recipient
mp->has_payload = true;
handleReceive(mp);
}
}
#endif
#if 0
// Poll to see if we need to send any packets
MeshPacket *txp = txQueue.dequeuePtr(0); // nowait
if (txp)
{
DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
assert(txp->has_payload);
size_t numbytes = pb_encode_to_bytes(radiobuf, sizeof(radiobuf), SubPacket_fields, &txp->payload);
int res = sendTo(txp->to, radiobuf, numbytes);
assert(res == ERRNO_OK);
bool loopbackTest = false; // if true we will pretend to receive any packets we just sent
if (loopbackTest)
handleReceive(txp);
else
pool.release(txp);
DEBUG_MSG("Done with send\n");
}
#endif
} }

View File

@ -55,6 +55,8 @@
*/ */
class MeshRadio { class MeshRadio {
public: public:
CustomRF95 rf95; // the raw radio interface - for now I'm leaving public - because this class is shrinking to be almost nothing
/** 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
*/ */
@ -62,9 +64,6 @@ public:
bool init(); bool init();
/// Prepare the radio to enter sleep mode, where it should draw only 0.2 uA
void 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 because it is called from /// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// 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
@ -78,7 +77,6 @@ public:
void reloadConfig(); void reloadConfig();
private: private:
CustomRF95 rf95; // the raw radio interface
// RHDatagram manager; // RHDatagram manager;
// RHReliableDatagram manager; // don't use mesh yet // RHReliableDatagram manager; // don't use mesh yet

View File

@ -29,6 +29,11 @@ public:
return uxQueueSpacesAvailable(h); return uxQueueSpacesAvailable(h);
} }
bool isEmpty()
{
return uxQueueMessagesWaiting(h) == 0;
}
// pdTRUE for success else failure // pdTRUE for success else failure
BaseType_t enqueue(T x, TickType_t maxWait = portMAX_DELAY) BaseType_t enqueue(T x, TickType_t maxWait = portMAX_DELAY)
{ {

View File

@ -35,6 +35,7 @@
#include "Periodic.h" #include "Periodic.h"
#include "esp32/pm.h" #include "esp32/pm.h"
#include "esp_pm.h" #include "esp_pm.h"
#include "MeshRadio.h"
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
#include "axp20x.h" #include "axp20x.h"
@ -71,7 +72,8 @@ void setCPUFast(bool on)
setCpuFrequencyMhz(on ? 240 : 80); setCpuFrequencyMhz(on ? 240 : 80);
} }
static void setLed(bool ledOn) { static void setLed(bool ledOn)
{
#ifdef LED_PIN #ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing // toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn); digitalWrite(LED_PIN, ledOn);
@ -98,7 +100,7 @@ void doDeepSleep(uint64_t msecToWake)
screen_off(); // datasheet says this will draw only 10ua screen_off(); // datasheet says this will draw only 10ua
// Put radio in sleep mode (will still draw power but only 0.2uA) // Put radio in sleep mode (will still draw power but only 0.2uA)
service.radio.sleep(); service.radio.rf95.sleep();
nodeDB.saveToDisk(); nodeDB.saveToDisk();
@ -110,7 +112,7 @@ 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
setLed(false); setLed(false);
#ifdef T_BEAM_V10 #ifdef T_BEAM_V10
if (axp192_found) if (axp192_found)
@ -175,6 +177,32 @@ setLed(false);
#include "esp_bt_main.h" #include "esp_bt_main.h"
bool bluetoothOn = true; // we turn it on during setup() so default on
void setBluetoothEnable(bool on)
{
if (on != bluetoothOn)
{
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
bluetoothOn = on;
if (on)
{
if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK)
DEBUG_MSG("error reenabling bt controller\n");
if (esp_bluedroid_enable() != ESP_OK)
DEBUG_MSG("error reenabling bluedroid\n");
}
else
{
if (esp_bluedroid_disable() != ESP_OK)
DEBUG_MSG("error disabling bluedroid\n");
if (esp_bt_controller_disable() != ESP_OK)
DEBUG_MSG("error disabling bt controller\n");
}
}
}
/** /**
* enter light sleep (preserves ram but stops everything about CPU). * enter light sleep (preserves ram but stops everything about CPU).
* *
@ -187,26 +215,20 @@ void doLightSleep(uint32_t sleepMsec = 20 * 1000) // FIXME, use a more reasonabl
setLed(false); // Never leave led on while in light sleep setLed(false); // Never leave led on while in light sleep
// ESP docs say we must disable bluetooth and wifi before light sleep // NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
if (esp_bluedroid_disable() != ESP_OK)
DEBUG_MSG("error disabling bluedroid\n");
if (esp_bt_controller_disable() != ESP_OK)
DEBUG_MSG("error disabling bt controller\n");
// We want RTC peripherals to stay on // We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low
gpio_wakeup_enable((gpio_num_t)DIO0_GPIO, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high gpio_wakeup_enable((gpio_num_t)DIO0_GPIO, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high
#ifdef PMU_IRQ
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif
esp_sleep_enable_gpio_wakeup(); esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup(sleepUsec); esp_sleep_enable_timer_wakeup(sleepUsec);
esp_light_sleep_start(); esp_light_sleep_start();
DEBUG_MSG("Exit light sleep\n"); DEBUG_MSG("Exit light sleep\n");
if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK)
DEBUG_MSG("error reenabling bt controller\n");
if (esp_bluedroid_enable() != ESP_OK)
DEBUG_MSG("error reenabling bluedroid\n");
} }
/** /**
@ -470,10 +492,8 @@ void setup()
serve->getAdvertising()->start(); serve->getAdvertising()->start();
} }
// enableModemSleep(); setBluetoothEnable(false);
setCPUFast(false); setCPUFast(true); // FIXME, switch to low speed now
// doLightSleep();
} }
uint32_t ledBlinker() uint32_t ledBlinker()
@ -483,7 +503,6 @@ uint32_t ledBlinker()
setLed(ledOn); setLed(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that // have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return isCharging ? 1000 : (ledOn ? 2 : 1000); return isCharging ? 1000 : (ledOn ? 2 : 1000);
} }
@ -594,13 +613,12 @@ void loop()
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons feel slow // FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons feel slow
msecstosleep = 10; msecstosleep = 10;
bool bluetoothOn = false; // FIXME, leave bluetooth on per our power management policy (see doc)
// while we have bluetooth on, we can't do light sleep, but once off stay in light_sleep all the time // while we have bluetooth on, we can't do light sleep, but once off stay in light_sleep all the time
// we will wake from light sleep on button press or interrupt from the RF95 radio // we will wake from light sleep on button press or interrupt from the RF95 radio
if (!bluetoothOn && !is_screen_on()) if (!bluetoothOn && !is_screen_on() && service.radio.rf95.canSleep())
doLightSleep(60 * 1000); // FIXME, wake up to briefly flash led, then go back to sleep (without repowering bluetooth) doLightSleep(60 * 1000); // FIXME, wake up to briefly flash led, then go back to sleep (without repowering bluetooth)
else { else
{
delay(msecstosleep); delay(msecstosleep);
} }
} }