Merge pull request #45 from girtsf/screen-cpp-refactor

Screen cleanups and refactoring
This commit is contained in:
Kevin Hester 2020-03-18 18:35:51 -07:00 committed by GitHub
commit 6bc8e1b10a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 398 additions and 355 deletions

View File

@ -5,7 +5,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <Update.h> #include <Update.h>
#include "configuration.h" #include "configuration.h"
#include "screen.h"
SimpleAllocator btPool; SimpleAllocator btPool;
@ -173,7 +172,7 @@ uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue)
class MySecurity : public BLESecurityCallbacks class MySecurity : public BLESecurityCallbacks
{ {
protected:
bool onConfirmPIN(uint32_t pin) bool onConfirmPIN(uint32_t pin)
{ {
Serial.printf("onConfirmPIN %u\n", pin); Serial.printf("onConfirmPIN %u\n", pin);
@ -189,7 +188,7 @@ class MySecurity : public BLESecurityCallbacks
void onPassKeyNotify(uint32_t pass_key) void onPassKeyNotify(uint32_t pass_key)
{ {
Serial.printf("onPassKeyNotify %u\n", pass_key); Serial.printf("onPassKeyNotify %u\n", pass_key);
screen_start_bluetooth(pass_key); startCb(pass_key);
} }
bool onSecurityRequest() bool onSecurityRequest()
@ -211,9 +210,13 @@ class MySecurity : public BLESecurityCallbacks
Serial.printf("onAuthenticationComplete -> fail %d\n", cmpl.fail_reason); Serial.printf("onAuthenticationComplete -> fail %d\n", cmpl.fail_reason);
} }
// Remove our custom screen // Remove our custom PIN request screen.
screen.setFrames(); stopCb();
} }
public:
StartBluetoothPinScreenCallback startCb;
StopBluetoothPinScreenCallback stopCb;
}; };
BLEServer *pServer; BLEServer *pServer;
@ -255,7 +258,10 @@ void deinitBLE()
btPool.reset(); btPool.reset();
} }
BLEServer *initBLE(std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion) BLEServer *initBLE(
StartBluetoothPinScreenCallback startBtPinScreen,
StopBluetoothPinScreenCallback stopBtPinScreen,
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
{ {
BLEDevice::init(deviceName); BLEDevice::init(deviceName);
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
@ -264,6 +270,8 @@ BLEServer *initBLE(std::string deviceName, std::string hwVendor, std::string swV
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
*/ */
static MySecurity mySecurity; static MySecurity mySecurity;
mySecurity.startCb = startBtPinScreen;
mySecurity.stopCb = stopBtPinScreen;
BLEDevice::setSecurityCallbacks(&mySecurity); BLEDevice::setSecurityCallbacks(&mySecurity);
// Create the BLE Server // Create the BLE Server

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <functional>
#include <Arduino.h> #include <Arduino.h>
#include <BLEDevice.h> #include <BLEDevice.h>
#include <BLEServer.h> #include <BLEServer.h>
@ -17,8 +19,14 @@ void dumpCharacteristic(BLECharacteristic *c);
/** converting endianness pull out a 32 bit value */ /** converting endianness pull out a 32 bit value */
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue); uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue);
// TODO(girts): create a class for the bluetooth utils helpers?
using StartBluetoothPinScreenCallback = std::function<void(uint32_t pass_key)>;
using StopBluetoothPinScreenCallback = std::function<void(void)>;
void loopBLE(); void loopBLE();
BLEServer *initBLE(std::string devName, std::string hwVendor, std::string swVersion, std::string hwVersion = ""); BLEServer *initBLE(
StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
std::string devName, std::string hwVendor, std::string swVersion, std::string hwVersion = "");
void deinitBLE(); void deinitBLE();
/// Add a characteristic that we will delete when we restart /// Add a characteristic that we will delete when we restart

View File

@ -2,12 +2,12 @@
#include <Arduino.h> #include <Arduino.h>
#include <assert.h> #include <assert.h>
#include "main.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "MeshService.h" #include "MeshService.h"
#include "MeshBluetoothService.h" #include "MeshBluetoothService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "GPS.h" #include "GPS.h"
#include "screen.h"
#include "Periodic.h" #include "Periodic.h"
#include "PowerFSM.h" #include "PowerFSM.h"
@ -118,7 +118,7 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
sendOurOwner(mp->from); sendOurOwner(mp->from);
String lcd = String("Joined: ") + mp->payload.variant.user.long_name + "\n"; String lcd = String("Joined: ") + mp->payload.variant.user.long_name + "\n";
screen_print(lcd.c_str()); screen.print(lcd.c_str());
} }
return mp; return mp;
@ -360,4 +360,4 @@ void MeshService::onNotify(Observable *o)
{ {
DEBUG_MSG("got gps notify\n"); DEBUG_MSG("got gps notify\n");
onGPSChanged(); onGPSChanged();
} }

View File

@ -179,4 +179,4 @@ void PowerFSM_setup()
// timeout"); // timeout");
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
} }

View File

@ -44,6 +44,14 @@ AXP20X_Class axp;
bool pmu_irq = false; bool pmu_irq = false;
#endif #endif
// Global Screen singleton
#ifdef I2C_SDA
meshtastic::Screen screen(SSD1306_ADDRESS, I2C_SDA, I2C_SCL);
#else
// Fake values for pins to keep build happy, we won't ever initialize it.
meshtastic::Screen screen(SSD1306_ADDRESS, 0, 0);
#endif
// these flags are all in bss so they default false // these flags are all in bss so they default false
bool isCharging; bool isCharging;
bool isUSBPowered; bool isUSBPowered;
@ -221,8 +229,6 @@ void setup()
scanI2Cdevice(); scanI2Cdevice();
#endif #endif
axp192Init();
// Buttons & LED // Buttons & LED
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(BUTTON_PIN, INPUT_PULLUP);
@ -240,13 +246,16 @@ void setup()
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER) if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
ssd1306_found = false; // forget we even have the hardware ssd1306_found = false; // forget we even have the hardware
// Initialize the screen first so we can show the logo while we start up everything else.
if (ssd1306_found) if (ssd1306_found)
screen.setup(); screen.setup();
axp192Init();
// Init GPS // Init GPS
gps.setup(); gps.setup();
screen_print("Started...\n"); screen.print("Started...\n");
service.init(); service.init();
@ -264,7 +273,14 @@ void initBluetooth()
// FIXME - we are leaking like crazy // FIXME - we are leaking like crazy
// AllocatorScope scope(btPool); // AllocatorScope scope(btPool);
BLEServer *serve = initBLE(getDeviceName(), HW_VENDOR, xstr(APP_VERSION), xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr // Note: these callbacks might be coming in from a different thread.
BLEServer *serve = initBLE(
[](uint8_t pin) {
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
screen.startBluetoothPinScreen(pin);
},
[]() { screen.stopBluetoothPinScreen(); },
getDeviceName(), HW_VENDOR, xstr(APP_VERSION), xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
createMeshBluetoothService(serve); createMeshBluetoothService(serve);
// Start advertising - this must be done _after_ creating all services // Start advertising - this must be done _after_ creating all services
@ -392,6 +408,14 @@ void loop()
} }
#endif #endif
// Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 3000))
{
screen.stopBootScreen();
showingBootScreen = false;
}
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
// i.e. don't just keep spinning in loop as fast as we can. // i.e. don't just keep spinning in loop as fast as we can.
//DEBUG_MSG("msecs %d\n", msecstosleep); //DEBUG_MSG("msecs %d\n", msecstosleep);

View File

@ -1,6 +1,11 @@
#pragma once #pragma once
#include "screen.h"
extern bool axp192_found; extern bool axp192_found;
extern bool ssd1306_found; extern bool ssd1306_found;
extern bool isCharging; extern bool isCharging;
extern bool isUSBPowered; extern bool isUSBPowered;
// Global Screen singleton.
extern meshtastic::Screen screen;

View File

@ -20,55 +20,37 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <OLEDDisplay.h>
#include <Wire.h> #include <Wire.h>
#include "SSD1306Wire.h"
#include "OLEDDisplay.h"
#include "images.h"
#include "fonts.h"
#include "GPS.h" #include "GPS.h"
#include "OLEDDisplayUi.h"
#include "screen.h"
#include "mesh-pb-constants.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "main.h"
#include "configuration.h" #include "configuration.h"
#include "fonts.h"
#include "images.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "screen.h"
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space #define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1) #define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
#define SCREEN_WIDTH 128 #define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64 #define SCREEN_HEIGHT 64
#define TRANSITION_FRAMERATE 30 // fps
#ifdef I2C_SDA #define IDLE_FRAMERATE 10 // in fps
SSD1306Wire dispdev(SSD1306_ADDRESS, I2C_SDA, I2C_SCL); #define COMPASS_DIAM 44
#else
SSD1306Wire dispdev(SSD1306_ADDRESS, 0, 0); // fake values to keep build happy, we won't ever init
#endif
bool disp; // true if we are using display
bool screenOn; // true if the display is currently powered
OLEDDisplayUi ui(&dispdev);
#define NUM_EXTRA_FRAMES 2 // text message and debug frame #define NUM_EXTRA_FRAMES 2 // text message and debug frame
// A text message frame + debug frame + all the node infos
FrameCallback nonBootFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
Screen screen; namespace meshtastic
static bool showingBluetooth;
/// If set to true (possibly from an ISR), we should turn on the screen the next time our idle loop runs.
static bool showingBootScreen = true; // start by showing the bootscreen
bool Screen::isOn() { return screenOn; }
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
{ {
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->setFont(ArialMT_Plain_10);
display->drawString(128, 0, String(millis()));
}
void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) // A text message frame + debug frame + all the node infos
static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
static uint32_t targetFramerate = IDLE_FRAMERATE;
static char btPIN[16] = "888888";
static void drawBootScreen(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
@ -79,16 +61,10 @@ void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->setFont(ArialMT_Plain_16); display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org"); display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
ui.disableIndicator();
} }
static char btPIN[16] = "888888"; static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
// Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
// Besides the default fonts there will be a program to convert TrueType fonts into this format
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16); display->setFont(ArialMT_Plain_16);
display->drawString(64 + x, 2 + y, "Bluetooth"); display->drawString(64 + x, 2 + y, "Bluetooth");
@ -99,81 +75,45 @@ void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_24); display->setFont(ArialMT_Plain_24);
display->drawString(64 + x, 22 + y, btPIN); display->drawString(64 + x, 22 + y, btPIN);
ui.disableIndicator();
}
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
// Besides the default fonts there will be a program to convert TrueType fonts into this format
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawString(0 + x, 10 + y, "Arial 10");
display->setFont(ArialMT_Plain_16);
display->drawString(0 + x, 20 + y, "Arial 16");
display->setFont(ArialMT_Plain_24);
display->drawString(0 + x, 34 + y, "Arial 24");
}
void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Text alignment demo
display->setFont(ArialMT_Plain_10);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0 + x, 11 + y, "Left aligned (0,10)");
// The coordinates define the center of the text
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, 22 + y, "Center aligned (64,22)");
// The coordinates define the right end of the text
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(128 + x, 33 + y, "Right aligned (128,33)");
} }
/// Draw the last text message we received /// Draw the last text message we received
void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
MeshPacket &mp = devicestate.rx_text_message; MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(mp.from); NodeInfo *node = nodeDB.getNode(mp.from);
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from, mp.payload.variant.data.payload.bytes); // DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
// mp.payload.variant.data.payload.bytes);
// Demo for drawStringMaxWidth: // Demo for drawStringMaxWidth:
// with the third parameter you can define the width after which words will be wrapped. // with the third parameter you can define the width after which words will
// Currently only spaces and "-" are allowed for wrapping // be wrapped. Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16); display->setFont(ArialMT_Plain_16);
String sender = (node && node->has_user) ? node->user.short_name : "???"; String sender = (node && node->has_user) ? node->user.short_name : "???";
display->drawString(0 + x, 0 + y, sender); display->drawString(0 + x, 0 + y, sender);
display->setFont(ArialMT_Plain_10); display->setFont(ArialMT_Plain_10);
// the max length of this buffer is much longer than we can possibly print
static char tempBuf[96]; static char tempBuf[96];
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.payload.variant.data.payload.bytes); // the max length of this buffer is much longer than we can possibly print snprintf(tempBuf, sizeof(tempBuf), " %s",
mp.payload.variant.data.payload.bytes);
display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf); display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf);
// ui.disableIndicator();
} }
/// Draw a series of fields in a column, wrapping to multiple colums if needed /// Draw a series of fields in a column, wrapping to multiple colums if needed
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{ {
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
const char **f = fields; const char **f = fields;
int xo = x, yo = y; int xo = x, yo = y;
while (*f) while (*f) {
{
display->drawString(xo, yo, *f); display->drawString(xo, yo, *f);
yo += FONT_HEIGHT; yo += FONT_HEIGHT;
if (yo > SCREEN_HEIGHT - FONT_HEIGHT) if (yo > SCREEN_HEIGHT - FONT_HEIGHT) {
{
xo += SCREEN_WIDTH / 2; xo += SCREEN_WIDTH / 2;
yo = 0; yo = 0;
} }
@ -183,21 +123,23 @@ void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields
/// Draw a series of fields in a row, wrapping to multiple rows if needed /// Draw a series of fields in a row, wrapping to multiple rows if needed
/// @return the max y we ended up printing to /// @return the max y we ended up printing to
uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{ {
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
const char **f = fields; const char **f = fields;
int xo = x, yo = y; int xo = x, yo = y;
while (*f) const int COLUMNS = 2; // hardwired for two columns per row....
{ int col = 0; // track which column we are on
while (*f) {
display->drawString(xo, yo, *f); display->drawString(xo, yo, *f);
xo += SCREEN_WIDTH / 2; // hardwired for two columns per row.... xo += SCREEN_WIDTH / COLUMNS;
if (xo >= SCREEN_WIDTH) // Wrap to next row, if needed.
{ if (++col > COLUMNS) {
xo = x;
yo += FONT_HEIGHT; yo += FONT_HEIGHT;
xo = 0; col = 0;
} }
f++; f++;
} }
@ -207,8 +149,9 @@ uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **field
return yo; return yo;
} }
/// Ported from my old java code, returns distance in meters along the globe surface (by magic?) /// Ported from my old java code, returns distance in meters along the globe
float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b) /// surface (by magic?)
static float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
{ {
double pk = (180 / 3.14169); double pk = (180 / 3.14169);
double a1 = lat_a / pk; double a1 = lat_a / pk;
@ -217,10 +160,8 @@ float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
double b2 = lng_b / pk; double b2 = lng_b / pk;
double cos_b1 = cos(b1); double cos_b1 = cos(b1);
double cos_a1 = cos(a1); double cos_a1 = cos(a1);
double t1 = double t1 = cos_a1 * cos(a2) * cos_b1 * cos(b2);
cos_a1 * cos(a2) * cos_b1 * cos(b2); double t2 = cos_a1 * sin(a2) * cos_b1 * sin(b2);
double t2 =
cos_a1 * sin(a2) * cos_b1 * sin(b2);
double t3 = sin(a1) * sin(b1); double t3 = sin(a1) * sin(b1);
double tt = acos(t1 + t2 + t3); double tt = acos(t1 + t2 + t3);
if (isnan(tt)) if (isnan(tt))
@ -229,31 +170,32 @@ float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
return (float)(6366000 * tt); return (float)(6366000 * tt);
} }
inline double toRadians(double deg) static inline double toRadians(double deg)
{ {
return deg * PI / 180; return deg * PI / 180;
} }
inline double toDegrees(double r) static inline double toDegrees(double r)
{ {
return r * 180 / PI; return r * 180 / PI;
} }
/** /**
* Computes the bearing in degrees between two points on Earth. Ported from my old Gaggle android app. * Computes the bearing in degrees between two points on Earth. Ported from my
* * old Gaggle android app.
* @param lat1 *
* Latitude of the first point * @param lat1
* @param lon1 * Latitude of the first point
* Longitude of the first point * @param lon1
* @param lat2 * Longitude of the first point
* Latitude of the second point * @param lat2
* @param lon2 * Latitude of the second point
* Longitude of the second point * @param lon2
* @return Bearing between the two points in radians. A value of 0 means due * Longitude of the second point
* north. * @return Bearing between the two points in radians. A value of 0 means due
*/ * north.
float bearing(double lat1, double lon1, double lat2, double lon2) */
static float bearing(double lat1, double lon1, double lat2, double lon2)
{ {
double lat1Rad = toRadians(lat1); double lat1Rad = toRadians(lat1);
double lat2Rad = toRadians(lat2); double lat2Rad = toRadians(lat2);
@ -263,10 +205,13 @@ float bearing(double lat1, double lon1, double lat2, double lon2)
return atan2(y, x); return atan2(y, x);
} }
namespace
{
/// A basic 2D point class for drawing /// A basic 2D point class for drawing
class Point class Point
{ {
public: public:
float x, y; float x, y;
Point(float _x, float _y) : x(_x), y(_y) {} Point(float _x, float _y) : x(_x), y(_y) {}
@ -274,10 +219,8 @@ public:
/// Apply a rotation around zero (standard rotation matrix math) /// Apply a rotation around zero (standard rotation matrix math)
void rotate(float radian) void rotate(float radian)
{ {
float cos = cosf(radian), float cos = cosf(radian), sin = sinf(radian);
sin = sinf(radian); float rx = x * cos - y * sin, ry = x * sin + y * cos;
float rx = x * cos - y * sin,
ry = x * sin + y * cos;
x = rx; x = rx;
y = ry; y = ry;
@ -296,23 +239,25 @@ public:
} }
}; };
void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2) } // namespace
static void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2)
{ {
d->drawLine(p1.x, p1.y, p2.x, p2.y); d->drawLine(p1.x, p1.y, p2.x, p2.y);
} }
/** /**
* Given a recent lat/lon return a guess of the heading the user is walking on. * Given a recent lat/lon return a guess of the heading the user is walking on.
* *
* We keep a series of "after you've gone 10 meters, what is your heading since the last reference point?" * We keep a series of "after you've gone 10 meters, what is your heading since
* the last reference point?"
*/ */
float estimatedHeading(double lat, double lon) static float estimatedHeading(double lat, double lon)
{ {
static double oldLat, oldLon; static double oldLat, oldLon;
static float b; static float b;
if (oldLat == 0) if (oldLat == 0) {
{
// just prepare for next time // just prepare for next time
oldLat = lat; oldLat = lat;
oldLon = lon; oldLon = lon;
@ -331,28 +276,28 @@ float estimatedHeading(double lat, double lon)
return b; return b;
} }
/// Sometimes we will have Position objects that only have a time, so check for valid lat/lon /// Sometimes we will have Position objects that only have a time, so check for
bool hasPosition(NodeInfo *n) /// valid lat/lon
static bool hasPosition(NodeInfo *n)
{ {
return n->has_position && (n->position.latitude != 0 || n->position.longitude != 0); return n->has_position && (n->position.latitude != 0 || n->position.longitude != 0);
} }
#define COMPASS_DIAM 44
/// We will skip one node - the one for us, so we just blindly loop over all nodes /// We will skip one node - the one for us, so we just blindly loop over all
/// nodes
static size_t nodeIndex; static size_t nodeIndex;
static int8_t prevFrame = -1; static int8_t prevFrame = -1;
void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
// We only advance our nodeIndex if the frame # has changed - because drawNodeInfo will be called repeatedly while the frame is shown // We only advance our nodeIndex if the frame # has changed - because
if (state->currentFrame != prevFrame) // drawNodeInfo will be called repeatedly while the frame is shown
{ if (state->currentFrame != prevFrame) {
prevFrame = state->currentFrame; prevFrame = state->currentFrame;
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes(); nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
NodeInfo *n = nodeDB.getNodeByIndex(nodeIndex); NodeInfo *n = nodeDB.getNodeByIndex(nodeIndex);
if (n->num == nodeDB.getNodeNum()) if (n->num == nodeDB.getNodeNum()) {
{
// Don't show our node, just skip to next // Don't show our node, just skip to next
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes(); nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
} }
@ -380,14 +325,14 @@ void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, in
snprintf(lastStr, sizeof(lastStr), "%d hours ago", agoSecs / 60 / 60); snprintf(lastStr, sizeof(lastStr), "%d hours ago", agoSecs / 60 / 60);
static float simRadian; static float simRadian;
simRadian += 0.1; // For testing, have the compass spin unless both locations are valid simRadian += 0.1; // For testing, have the compass spin unless both
// locations are valid
static char distStr[20]; static char distStr[20];
*distStr = 0; // might not have location data *distStr = 0; // might not have location data
float headingRadian = simRadian; float headingRadian = simRadian;
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum()); NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
if (ourNode && hasPosition(ourNode) && hasPosition(node)) if (ourNode && hasPosition(ourNode) && hasPosition(node)) {
{
Position &op = ourNode->position, &p = node->position; Position &op = ourNode->position, &p = node->position;
float d = latLongToMeter(p.latitude, p.longitude, op.latitude, op.longitude); float d = latLongToMeter(p.latitude, p.longitude, op.latitude, op.longitude);
if (d < 2000) if (d < 2000)
@ -395,23 +340,20 @@ void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, in
else else
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
// FIXME, also keep the guess at the operators heading and add/substract it. currently we don't do this and instead draw north up only. // FIXME, also keep the guess at the operators heading and add/substract
// it. currently we don't do this and instead draw north up only.
float bearingToOther = bearing(p.latitude, p.longitude, op.latitude, op.longitude); float bearingToOther = bearing(p.latitude, p.longitude, op.latitude, op.longitude);
float myHeading = estimatedHeading(p.latitude, p.longitude); float myHeading = estimatedHeading(p.latitude, p.longitude);
headingRadian = bearingToOther - myHeading; headingRadian = bearingToOther - myHeading;
} }
const char *fields[] = { const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
username,
distStr,
signalStr,
lastStr,
NULL};
drawColumns(display, x, y, fields); drawColumns(display, x, y, fields);
// coordinates for the center of the compass // coordinates for the center of the compass
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2; int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
// display->drawXbm(compassX, compassY, compass_width, compass_height, (const uint8_t *)compass_bits); // display->drawXbm(compassX, compassY, compass_width, compass_height,
// (const uint8_t *)compass_bits);
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
@ -419,8 +361,7 @@ void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, in
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow}; Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++) {
{
points[i]->rotate(headingRadian); points[i]->rotate(headingRadian);
points[i]->scale(COMPASS_DIAM * 0.6); points[i]->scale(COMPASS_DIAM * 0.6);
points[i]->translate(compassX, compassY); points[i]->translate(compassX, compassY);
@ -432,7 +373,7 @@ void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, in
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
} }
void drawDebugInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawDebugInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
display->setFont(ArialMT_Plain_10); display->setFont(ArialMT_Plain_10);
@ -451,32 +392,17 @@ void drawDebugInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, i
static char gpsStr[20]; static char gpsStr[20];
if (myNodeInfo.has_gps) if (myNodeInfo.has_gps)
snprintf(gpsStr, sizeof(gpsStr), "GPS %d%%", 75); // FIXME, use something based on hdop snprintf(gpsStr, sizeof(gpsStr), "GPS %d%%",
75); // FIXME, use something based on hdop
else else
gpsStr[0] = '\0'; // Just show emptystring gpsStr[0] = '\0'; // Just show emptystring
const char *fields[] = { const char *fields[] = {batStr, gpsStr, usersStr, channelStr, NULL};
batStr,
gpsStr,
usersStr,
channelStr,
NULL};
uint32_t yo = drawRows(display, x, y, fields); uint32_t yo = drawRows(display, x, y, fields);
display->drawLogBuffer(x, yo); display->drawLogBuffer(x, yo);
} }
// This array keeps function pointers to all frames
// frames are the single views that slide in
FrameCallback bootFrames[] = {drawBootScreen};
// Overlays are statically drawn on top of a frame eg. a clock
OverlayCallback overlays[] = {/* msOverlay */};
// how many frames are there?
const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
const int overlaysCount = sizeof(overlays) / sizeof(overlays[0]);
#if 0 #if 0
void _screen_header() void _screen_header()
{ {
@ -501,20 +427,21 @@ void _screen_header()
} }
#endif #endif
void Screen::setOn(bool on) Screen::Screen(uint8_t address, uint8_t sda, uint8_t scl)
: cmdQueue(32), useDisplay(sda || scl), dispdev(address, sda, scl), ui(&dispdev)
{ {
if (!disp) }
void Screen::handleSetOn(bool on)
{
if (!useDisplay)
return; return;
if (on != screenOn) if (on != screenOn) {
{ if (on) {
if (on)
{
DEBUG_MSG("Turning on screen\n"); DEBUG_MSG("Turning on screen\n");
dispdev.displayOn(); dispdev.displayOn();
setPeriod(1); // redraw ASAP } else {
}
else {
DEBUG_MSG("Turning off screen\n"); DEBUG_MSG("Turning off screen\n");
dispdev.displayOff(); dispdev.displayOff();
} }
@ -522,167 +449,134 @@ void Screen::setOn(bool on)
} }
} }
void screen_print(const char *text, uint8_t x, uint8_t y, uint8_t alignment)
{
DEBUG_MSG(text);
if (!disp)
return;
dispdev.setTextAlignment((OLEDDISPLAY_TEXT_ALIGNMENT)alignment);
dispdev.drawString(x, y, text);
}
void screen_print(const char *text)
{
DEBUG_MSG("Screen: %s", text);
if (!disp)
return;
dispdev.print(text);
// ui.update();
}
void Screen::setup() void Screen::setup()
{ {
#ifdef I2C_SDA if (!useDisplay)
// Display instance return;
disp = true;
// The ESP is capable of rendering 60fps in 80Mhz mode
// but that won't give you much time for anything else
// run it in 160Mhz mode or just set it to 30 fps
// We do this now in loop()
// ui.setTargetFPS(30);
// Customize the active and inactive symbol
//ui.setActiveSymbol(activeSymbol);
//ui.setInactiveSymbol(inactiveSymbol);
ui.setTimePerTransition(300); // msecs
// You can change this to
// TOP, LEFT, BOTTOM, RIGHT
ui.setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
// You can change the transition that is used
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
ui.setFrameAnimation(SLIDE_LEFT);
// Add frames - we subtract one from the framecount so there won't be a visual glitch when we take the boot screen out of the sequence.
ui.setFrames(bootFrames, bootFrameCount);
// Add overlays
ui.setOverlays(overlays, overlaysCount);
// Initialising the UI will init the display too.
ui.init();
// Scroll buffer
dispdev.setLogBuffer(3, 32);
setOn(true); // update our screenOn bool
// TODO(girts): how many of the devices come with the bicolor displays? With
// this enabled, the logo looklooks nice, but the regular screens look a bit
// wacky as the text crosses the color boundary and there's a 1px gap.
#ifdef BICOLOR_DISPLAY #ifdef BICOLOR_DISPLAY
dispdev.flipScreenVertically(); // looks better without this on lora32 dispdev.flipScreenVertically(); // looks better without this on lora32
#endif #endif
// dispdev.setFont(Custom_ArialMT_Plain_10); // Initialising the UI will init the display too.
ui.init();
ui.setTimePerTransition(300); // msecs
ui.setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
ui.setFrameAnimation(SLIDE_LEFT);
// Don't show the page swipe dots while in boot screen.
ui.disableAllIndicators();
ui.disableAutoTransition(); // we now require presses // Add frames.
ui.update(); // force an immediate draw of the bootscreen, because on some ssd1306 clones, the first draw command is discarded static FrameCallback bootFrames[] = {drawBootScreen};
#endif static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
ui.setFrames(bootFrames, bootFrameCount);
// No overlays.
ui.setOverlays(nullptr, 0);
// Require presses to switch between frames.
ui.disableAutoTransition();
// Set up a log buffer with 3 lines, 32 chars each.
dispdev.setLogBuffer(3, 32);
// Turn on the display hardware.
handleSetOn(true);
// On some ssd1306 clones, the first draw command is discarded, so draw it
// twice initially.
ui.update();
ui.update();
} }
#define TRANSITION_FRAMERATE 30 // fps
#define IDLE_FRAMERATE 10 // in fps
static uint32_t targetFramerate = IDLE_FRAMERATE;
void Screen::doTask() void Screen::doTask()
{ {
if (!disp) // If we don't have a screen, don't ever spend any CPU for us.
{ // If we don't have a screen, don't ever spend any CPU for us if (!useDisplay) {
setPeriod(0); setPeriod(0);
return; return;
} }
if (!screenOn) // Process incoming commands.
{ // If we didn't just wake and the screen is still off, then stop updating until it is on again for (;;) {
CmdItem cmd;
if (!cmdQueue.dequeue(&cmd, 0)) {
break;
}
switch (cmd.cmd) {
case Cmd::SET_ON:
handleSetOn(true);
break;
case Cmd::SET_OFF:
handleSetOn(false);
break;
case Cmd::ON_PRESS:
handleOnPress();
break;
case Cmd::START_BLUETOOTH_PIN_SCREEN:
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
break;
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
case Cmd::STOP_BOOT_SCREEN:
setFrames();
break;
case Cmd::PRINT:
handlePrint(cmd.print_text);
free(cmd.print_text);
break;
default:
DEBUG_MSG("BUG: invalid cmd");
}
}
if (!screenOn) { // If we didn't just wake and the screen is still off, then
// stop updating until it is on again
setPeriod(0); setPeriod(0);
return; return;
} }
// Switch to a low framerate (to save CPU) when we are not in transition // Switch to a low framerate (to save CPU) when we are not in transition
// but we should only call setTargetFPS when framestate changes, because otherwise that breaks // but we should only call setTargetFPS when framestate changes, because
// animations. // otherwise that breaks animations.
if (targetFramerate != IDLE_FRAMERATE && ui.getUiState()->frameState == FIXED) if (targetFramerate != IDLE_FRAMERATE && ui.getUiState()->frameState == FIXED) {
{
// oldFrameState = ui.getUiState()->frameState; // oldFrameState = ui.getUiState()->frameState;
DEBUG_MSG("Setting idle framerate\n"); DEBUG_MSG("Setting idle framerate\n");
targetFramerate = IDLE_FRAMERATE; targetFramerate = IDLE_FRAMERATE;
ui.setTargetFPS(targetFramerate); ui.setTargetFPS(targetFramerate);
} }
// While showing the bluetooth pair screen all of our standard screen switching is stopped // While showing the bootscreen or Bluetooth pair screen all of our
if (!showingBluetooth) // standard screen switching is stopped.
{ if (showingNormalScreen) {
// Once we finish showing the bootscreen, remove it from the loop // TODO(girts): decouple nodeDB from screen.
if (showingBootScreen) // standard screen loop handling ehre
{ // If the # nodes changes, we need to regen our list of screens
if (millis() > 3 * 1000) // we show the boot screen for a few seconds only if (nodeDB.updateGUI || nodeDB.updateTextMessage) {
{ setFrames();
showingBootScreen = false; nodeDB.updateGUI = false;
setFrames(); nodeDB.updateTextMessage = false;
}
}
else // standard screen loop handling ehre
{
// If the # nodes changes, we need to regen our list of screens
if (nodeDB.updateGUI || nodeDB.updateTextMessage)
{
setFrames();
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
}
} }
} }
// This must be after we possibly do screen_set_frames() to ensure we draw the new data
ui.update(); ui.update();
// DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate, ui.getUiState()->frameState); // DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate,
// If we are scrolling we need to be called soon, otherwise just 1 fps (to save CPU) // ui.getUiState()->frameState); If we are scrolling we need to be called
// We also ask to be called twice as fast as we really need so that any rounding errors still result // soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
// with the correct framerate // as fast as we really need so that any rounding errors still result with
// the correct framerate
setPeriod(1000 / targetFramerate); setPeriod(1000 / targetFramerate);
} }
#include "PowerFSM.h"
// Show the bluetooth PIN screen
void screen_start_bluetooth(uint32_t pin)
{
static FrameCallback btFrames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06d", pin);
DEBUG_MSG("showing bluetooth screen\n");
showingBluetooth = true;
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
ui.setFrames(btFrames, 1); // Just show the bluetooth frame
// we rely on our main loop to show this screen (because we are invoked deep inside of bluetooth callbacks)
// ui.update(); // manually draw once, because I'm not sure if loop is getting called
}
// restore our regular frame list // restore our regular frame list
void Screen::setFrames() void Screen::setFrames()
{ {
DEBUG_MSG("showing standard frames\n"); DEBUG_MSG("showing standard frames\n");
showingNormalScreen = true;
size_t numnodes = nodeDB.getNumNodes(); size_t numnodes = nodeDB.getNumNodes();
// We don't show the node info our our node (if we have it yet - we should) // We don't show the node info our our node (if we have it yet - we should)
@ -693,28 +587,49 @@ void Screen::setFrames()
// If we have a text message - show it first // If we have a text message - show it first
if (devicestate.has_rx_text_message) if (devicestate.has_rx_text_message)
nonBootFrames[numframes++] = drawTextMessageFrame; normalFrames[numframes++] = drawTextMessageFrame;
// then all the nodes // then all the nodes
for (size_t i = 0; i < numnodes; i++) for (size_t i = 0; i < numnodes; i++)
nonBootFrames[numframes++] = drawNodeInfo; normalFrames[numframes++] = drawNodeInfo;
// then the debug info // then the debug info
nonBootFrames[numframes++] = drawDebugInfo; normalFrames[numframes++] = drawDebugInfo;
ui.setFrames(nonBootFrames, numframes); ui.setFrames(normalFrames, numframes);
showingBluetooth = false; ui.enableAllIndicators();
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list just changed) prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
} }
/// handle press of the button void Screen::handleStartBluetoothPinScreen(uint32_t pin)
void Screen::onPress() {
DEBUG_MSG("showing bluetooth screen\n");
showingNormalScreen = false;
static FrameCallback btFrames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06d", pin);
ui.disableAllIndicators();
ui.setFrames(btFrames, 1);
}
void Screen::handlePrint(const char *text)
{
DEBUG_MSG("Screen: %s", text);
if (!useDisplay)
return;
dispdev.print(text);
}
void Screen::handleOnPress()
{ {
// If screen was off, just wake it, otherwise advance to next frame // If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it. // If we are in a transition, the press must have bounced, drop it.
if (ui.getUiState()->frameState == FIXED) if (ui.getUiState()->frameState == FIXED) {
{
setPeriod(1); // redraw ASAP setPeriod(1); // redraw ASAP
ui.nextFrame(); ui.nextFrame();
@ -725,3 +640,5 @@ void Screen::onPress()
ui.setTargetFPS(targetFramerate); ui.setTargetFPS(targetFramerate);
} }
} }
} // namespace meshtastic

View File

@ -1,44 +1,126 @@
#pragma once #pragma once
#include <cstring>
#include <OLEDDisplayUi.h>
#include <SSD1306Wire.h>
#include "PeriodicTask.h" #include "PeriodicTask.h"
#include "TypedQueue.h"
void screen_print(const char * text); namespace meshtastic
void screen_print(const char * text, uint8_t x, uint8_t y, uint8_t alignment); {
/// Deals with showing things on the screen of the device.
// Show the bluetooth PIN screen //
void screen_start_bluetooth(uint32_t pin); // Other than setup(), this class is thread-safe. All state-changing calls are
// queued and executed when the main loop calls us.
// restore our regular frame list
void screen_set_frames();
/**
* Slowly I'm moving screen crap into this class
*/
class Screen : public PeriodicTask class Screen : public PeriodicTask
{ {
public: public:
Screen(uint8_t address, uint8_t sda, uint8_t scl);
Screen(const Screen &) = delete;
Screen &operator=(const Screen &) = delete;
/// Initializes the UI, turns on the display, starts showing boot screen.
//
// Not thread safe - must be called before any other methods are called.
void setup(); void setup();
virtual void doTask(); /// Turns the screen on/off.
void setOn(bool on) { enqueueCmd(CmdItem{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF}); }
/// Turn on the screen asap /// Handles a button press.
void doWakeScreen(); void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
/// Is the screen currently on /// Starts showing the Bluetooth PIN screen.
bool isOn(); //
// Switches over to a static frame showing the Bluetooth pairing screen
// with the PIN.
void startBluetoothPinScreen(uint32_t pin)
{
CmdItem cmd;
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
cmd.bluetooth_pin = pin;
enqueueCmd(cmd);
}
/// Turn the screen on/off /// Stops showing the bluetooth PIN screen.
void setOn(bool on); void stopBluetoothPinScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
/// Handle a button press /// Stops showing the boot screen.
void onPress(); void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
/// Rebuilt our list of screens /// Writes a string to the screen.
void print(const char *text)
{
CmdItem cmd;
cmd.cmd = Cmd::PRINT;
// TODO(girts): strdup() here is scary, but we can't use std::string as
// FreeRTOS queue is just dumbly copying memory contents. It would be
// nice if we had a queue that could copy objects by value.
cmd.print_text = strdup(text);
if (!enqueueCmd(cmd)) {
free(cmd.print_text);
}
}
protected:
/// Updates the UI.
//
// Called periodically from the main loop.
void doTask() final;
private:
enum class Cmd {
INVALID,
SET_ON,
SET_OFF,
ON_PRESS,
START_BLUETOOTH_PIN_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN,
PRINT,
};
struct CmdItem {
Cmd cmd;
union {
uint32_t bluetooth_pin;
char *print_text;
};
};
/// Enques given command item to be processed by main loop().
bool enqueueCmd(const CmdItem &cmd)
{
bool success = cmdQueue.enqueue(cmd, 0);
setPeriod(1); // handle ASAP
return success;
}
// Implementations of various commands, called from doTask().
void handleSetOn(bool on);
void handleOnPress();
void handleStartBluetoothPinScreen(uint32_t pin);
void handlePrint(const char *text);
/// Rebuilds our list of frames (screens) to default ones.
void setFrames(); void setFrames();
private:
/// Queue of commands to execute in doTask.
TypedQueue<CmdItem> cmdQueue;
/// Whether we are using a display
bool useDisplay = false;
/// Whether the display is currently powered
bool screenOn = false;
// Whether we are showing the regular screen (as opposed to booth screen or
// Bluetooth PIN screen)
bool showingNormalScreen = false;
/// Display device
SSD1306Wire dispdev;
/// UI helper for rendering to frames and switching between them
OLEDDisplayUi ui;
}; };
extern Screen screen; } // namespace meshtastic

View File

@ -6,7 +6,6 @@
#include "MeshBluetoothService.h" #include "MeshBluetoothService.h"
#include "MeshService.h" #include "MeshService.h"
#include "GPS.h" #include "GPS.h"
#include "screen.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "Periodic.h" #include "Periodic.h"
#include "esp32/pm.h" #include "esp32/pm.h"