From ef87cde9d643ce0ccf80c03fa011b6071fe54142 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 24 Mar 2025 03:52:46 +0700 Subject: [PATCH] Change error status on display to icon --- examples/OneOpenAir/OneOpenAir.ino | 20 ++++-- src/AgOledDisplay.cpp | 102 +++++++++++++++++++++-------- src/AgOledDisplay.h | 25 +++++-- src/AgStateMachine.cpp | 41 +++++++----- src/Libraries/airgradient-client | 2 +- 5 files changed, 133 insertions(+), 57 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 9471041..85db0da 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -34,6 +34,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #include "AgValue.h" #include "AgWiFiConnector.h" #include "AirGradient.h" +#include "App/AppDef.h" #include "Arduino.h" #include "EEPROM.h" #include "ESPmDNS.h" @@ -1111,12 +1112,21 @@ static void updateDisplayAndLedBar(void) { return; } - if (wifiConnector.isConnected() == false && networkOption == UseWifi) { - stateMachine.displayHandle(AgStateMachineWiFiLost); - stateMachine.handleLeds(AgStateMachineWiFiLost); - return; + if (networkOption == UseWifi) { + if (wifiConnector.isConnected() == false) { + stateMachine.displayHandle(AgStateMachineWiFiLost); + stateMachine.handleLeds(AgStateMachineWiFiLost); + return; + } + } + else if (networkOption == UseCellular) { + if (agClient->isClientReady() == false) { + // Same action as wifi + stateMachine.displayHandle(AgStateMachineWiFiLost); + stateMachine.handleLeds(AgStateMachineWiFiLost); + return; + } } - // TODO: Also show for cellular connection problem if (configuration.isCloudConnectionDisabled()) { // Ignore API related check since cloud is disabled diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 840d24f..45e50e8 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -5,12 +5,31 @@ /** Cast U8G2 */ #define DISP() ((U8G2_SH1106_128X64_NONAME_F_HW_I2C *)(this->u8g2)) +static const unsigned char wifi_issue_bits[] = { + 0xd8, 0xc6, 0xde, 0xde, 0xc7, 0xf8, 0xd1, 0xe2, 0xdc, 0xce, 0xcc, + 0xcc, 0xc0, 0xc0, 0xd0, 0xc2, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}; + +static const unsigned char cloud_issue_bits[] = { + 0x70, 0xc0, 0x88, 0xc0, 0x04, 0xc1, 0x04, 0xcf, 0x02, 0xd0, 0x01, + 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0xa2, 0xd0, 0x4c, 0xce, 0xa0, 0xc0}; + +// Offline mode icon +static unsigned char offline_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x62, 0x00, + 0xE6, 0x00, 0xFE, 0x1F, 0xFE, 0x1F, 0xE6, 0x00, 0x62, 0x00, + 0x30, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +// { +// 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x62, 0x00, 0xE2, 0x00, +// 0xFE, 0x1F, 0xFE, 0x1F, 0xE2, 0x00, 0x62, 0x00, 0x60, 0x00, 0x30, 0x00, +// 0x00, 0x00, 0x00, 0x00, }; /** * @brief Show dashboard temperature and humdity * * @param hasStatus */ -void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { +void OledDisplay::showTempHum(bool hasStatus) { + char buf[10]; /** Temperature */ float temp = value.getCorrectedTempHum(Measurements::Temperature, 1); if (utils::isValidTemperature(temp)) { @@ -23,22 +42,22 @@ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { if (config.isTemperatureUnitInF()) { if (hasStatus) { - snprintf(buf, buf_size, "%0.1f", t); + snprintf(buf, sizeof(buf), "%0.1f", t); } else { - snprintf(buf, buf_size, "%0.1f°F", t); + snprintf(buf, sizeof(buf), "%0.1f°F", t); } } else { if (hasStatus) { - snprintf(buf, buf_size, "%.1f", t); + snprintf(buf, sizeof(buf), "%.1f", t); } else { - snprintf(buf, buf_size, "%.1f°C", t); + snprintf(buf, sizeof(buf), "%.1f°C", t); } } } else { /** Show invalid value */ if (config.isTemperatureUnitInF()) { - snprintf(buf, buf_size, "-°F"); + snprintf(buf, sizeof(buf), "-°F"); } else { - snprintf(buf, buf_size, "-°C"); + snprintf(buf, sizeof(buf), "-°C"); } } DISP()->drawUTF8(1, 10, buf); @@ -46,9 +65,9 @@ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { /** Show humidity */ int rhum = round(value.getCorrectedTempHum(Measurements::Humidity, 1)); if (utils::isValidHumidity(rhum)) { - snprintf(buf, buf_size, "%d%%", rhum); + snprintf(buf, sizeof(buf), "%d%%", rhum); } else { - snprintf(buf, buf_size, "-%%"); + snprintf(buf, sizeof(buf), "-%%"); } if (rhum > 99.0) { @@ -67,6 +86,9 @@ void OledDisplay::setCentralText(int y, const char *text) { DISP()->drawStr(x, y, text); } +void OledDisplay::showIcon(int x, int y, xbm_icon_t *icon) { + DISP()->drawXBM(x, y, icon->width, icon->height, icon->icon); +} /** * @brief Construct a new Ag Oled Display:: Ag Oled Display object * @@ -252,36 +274,60 @@ void OledDisplay::setText(const char *line1, const char *line2, * @brief Update dashboard content * */ -void OledDisplay::showDashboard(void) { showDashboard(NULL); } +void OledDisplay::showDashboard(void) { showDashboard(DashBoardStatusNone); } /** * @brief Update dashboard content and error status * */ -void OledDisplay::showDashboard(const char *status) { +void OledDisplay::showDashboard(DashboardStatus status) { if (isDisplayOff) { return; } char strBuf[16]; + const int icon_pos_x = 64; + xbm_icon_t xbm_icon = { + .width = 0, + .height = 0, + .icon = nullptr, + }; + if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) { DISP()->firstPage(); do { DISP()->setFont(u8g2_font_t0_16_tf); - if ((status == NULL) || (strlen(status) == 0)) { - showTempHum(false, strBuf, sizeof(strBuf)); - } else { - String strStatus = "Show status: " + String(status); - logInfo(strStatus); - - int strWidth = DISP()->getStrWidth(status); - DISP()->drawStr((DISP()->getWidth() - strWidth) / 2, 10, status); - - /** Show WiFi NA*/ - if (strcmp(status, "WiFi N/A") == 0) { - DISP()->setFont(u8g2_font_t0_12_tf); - showTempHum(true, strBuf, sizeof(strBuf)); - } + switch (status) { + case DashBoardStatusNone: { + // Maybe show signal strength? + showTempHum(false); + break; + } + case DashBoardStatusWiFiIssue: { + DISP()->drawXBM(icon_pos_x, 0, 14, 11, wifi_issue_bits); + showTempHum(false); + break; + } + case DashBoardStatusServerIssue: { + DISP()->drawXBM(icon_pos_x, 0, 14, 11, cloud_issue_bits); + showTempHum(false); + break; + } + case DashBoardStatusAddToDashboard: { + setCentralText(10, "Add To Dashboard"); + break; + } + case DashBoardStatusDeviceId: { + setCentralText(10, ag->deviceId().c_str()); + break; + } + case DashBoardStatusOfflineMode: { + DISP()->drawXBM(icon_pos_x, 0, 14, 14, offline_bits); + showTempHum(false); // First true + break; + } + default: + break; } /** Draw horizonal line */ @@ -392,7 +438,8 @@ void OledDisplay::showDashboard(const char *status) { float temp = value.getCorrectedTempHum(Measurements::Temperature, 1); if (utils::isValidTemperature(temp)) { if (config.isTemperatureUnitInF()) { - snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", utils::degreeC_To_F(temp)); + snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", + utils::degreeC_To_F(temp)); } else { snprintf(strBuf, sizeof(strBuf), "T:%0.1f C", temp); } @@ -442,8 +489,7 @@ void OledDisplay::setBrightness(int percent) { // Clear display. ag->display.clear(); ag->display.show(); - } - else { + } else { isDisplayOff = false; ag->display.setContrast((255 * percent) / 100); } diff --git a/src/AgOledDisplay.h b/src/AgOledDisplay.h index 435ff4c..8f71802 100644 --- a/src/AgOledDisplay.h +++ b/src/AgOledDisplay.h @@ -16,17 +16,32 @@ private: Measurements &value; bool isDisplayOff = false; - void showTempHum(bool hasStatus, char* buf, int buf_size); + typedef struct { + int width; + int height; + unsigned char *icon; + } xbm_icon_t; + + void showTempHum(bool hasStatus); void setCentralText(int y, String text); void setCentralText(int y, const char *text); + void showIcon(int x, int y, xbm_icon_t *icon); public: - OledDisplay(Configuration &config, Measurements &value, - Stream &log); + OledDisplay(Configuration &config, Measurements &value, Stream &log); ~OledDisplay(); + enum DashboardStatus { + DashBoardStatusNone, + DashBoardStatusWiFiIssue, + DashBoardStatusServerIssue, + DashBoardStatusAddToDashboard, + DashBoardStatusDeviceId, + DashBoardStatusOfflineMode, + }; + void setAirGradient(AirGradient *ag); - bool begin(void); + bool begin(void); void end(void); void setText(String &line1, String &line2, String &line3); void setText(const char *line1, const char *line2, const char *line3); @@ -34,7 +49,7 @@ public: void setText(const char *line1, const char *line2, const char *line3, const char *line4); void showDashboard(void); - void showDashboard(const char *status); + void showDashboard(DashboardStatus status); void setBrightness(int percent); #ifdef ESP32 void showFirmwareUpdateVersion(String version); diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 8d1f45d..8af3238 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -1,6 +1,7 @@ #include "AgStateMachine.h" +#include "AgOledDisplay.h" -#define LED_TEST_BLINK_DELAY 50 /** ms */ +#define LED_TEST_BLINK_DELAY 50 /** ms */ #define LED_FAST_BLINK_DELAY 250 /** ms */ #define LED_SLOW_BLINK_DELAY 1000 /** ms */ #define LED_SHORT_BLINK_DELAY 500 /** ms */ @@ -8,9 +9,9 @@ #define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */ -#define RGB_COLOR_R 255, 0, 0 /** Red */ -#define RGB_COLOR_G 0, 255, 0 /** Green */ -#define RGB_COLOR_Y 255, 150, 0 /** Yellow */ +#define RGB_COLOR_R 255, 0, 0 /** Red */ +#define RGB_COLOR_G 0, 255, 0 /** Green */ +#define RGB_COLOR_Y 255, 150, 0 /** Yellow */ #define RGB_COLOR_O 255, 40, 0 /** Orange */ #define RGB_COLOR_P 180, 0, 255 /** Purple */ #define RGB_COLOR_CLEAR 0, 0, 0 /** No color */ @@ -50,7 +51,7 @@ void StateMachine::ledStatusBlinkDelay(uint32_t ms) { /** * @brief Led bar show PM or CO2 led color status * - * @return true if all led bar are used, false othwerwise + * @return true if all led bar are used, false othwerwise */ bool StateMachine::sensorhandleLeds(void) { int totalLedUsed = 0; @@ -82,7 +83,7 @@ bool StateMachine::sensorhandleLeds(void) { /** * @brief Show CO2 LED status * - * @return return total number of led that are used on the monitor + * @return return total number of led that are used on the monitor */ int StateMachine::co2handleLeds(void) { int totalUsed = ag->ledBar.getNumberOfLeds(); @@ -166,8 +167,8 @@ int StateMachine::co2handleLeds(void) { /** * @brief Show PM2.5 LED status - * - * @return return total number of led that are used on the monitor + * + * @return return total number of led that are used on the monitor */ int StateMachine::pm25handleLeds(void) { int totalUsed = ag->ledBar.getNumberOfLeds(); @@ -369,18 +370,17 @@ void StateMachine::ledBarTest(void) { } else { ledBarRunTest(); } - } - else if(ag->isOpenAir()) { + } else if (ag->isOpenAir()) { ledBarRunTest(); } } } -void StateMachine::ledBarPowerUpTest(void) { +void StateMachine::ledBarPowerUpTest(void) { if (ag->isOne()) { ag->ledBar.clear(); } - ledBarRunTest(); + ledBarRunTest(); } void StateMachine::ledBarRunTest(void) { @@ -544,11 +544,11 @@ void StateMachine::displayHandle(AgStateMachineState state) { break; } case AgStateMachineWiFiLost: { - disp.showDashboard("WiFi N/A"); + disp.showDashboard(OledDisplay::DashBoardStatusWiFiIssue); break; } case AgStateMachineServerLost: { - disp.showDashboard("AG Server N/A"); + disp.showDashboard(OledDisplay::DashBoardStatusServerIssue); break; } case AgStateMachineSensorConfigFailed: { @@ -557,19 +557,24 @@ void StateMachine::displayHandle(AgStateMachineState state) { if (ms >= 5000) { addToDashboardTime = millis(); if (addToDashBoardToggle) { - disp.showDashboard("Add to AG Dashb."); + disp.showDashboard(OledDisplay::DashBoardStatusAddToDashboard); } else { - disp.showDashboard(ag->deviceId().c_str()); + disp.showDashboard(OledDisplay::DashBoardStatusDeviceId); } addToDashBoardToggle = !addToDashBoardToggle; } } else { - disp.showDashboard(""); + disp.showDashboard(); } break; } case AgStateMachineNormal: { - disp.showDashboard(); + if (config.isOfflineMode()) { + disp.showDashboard( + OledDisplay::DashBoardStatusOfflineMode); + } else { + disp.showDashboard(); + } break; } case AgStateMachineCo2Calibration: diff --git a/src/Libraries/airgradient-client b/src/Libraries/airgradient-client index 1419c83..edc6898 160000 --- a/src/Libraries/airgradient-client +++ b/src/Libraries/airgradient-client @@ -1 +1 @@ -Subproject commit 1419c83657bc21bae48c00a2a44ad7423cc63cea +Subproject commit edc68980e3c699fc57a9fb1ee43867f0aabd826d