diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 6c6be7580..a4bddaf17 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -31,6 +31,7 @@ along with this program. If not, see . #include "graphics/images.h" #include "main.h" #include "mesh-pb-constants.h" +#include "mesh/generated/deviceonly.pb.h" #include "mesh/Channels.h" #include "modules/TextMessageModule.h" #include "sleep.h" @@ -44,6 +45,8 @@ along with this program. If not, see . using namespace meshtastic; /** @todo remove */ +extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct); + namespace graphics { @@ -77,6 +80,10 @@ static char ourId[5]; // GeoCoord object for the screen GeoCoord geoCoord; +// OEM Config File +static const char *oemConfigFile = "/prefs/oem.proto"; +OEMStore oemStore; + #ifdef SHOW_REDRAWS static bool heartbeat = false; #endif @@ -148,6 +155,54 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 drawIconScreen(region, display, state, x, y); } +static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // draw an xbm image. + // Please note that everything that should be transitioned + // needs to be drawn relative to x and y + + // draw centered icon left to right and centered above the one line of app text + display->drawXbm(x + (SCREEN_WIDTH - oemStore.oem_icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - oemStore.oem_icon_height) / 2 + 2, + oemStore.oem_icon_width, oemStore.oem_icon_height, (const uint8_t *)oemStore.oem_icon_bits.bytes); + + switch(oemStore.oem_font){ + case 0: + display->setFont(FONT_SMALL); + break; + case 2: + display->setFont(FONT_LARGE); + break; + default: + display->setFont(FONT_MEDIUM); + break; + } + + display->setTextAlignment(TEXT_ALIGN_LEFT); + const char *title = oemStore.oem_text; + display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); + display->setFont(FONT_SMALL); + + // Draw region in upper left + if (upperMsg) + display->drawString(x + 0, y + 0, upperMsg); + + // Draw version in upper right + char buf[16]; + snprintf(buf, sizeof(buf), "%s", + xstr(APP_VERSION_SHORT)); // 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); + screen->forceDisplay(); + + // FIXME - draw serial # somewhere? +} + +static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // Draw region in upper left + const char *region = myRegion ? myRegion->name : NULL; + drawOEMIconScreen(region, display, state, x, y); +} + // Used on boot when a certificate is being created static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -813,8 +868,8 @@ void Screen::setup() dispdev.setDetected(screen_model); #endif - // I think this is not needed - redundant with ui.init - // dispdev.resetOrientation(); + // Load OEM config from Proto file if existent + loadProto(oemConfigFile, OEMStore_size, sizeof(oemConfigFile), OEMStore_fields, &oemStore); // Initialising the UI will init the display too. ui.init(); @@ -867,6 +922,7 @@ void Screen::setup() // twice initially. ui.update(); ui.update(); + serialSinceMsec = millis(); // Subscribe to status updates powerStatusObserver.observe(&powerStatus->onNewStatus); @@ -897,7 +953,7 @@ int32_t Screen::runOnce() return RUN_SAME; } - // Show boot screen for first 3 seconds, then switch to normal operation. + // Show boot screen for first 5 seconds, then switch to normal operation. // serialSinceMsec adjusts for additional serial wait time during nRF52 bootup static bool showingBootScreen = true; if (showingBootScreen && (millis() > (5000 + serialSinceMsec))) { @@ -906,6 +962,21 @@ int32_t Screen::runOnce() showingBootScreen = false; } + // If we have an OEM Boot screen, toggle after 2,5 seconds + if(strlen(oemStore.oem_text) > 0){ + static bool showingOEMBootScreen = true; + if (showingOEMBootScreen && (millis() > (2500 + serialSinceMsec))) { + DEBUG_MSG("Switch to OEM screen...\n"); + // Change frames. + static FrameCallback bootOEMFrames[] = {drawOEMBootScreen}; + static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]); + ui.setFrames(bootOEMFrames, bootOEMFrameCount); + ui.update(); + ui.update(); + showingOEMBootScreen = false; + } + } + #ifndef DISABLE_WELCOME_UNSET if (showingNormalScreen && radioConfig.preferences.region == RegionCode_Unset) { setWelcomeFrames(); diff --git a/src/mesh/generated/deviceonly.pb.c b/src/mesh/generated/deviceonly.pb.c index a91649052..76aba1aef 100644 --- a/src/mesh/generated/deviceonly.pb.c +++ b/src/mesh/generated/deviceonly.pb.c @@ -12,4 +12,8 @@ PB_BIND(DeviceState, DeviceState, 4) PB_BIND(ChannelFile, ChannelFile, 2) +PB_BIND(OEMStore, OEMStore, 2) + + + diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index 8250cf568..a8df04eae 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -11,6 +11,17 @@ #error Regenerate this file with the current version of nanopb generator. #endif +/* Enum definitions */ +/* TODO: REPLACE */ +typedef enum _ScreenFonts { + /* TODO: REPLACE */ + ScreenFonts_FONT_SMALL = 0, + /* TODO: REPLACE */ + ScreenFonts_FONT_MEDIUM = 1, + /* TODO: REPLACE */ + ScreenFonts_FONT_LARGE = 2 +} ScreenFonts; + /* Struct definitions */ /* The on-disk saved channels */ typedef struct _ChannelFile { @@ -33,7 +44,7 @@ typedef struct _DeviceState { User owner; /* TODO: REPLACE */ pb_size_t node_db_count; - NodeInfo node_db[80]; + NodeInfo node_db[64]; /* Received packets saved for delivery to the phone */ pb_size_t receive_queue_count; MeshPacket receive_queue[1]; @@ -53,16 +64,40 @@ typedef struct _DeviceState { bool did_gps_reset; } DeviceState; +typedef PB_BYTES_ARRAY_T(2048) OEMStore_oem_icon_bits_t; +/* This can be used for customizing the firmware distribution. If populated, + show a secondary bootup screen with cuatom logo and text for 2.5 seconds. */ +typedef struct _OEMStore { + /* The Logo width in Px */ + uint32_t oem_icon_width; + /* The Logo height in Px */ + uint32_t oem_icon_height; + /* The Logo in xbm bytechar format */ + OEMStore_oem_icon_bits_t oem_icon_bits; + /* Use this font for the OEM text. */ + ScreenFonts oem_font; + /* Use this font for the OEM text. */ + char oem_text[40]; +} OEMStore; + + +/* Helper constants for enums */ +#define _ScreenFonts_MIN ScreenFonts_FONT_SMALL +#define _ScreenFonts_MAX ScreenFonts_FONT_LARGE +#define _ScreenFonts_ARRAYSIZE ((ScreenFonts)(ScreenFonts_FONT_LARGE+1)) + #ifdef __cplusplus extern "C" { #endif /* Initializer values for message structs */ -#define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} +#define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} #define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}} -#define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} +#define OEMStore_init_default {0, 0, {0, {0}}, _ScreenFonts_MIN, ""} +#define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} #define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}} +#define OEMStore_init_zero {0, 0, {0, {0}}, _ScreenFonts_MIN, ""} /* Field tags (for use in manual encoding/decoding) */ #define ChannelFile_channels_tag 1 @@ -74,6 +109,11 @@ extern "C" { #define DeviceState_version_tag 8 #define DeviceState_no_save_tag 9 #define DeviceState_did_gps_reset_tag 11 +#define OEMStore_oem_icon_width_tag 1 +#define OEMStore_oem_icon_height_tag 2 +#define OEMStore_oem_icon_bits_tag 3 +#define OEMStore_oem_font_tag 4 +#define OEMStore_oem_text_tag 5 /* Struct field encoding specification for nanopb */ #define DeviceState_FIELDLIST(X, a) \ @@ -99,16 +139,28 @@ X(a, STATIC, REPEATED, MESSAGE, channels, 1) #define ChannelFile_DEFAULT NULL #define ChannelFile_channels_MSGTYPE Channel +#define OEMStore_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, oem_icon_width, 1) \ +X(a, STATIC, SINGULAR, UINT32, oem_icon_height, 2) \ +X(a, STATIC, SINGULAR, BYTES, oem_icon_bits, 3) \ +X(a, STATIC, SINGULAR, UENUM, oem_font, 4) \ +X(a, STATIC, SINGULAR, STRING, oem_text, 5) +#define OEMStore_CALLBACK NULL +#define OEMStore_DEFAULT NULL + extern const pb_msgdesc_t DeviceState_msg; extern const pb_msgdesc_t ChannelFile_msg; +extern const pb_msgdesc_t OEMStore_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define DeviceState_fields &DeviceState_msg #define ChannelFile_fields &ChannelFile_msg +#define OEMStore_fields &OEMStore_msg /* Maximum encoded size of messages (where known) */ #define ChannelFile_size 832 -#define DeviceState_size 23903 +#define DeviceState_size 19327 +#define OEMStore_size 2106 #ifdef __cplusplus } /* extern "C" */