Merge branch 'eink' into bug513

This commit is contained in:
Kevin Hester 2020-11-13 09:41:23 +08:00
commit f346b4f0f2
15 changed files with 100 additions and 28 deletions

Binary file not shown.

View File

@ -1,4 +1,5 @@
#include "RedirectablePrint.h" #include "RedirectablePrint.h"
#include "configuration.h"
#include <assert.h> #include <assert.h>
/** /**
@ -10,4 +11,14 @@ void RedirectablePrint::setDestination(Print *_dest)
{ {
assert(_dest); assert(_dest);
dest = _dest; dest = _dest;
}
size_t RedirectablePrint::write(uint8_t c)
{
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
#endif
dest->write(c);
return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero)
} }

View File

@ -19,7 +19,7 @@ class RedirectablePrint : public Print
*/ */
void setDestination(Print *dest); void setDestination(Print *dest);
virtual size_t write(uint8_t c) { return dest->write(c); } virtual size_t write(uint8_t c);
}; };
class NoopPrint : public Print class NoopPrint : public Print

View File

@ -404,8 +404,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Always include the SEGGER code on NRF52 - because useful for debugging // Always include the SEGGER code on NRF52 - because useful for debugging
#include "SEGGER_RTT.h" #include "SEGGER_RTT.h"
// The channel we send stdout data to
#define SEGGER_STDOUT_CH 0
// Debug printing to segger console // Debug printing to segger console
#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define SEGGER_MSG(...) SEGGER_RTT_printf(SEGGER_STDOUT_CH, __VA_ARGS__)
// If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST // If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST
// use SEGGER for debug output // use SEGGER for debug output

View File

@ -85,13 +85,16 @@ static uint16_t displayWidth, displayHeight;
#define SCREEN_TRANSITION_MSECS 300 #define SCREEN_TRANSITION_MSECS 300
#endif #endif
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) /**
* Draw the icon with extra info printed around the corners
*/
static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
// draw an xbm image. // draw an xbm image.
// Please note that everything that should be transitioned // Please note that everything that should be transitioned
// needs to be drawn relative to x and y // needs to be drawn relative to x and y
// draw centered left to right and centered above the one line of app text // draw centered icon left to right and centered above the one line of app text
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2, display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
icon_width, icon_height, (const uint8_t *)icon_bits); icon_width, icon_height, (const uint8_t *)icon_bits);
@ -101,15 +104,31 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
const char *region = myRegion ? myRegion->name : NULL; // Draw region in upper left
if (region) if (upperMsg)
display->drawString(x + 0, y + 0, region); display->drawString(x + 0, y + 0, upperMsg);
// Draw version in upper right
char buf[16]; char buf[16];
snprintf(buf, sizeof(buf), "%s", snprintf(buf, sizeof(buf), "%s",
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
screen->forceDisplay(); screen->forceDisplay();
// FIXME - draw serial # somewhere?
}
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
/// Used on eink displays while in deep sleep
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
drawIconScreen("Sleeping...", display, state, x, y);
} }
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@ -600,6 +619,21 @@ Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue
cmdQueue.setReader(this); cmdQueue.setReader(this);
} }
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/
void Screen::doDeepSleep()
{
#ifdef HAS_EINK
static FrameCallback sleepFrames[] = {drawSleepScreen};
static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]);
ui.setFrames(sleepFrames, sleepFrameCount);
ui.update();
#endif
setOn(false);
}
void Screen::handleSetOn(bool on) void Screen::handleSetOn(bool on)
{ {
if (!useDisplay) if (!useDisplay)
@ -636,7 +670,7 @@ void Screen::setup()
displayWidth = dispdev.width(); displayWidth = dispdev.width();
displayHeight = dispdev.height(); displayHeight = dispdev.height();
ui.setTimePerTransition(SCREEN_TRANSITION_MSECS); ui.setTimePerTransition(SCREEN_TRANSITION_MSECS);
ui.setIndicatorPosition(BOTTOM); ui.setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar. // Defines where the first frame is located in the bar.

View File

@ -99,6 +99,12 @@ class Screen : public concurrency::OSThread
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF}); enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
} }
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/
void doDeepSleep();
/// Handles a button press. /// Handles a button press.
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); } void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }

View File

@ -172,6 +172,10 @@ class ButtonThread : public OSThread
{ {
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
userButton = OneButton(BUTTON_PIN, true, true); userButton = OneButton(BUTTON_PIN, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE);
#endif
userButton.attachClick(userButtonPressed); userButton.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong); userButton.attachDuringLongPress(userButtonPressedLong);
userButton.attachDoubleClick(userButtonDoublePressed); userButton.attachDoubleClick(userButtonDoublePressed);
@ -179,6 +183,10 @@ class ButtonThread : public OSThread
#endif #endif
#ifdef BUTTON_PIN_ALT #ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true); userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE);
#endif
userButtonAlt.attachClick(userButtonPressed); userButtonAlt.attachClick(userButtonPressed);
userButtonAlt.attachDuringLongPress(userButtonPressedLong); userButtonAlt.attachDuringLongPress(userButtonPressedLong);
userButtonAlt.attachDoubleClick(userButtonDoublePressed); userButtonAlt.attachDoubleClick(userButtonDoublePressed);
@ -233,8 +241,8 @@ RadioInterface *rIf = NULL;
void setup() void setup()
{ {
#ifdef USE_SEGGER #ifdef SEGGER_STDOUT_CH
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM); SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif #endif
// Debug // Debug

View File

@ -1,6 +1,8 @@
#include "NRF52Bluetooth.h" #include "NRF52Bluetooth.h"
#include "configuration.h" #include "configuration.h"
#include "graphics/TFTDisplay.h" #include "graphics/TFTDisplay.h"
#include <SPI.h>
#include <Wire.h>
#include <assert.h> #include <assert.h>
#include <ble_gap.h> #include <ble_gap.h>
#include <memory.h> #include <memory.h>
@ -64,7 +66,7 @@ void setBluetoothEnable(bool on)
} }
} }
} else { } else {
if(nrf52Bluetooth) if (nrf52Bluetooth)
nrf52Bluetooth->shutdown(); nrf52Bluetooth->shutdown();
} }
bleOn = on; bleOn = on;
@ -98,7 +100,7 @@ void nrf52Setup()
#ifdef BQ25703A_ADDR #ifdef BQ25703A_ADDR
auto *bq = new BQ25713(); auto *bq = new BQ25713();
if(!bq->setup()) if (!bq->setup())
DEBUG_MSG("ERROR! Charge controller init failed\n"); DEBUG_MSG("ERROR! Charge controller init failed\n");
#endif #endif
@ -114,18 +116,26 @@ void nrf52Setup()
void cpuDeepSleep(uint64_t msecToWake) void cpuDeepSleep(uint64_t msecToWake)
{ {
DEBUG_MSG("FIXME: implement NRF52 deep sleep enter actions\n"); // FIXME, configure RTC or button press to wake us
// FIXME, configure RTC to wake us // FIXME, power down SPI, I2C, RAMs
// FIXME, power down SPI, I2C, RAMs Wire.end();
SPI.end();
Serial.end();
Serial1.end();
// FIXME, use system off mode with ram retention for key state? // 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 // FIXME, use non-init RAM per
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
while(1) { auto ok = sd_power_system_off();
delay(5000); if(ok != NRF_SUCCESS) {
DEBUG_MSG("."); DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
} NRF_POWER->SYSTEMOFF = 1;
}
// FIXME, after wake power up SPI, I2C, RAMs, reinit LORA // The following code should not be run, because we are off
DEBUG_MSG("FIXME: implement NRF52 deep sleep wake actions\n"); while (1) {
delay(5000);
DEBUG_MSG(".");
}
} }

View File

@ -142,14 +142,14 @@ static void waitEnterSleep()
void doDeepSleep(uint64_t msecToWake) void doDeepSleep(uint64_t msecToWake)
{ {
DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000); DEBUG_MSG("Entering deep sleep for %lu seconds\n", msecToWake / 1000);
// 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();
notifyDeepSleep.notifyObservers(NULL); notifyDeepSleep.notifyObservers(NULL);
screen->setOn(false); // datasheet says this will draw only 10ua screen->doDeepSleep(); // datasheet says this will draw only 10ua
nodeDB.saveToDisk(); nodeDB.saveToDisk();

View File

@ -243,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
} }

View File

@ -147,7 +147,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX1262_ANT_SW (32 + 10) // P1.10 #define SX1262_ANT_SW (32 + 10) // P1.10
// 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
} }

View File

@ -168,7 +168,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
// 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
} }