mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-25 01:42:15 +00:00
don't turn bluetooth back on every time we exit light sleep
This commit is contained in:
parent
7a745c9e65
commit
132e54ecc5
2
TODO.md
2
TODO.md
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
62
src/main.ino
62
src/main.ino
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user