diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 40ea25237..d0a235e8c 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -64,6 +64,10 @@ uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // Threshold values for the GPS lock accuracy bar display uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100}; +// At some point, we're going to ask all of the plugins if they would like to display a screen frame +// we'll need to hold onto pointers for the plugins that can draw a frame. +std::vector pluginFrames; + // Stores the last 4 of our hardware ID, to make finding the device for pairing easier static char ourId[5]; @@ -144,6 +148,14 @@ static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int drawIconScreen("Sleeping...", display, state, x, y); } +static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + DEBUG_MSG("Drawing Plugin Frame %d\n\n", state->currentFrame); + MeshPlugin &pi = *pluginFrames.at(state->currentFrame); + pi.drawFrame(display,state,x,y); + +} + static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_CENTER); @@ -887,6 +899,11 @@ void Screen::setFrames() DEBUG_MSG("showing standard frames\n"); showingNormalScreen = true; + pluginFrames = MeshPlugin::GetMeshPluginsWithUIFrames(); + DEBUG_MSG("Showing %d plugin frames\n", pluginFrames.size()); + int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + pluginFrames.size(); + DEBUG_MSG("Total frame count: %d\n", totalFrameCount); + // We don't show the node info our our node (if we have it yet - we should) size_t numnodes = nodeStatus->getNumTotal(); if (numnodes > 0) @@ -894,6 +911,18 @@ void Screen::setFrames() size_t numframes = 0; + // put all of the plugin frames first. + // this is a little bit of a dirty hack; since we're going to call + // the same drawPluginFrame handler here for all of these plugin frames + // and then we'll just assume that the state->currentFrame value + // is the same offset into the pluginFrames vector + // so that we can invoke the plugin's callback + for (auto i = pluginFrames.begin(); i != pluginFrames.end(); ++i) { + normalFrames[numframes++] = drawPluginFrame; + } + + DEBUG_MSG("Added plugins. numframes: %d", numframes); + // If we have a critical fault, show it first if (myNodeInfo.error_code) normalFrames[numframes++] = drawCriticalFaultFrame; @@ -922,6 +951,8 @@ void Screen::setFrames() } #endif + DEBUG_MSG("Finished building frames. numframes: %d\n", numframes); + ui.setFrames(normalFrames, numframes); ui.enableAllIndicators(); diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index ebaee49a6..006ce571b 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -75,4 +75,18 @@ void MeshPlugin::sendResponse(const MeshPacket &req) { void setReplyTo(MeshPacket *p, const MeshPacket &to) { p->to = to.from; p->want_ack = to.want_ack; -} \ No newline at end of file +} + +std::vector MeshPlugin::GetMeshPluginsWithUIFrames() { + + std::vector pluginsWithUIFrames; + for (auto i = plugins->begin(); i != plugins->end(); ++i) { + auto &pi = **i; + if ( pi.wantUIFrame()) { + DEBUG_MSG("Plugin wants a UI Frame\n"); + pluginsWithUIFrames.push_back(&pi); + } + } + return pluginsWithUIFrames; + +} diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 01af58114..a5fa48445 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -2,6 +2,8 @@ #include "mesh/MeshTypes.h" #include +#include +#include /** A baseclass for any mesh "plugin". * * A plugin allows you to add new features to meshtastic device code, without needing to know messaging details. @@ -14,7 +16,7 @@ */ class MeshPlugin { - static std::vector *plugins; + static std::vector *plugins; public: /** Constructor @@ -28,6 +30,10 @@ class MeshPlugin */ static void callPlugins(const MeshPacket &mp); + static std::vector GetMeshPluginsWithUIFrames(); + + virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } + protected: const char *name; @@ -61,6 +67,13 @@ class MeshPlugin * so that subclasses can (optionally) send a response back to the original sender. */ virtual MeshPacket *allocReply() { return NULL; } + /*** + * @return true if you want to be alloced a UI screen frame + */ + virtual bool wantUIFrame() { return false; } + + + private: /** Messages can be received that have the want_response bit set. If set, this callback will be invoked diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp index 1e6aed778..1d8ecfab7 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp @@ -7,6 +7,8 @@ #include "main.h" #include "../mesh/generated/environmental_measurement.pb.h" #include +#include +#include EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin; EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio; @@ -17,7 +19,7 @@ uint32_t sensor_read_error_count = 0; #define DHT_11_GPIO_PIN 13 //TODO: Make a related radioconfig preference to allow less-frequent reads -#define ENVIRONMENTAL_MEASUREMENT_APP_ENABLED false // DISABLED by default; set this to true if you want to use the plugin +#define ENVIRONMENTAL_MEASUREMENT_APP_ENABLED true // DISABLED by default; set this to true if you want to use the plugin #define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 #define SENSOR_READ_ERROR_COUNT_THRESHOLD 5 #define SENSOR_READ_MULTIPLIER 3 @@ -26,6 +28,24 @@ uint32_t sensor_read_error_count = 0; DHT dht(DHT_11_GPIO_PIN,DHT11); + +#ifdef HAS_EINK +// The screen is bigger so use bigger fonts +#define FONT_SMALL ArialMT_Plain_16 +#define FONT_MEDIUM ArialMT_Plain_24 +#define FONT_LARGE ArialMT_Plain_24 +#else +#define FONT_SMALL ArialMT_Plain_10 +#define FONT_MEDIUM ArialMT_Plain_16 +#define FONT_LARGE ArialMT_Plain_24 +#endif + +#define fontHeight(font) ((font)[1] + 1) // height is position 1 + +#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) +#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) + + int32_t EnvironmentalMeasurementPlugin::runOnce() { #ifndef NO_ESP32 if (!ENVIRONMENTAL_MEASUREMENT_APP_ENABLED){ @@ -70,6 +90,28 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { #endif } +void EnvironmentalMeasurementPluginRadio::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(x, y, "Environment"); + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": T:"+ String(lastMeasurement.temperature,2) + " H:" + String(lastMeasurement.relative_humidity,2)); + +} + +String GetSenderName(const MeshPacket &mp) { + String sender; + + if (nodeDB.getNode(mp.from)){ + sender = nodeDB.getNode(mp.from)->user.short_name; + } + else { + sender = "UNK"; + } + return sender; +} + bool EnvironmentalMeasurementPluginRadio::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement &p) { if (!ENVIRONMENTAL_MEASUREMENT_APP_ENABLED){ @@ -77,26 +119,24 @@ bool EnvironmentalMeasurementPluginRadio::handleReceivedProtobuf(const MeshPacke return false; } bool wasBroadcast = mp.to == NODENUM_BROADCAST; - String sender; + + String sender = GetSenderName(mp); - if (nodeDB.getNode(mp.from)){ - sender = nodeDB.getNode(mp.from)->user.short_name; - } - else { - sender = "UNK"; - } // Show new nodes on LCD screen if (DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN && wasBroadcast) { - String lcd = String("Env Measured: ") + sender + "\n" + + String lcd = String("Env Measured: ") +sender + "\n" + "T: " + p.temperature + "\n" + "H: " + p.relative_humidity + "\n"; screen->print(lcd.c_str()); } DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n",sender); + DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender); DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p.relative_humidity); DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature); + + lastMeasurement = p; + lastSender = sender; return false; // Let others look at this message also if they want } diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h index 31581b31b..8e9d968ba 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h @@ -1,6 +1,8 @@ #pragma once #include "ProtobufPlugin.h" #include "../mesh/generated/environmental_measurement.pb.h" +#include +#include class EnvironmentalMeasurementPlugin : private concurrency::OSThread @@ -31,6 +33,8 @@ class EnvironmentalMeasurementPluginRadio : public ProtobufPlugin