Files
arduino/src/AgOledDisplay.cpp

553 lines
13 KiB
C++
Raw Normal View History

2024-04-03 11:40:46 +07:00
#include "AgOledDisplay.h"
#include "Libraries/U8g2/src/U8g2lib.h"
2024-07-24 09:05:57 +07:00
#include "Main/utils.h"
2024-04-03 21:26:04 +07:00
2024-04-05 06:39:59 +07:00
/** Cast U8G2 */
2024-04-03 21:26:04 +07:00
#define DISP() ((U8G2_SH1106_128X64_NONAME_F_HW_I2C *)(this->u8g2))
2024-04-05 06:39:59 +07:00
/**
* @brief Show dashboard temperature and humdity
2024-06-24 18:34:24 +07:00
*
* @param hasStatus
2024-04-05 06:39:59 +07:00
*/
void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) {
/** Temperature */
2024-07-24 09:05:57 +07:00
if (utils::isValidTemperature(value.Temperature)) {
2024-09-21 13:06:01 +07:00
float t = 0.0f;
if (config.isTemperatureUnitInF()) {
t = utils::degreeC_To_F(value.Temperature);
2024-09-21 13:06:01 +07:00
} else {
t = value.Temperature;
2024-09-21 13:06:01 +07:00
}
2024-04-03 21:26:04 +07:00
if (config.isTemperatureUnitInF()) {
if (hasStatus) {
snprintf(buf, buf_size, "%0.1f", t);
} else {
snprintf(buf, buf_size, "%0.1f°F", t);
}
2024-04-03 21:26:04 +07:00
} else {
if (hasStatus) {
snprintf(buf, buf_size, "%.1f", t);
} else {
snprintf(buf, buf_size, "%.1f°C", t);
}
2024-04-03 21:26:04 +07:00
}
} else { /** Show invalid value */
if (config.isTemperatureUnitInF()) {
2024-09-21 13:06:01 +07:00
snprintf(buf, buf_size, "-°F");
2024-04-03 21:26:04 +07:00
} else {
2024-09-21 13:06:01 +07:00
snprintf(buf, buf_size, "-°C");
2024-04-03 21:26:04 +07:00
}
}
DISP()->drawUTF8(1, 10, buf);
2024-04-03 11:40:46 +07:00
/** Show humidity */
2024-07-24 09:05:57 +07:00
if (utils::isValidHumidity(value.Humidity)) {
2024-09-21 13:06:01 +07:00
snprintf(buf, buf_size, "%d%%", value.Humidity);
2024-04-03 21:26:04 +07:00
} else {
2024-09-21 13:06:01 +07:00
snprintf(buf, buf_size, "-%%");
2024-04-03 21:26:04 +07:00
}
2024-04-03 11:40:46 +07:00
2024-04-03 21:26:04 +07:00
if (value.Humidity > 99) {
DISP()->drawStr(97, 10, buf);
} else {
DISP()->drawStr(105, 10, buf);
}
2024-04-03 11:40:46 +07:00
}
void OledDisplay::setCentralText(int y, String text) {
setCentralText(y, text.c_str());
}
void OledDisplay::setCentralText(int y, const char *text) {
int x = (DISP()->getWidth() - DISP()->getStrWidth(text)) / 2;
DISP()->drawStr(x, y, text);
}
2024-04-05 06:39:59 +07:00
/**
* @brief Construct a new Ag Oled Display:: Ag Oled Display object
2024-06-24 18:34:24 +07:00
*
2024-04-05 06:39:59 +07:00
* @param config AgConfiguration
2024-04-07 16:39:01 +07:00
* @param value Measurements
2024-04-05 06:39:59 +07:00
* @param log Serial Stream
*/
2024-06-24 18:34:24 +07:00
OledDisplay::OledDisplay(Configuration &config, Measurements &value,
Stream &log)
2024-04-07 16:39:01 +07:00
: PrintLog(log, "OledDisplay"), config(config), value(value) {}
2024-04-04 10:36:59 +07:00
2024-04-05 06:39:59 +07:00
/**
* @brief Set AirGradient instance
2024-06-24 18:34:24 +07:00
*
2024-04-05 06:39:59 +07:00
* @param ag Point to AirGradient instance
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::setAirGradient(AirGradient *ag) { this->ag = ag; }
2024-04-03 21:26:04 +07:00
2024-04-07 16:39:01 +07:00
OledDisplay::~OledDisplay() {}
2024-04-03 11:40:46 +07:00
/**
* @brief Initialize display
*
* @return true Success
* @return false Failure
*/
2024-04-07 16:39:01 +07:00
bool OledDisplay::begin(void) {
2024-04-03 21:26:04 +07:00
if (isBegin) {
2024-04-03 11:40:46 +07:00
logWarning("Already begin, call 'end' and try again");
return true;
}
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
/** Create u8g2 instance */
u8g2 = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, U8X8_PIN_NONE);
if (u8g2 == NULL) {
logError("Create 'U8G2' failed");
return false;
}
2024-04-03 11:40:46 +07:00
2024-06-24 18:34:24 +07:00
/** Init u8g2 */
if (DISP()->begin() == false) {
logError("U8G2 'begin' failed");
return false;
}
} else if (ag->isBasic()) {
logInfo("DIY_BASIC init");
ag->display.begin(Wire);
ag->display.setTextColor(1);
ag->display.clear();
ag->display.show();
2024-04-03 11:40:46 +07:00
}
/** Show low brightness on startup. then it's completely turn off on main
* application */
int brightness = config.getDisplayBrightness();
2024-06-24 18:34:24 +07:00
if (brightness == 0) {
setBrightness(1);
}
2024-05-01 21:25:35 +07:00
2024-04-03 11:40:46 +07:00
isBegin = true;
logInfo("begin");
return true;
}
/**
* @brief De-Initialize display
*
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::end(void) {
2024-04-03 21:26:04 +07:00
if (!isBegin) {
2024-04-03 11:40:46 +07:00
logWarning("Already end, call 'begin' and try again");
return;
}
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
/** Free u8g2 */
delete DISP();
u8g2 = NULL;
} else if (ag->isBasic()) {
ag->display.end();
}
2024-04-03 11:40:46 +07:00
isBegin = false;
logInfo("end");
}
2024-04-05 06:39:59 +07:00
/**
* @brief Show text on 3 line of display
2024-06-24 18:34:24 +07:00
*
* @param line1
* @param line2
* @param line3
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::setText(String &line1, String &line2, String &line3) {
2024-04-03 11:40:46 +07:00
setText(line1.c_str(), line2.c_str(), line3.c_str());
}
2024-04-05 06:39:59 +07:00
/**
* @brief Show text on 3 line of display
2024-06-24 18:34:24 +07:00
*
* @param line1
* @param line2
* @param line3
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::setText(const char *line1, const char *line2,
2024-06-24 18:34:24 +07:00
const char *line3) {
if (isDisplayOff) {
return;
}
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
DISP()->drawStr(1, 10, line1);
DISP()->drawStr(1, 30, line2);
DISP()->drawStr(1, 50, line3);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(1, 1);
ag->display.setText(line1);
ag->display.setCursor(1, 17);
ag->display.setText(line2);
ag->display.setCursor(1, 33);
ag->display.setText(line3);
ag->display.show();
}
2024-04-03 11:40:46 +07:00
}
2024-04-05 06:39:59 +07:00
/**
* @brief Set Text on 4 line
2024-06-24 18:34:24 +07:00
*
* @param line1
* @param line2
* @param line3
* @param line4
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::setText(String &line1, String &line2, String &line3,
2024-06-24 18:34:24 +07:00
String &line4) {
2024-04-03 11:40:46 +07:00
setText(line1.c_str(), line2.c_str(), line3.c_str(), line4.c_str());
}
2024-04-05 06:39:59 +07:00
/**
* @brief Set Text on 4 line
2024-06-24 18:34:24 +07:00
*
* @param line1
* @param line2
* @param line3
* @param line4
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::setText(const char *line1, const char *line2,
2024-06-24 18:34:24 +07:00
const char *line3, const char *line4) {
if (isDisplayOff) {
return;
}
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
DISP()->drawStr(1, 10, line1);
DISP()->drawStr(1, 25, line2);
DISP()->drawStr(1, 40, line3);
DISP()->drawStr(1, 55, line4);
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(0, 0);
ag->display.setText(line1);
ag->display.setCursor(0, 10);
ag->display.setText(line2);
ag->display.setCursor(0, 20);
ag->display.setText(line3);
ag->display.show();
}
2024-04-03 11:40:46 +07:00
}
2024-04-03 21:26:04 +07:00
2024-04-05 06:39:59 +07:00
/**
* @brief Update dashboard content
2024-06-24 18:34:24 +07:00
*
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::showDashboard(void) { showDashboard(NULL); }
2024-04-03 21:26:04 +07:00
2024-04-05 06:39:59 +07:00
/**
* @brief Update dashboard content and error status
2024-06-24 18:34:24 +07:00
*
2024-04-05 06:39:59 +07:00
*/
2024-04-07 16:39:01 +07:00
void OledDisplay::showDashboard(const char *status) {
if (isDisplayOff) {
return;
}
2024-06-24 18:34:24 +07:00
char strBuf[16];
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
if ((status == NULL) || (strlen(status) == 0)) {
2024-09-21 13:06:01 +07:00
showTempHum(false, strBuf, sizeof(strBuf));
2024-06-24 18:34:24 +07:00
} else {
String strStatus = "Show status: " + String(status);
logInfo(strStatus);
int strWidth = DISP()->getStrWidth(status);
DISP()->drawStr((DISP()->getWidth() - strWidth) / 2, 10, status);
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Show WiFi NA*/
if (strcmp(status, "WiFi N/A") == 0) {
DISP()->setFont(u8g2_font_t0_12_tf);
2024-09-21 13:06:01 +07:00
showTempHum(true, strBuf, sizeof(strBuf));
2024-06-24 18:34:24 +07:00
}
}
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Draw horizonal line */
DISP()->drawLine(1, 13, 128, 13);
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Show CO2 label */
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawUTF8(1, 27, "CO2");
DISP()->setFont(u8g2_font_t0_22b_tf);
2024-07-24 09:05:57 +07:00
if (utils::isValidCO2(value.CO2)) {
sprintf(strBuf, "%d", value.CO2);
2024-06-24 18:34:24 +07:00
} else {
sprintf(strBuf, "%s", "-");
}
2024-06-24 18:34:24 +07:00
DISP()->drawStr(1, 48, strBuf);
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Show CO2 value index */
DISP()->setFont(u8g2_font_t0_12_tf);
DISP()->drawStr(1, 61, "ppm");
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Draw vertical line */
2024-07-18 11:28:12 +07:00
DISP()->drawLine(52, 14, 52, 64);
DISP()->drawLine(97, 14, 97, 64);
2024-06-24 18:34:24 +07:00
/** Draw PM2.5 label */
DISP()->setFont(u8g2_font_t0_12_tf);
2024-07-18 11:28:12 +07:00
DISP()->drawStr(55, 27, "PM2.5");
2024-06-24 18:34:24 +07:00
/** Draw PM2.5 value */
2024-09-21 13:06:01 +07:00
if (utils::isValidPm(value.pm25_1)) {
int pm25 = value.pm25_1;
/** Compensate PM2.5 value. */
if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
pm25 = ag->pms5003.compensate(pm25, value.Humidity);
logInfo("PM2.5 compensate: " + String(pm25));
}
if (config.isPmStandardInUSAQI()) {
2024-08-25 20:37:38 +07:00
sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25));
2024-06-24 18:34:24 +07:00
} else {
2024-09-21 13:06:01 +07:00
sprintf(strBuf, "%d", pm25);
2024-06-24 18:34:24 +07:00
}
} else { /** Show invalid value. */
2024-09-21 13:06:01 +07:00
sprintf(strBuf, "%s", "-");
}
DISP()->setFont(u8g2_font_t0_22b_tf);
DISP()->drawStr(55, 48, strBuf);
/** Draw PM2.5 unit */
2024-09-21 13:06:01 +07:00
DISP()->setFont(u8g2_font_t0_12_tf);
if (config.isPmStandardInUSAQI()) {
2024-07-18 11:28:12 +07:00
DISP()->drawUTF8(55, 61, "AQI");
2024-06-24 18:34:24 +07:00
} else {
2024-07-18 11:28:12 +07:00
DISP()->drawUTF8(55, 61, "ug/m³");
}
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Draw tvocIndexlabel */
DISP()->setFont(u8g2_font_t0_12_tf);
2024-07-18 11:28:12 +07:00
DISP()->drawStr(100, 27, "VOC:");
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
/** Draw tvocIndexvalue */
2024-07-24 09:05:57 +07:00
if (utils::isValidVOC(value.TVOC)) {
2024-06-24 18:34:24 +07:00
sprintf(strBuf, "%d", value.TVOC);
} else {
sprintf(strBuf, "%s", "-");
}
2024-07-18 11:28:12 +07:00
DISP()->drawStr(100, 39, strBuf);
2024-06-24 18:34:24 +07:00
/** Draw NOx label */
2024-07-30 20:26:19 +07:00
DISP()->drawStr(100, 53, "NOx:");
2024-07-24 09:05:57 +07:00
if (utils::isValidNOx(value.NOx)) {
2024-06-24 18:34:24 +07:00
sprintf(strBuf, "%d", value.NOx);
} else {
sprintf(strBuf, "%s", "-");
}
2024-07-18 11:28:12 +07:00
DISP()->drawStr(100, 63, strBuf);
2024-06-24 18:34:24 +07:00
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
/** Set CO2 */
2024-09-21 13:06:01 +07:00
if (utils::isValidCO2(value.CO2)) {
2024-07-24 09:05:57 +07:00
snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2);
} else {
snprintf(strBuf, sizeof(strBuf), "CO2:-");
}
2024-09-21 13:06:01 +07:00
2024-06-24 18:34:24 +07:00
ag->display.setCursor(0, 0);
ag->display.setText(strBuf);
/** Set PM */
2024-07-21 07:13:34 +07:00
int pm25 = value.pm25_1;
2024-09-21 13:06:01 +07:00
if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
2024-08-26 11:56:01 +07:00
pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity);
2024-07-21 07:13:34 +07:00
}
2024-06-24 18:34:24 +07:00
ag->display.setCursor(0, 12);
2024-08-30 19:07:31 +07:00
if (utils::isValidPm(pm25)) {
2024-08-25 20:37:38 +07:00
snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25);
2024-07-24 09:05:57 +07:00
} else {
snprintf(strBuf, sizeof(strBuf), "PM2.5:-");
}
2024-06-24 18:34:24 +07:00
ag->display.setText(strBuf);
/** Set temperature and humidity */
2024-07-24 09:05:57 +07:00
if (utils::isValidTemperature(value.Temperature)) {
2024-06-24 18:34:24 +07:00
if (config.isTemperatureUnitInF()) {
snprintf(strBuf, sizeof(strBuf), "T:%0.1f F",
utils::degreeC_To_F(value.Temperature));
2024-06-24 18:34:24 +07:00
} else {
snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature);
2024-06-24 18:34:24 +07:00
}
2024-04-03 21:26:04 +07:00
} else {
2024-06-24 18:34:24 +07:00
if (config.isTemperatureUnitInF()) {
2024-07-24 09:05:57 +07:00
snprintf(strBuf, sizeof(strBuf), "T:-F");
} else {
2024-07-24 09:05:57 +07:00
snprintf(strBuf, sizeof(strBuf), "T:-C");
}
2024-04-03 21:26:04 +07:00
}
2024-07-24 09:05:57 +07:00
2024-06-24 18:34:24 +07:00
ag->display.setCursor(0, 24);
ag->display.setText(strBuf);
2024-07-24 09:05:57 +07:00
if (utils::isValidHumidity(value.Humidity)) {
snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity);
} else {
snprintf(strBuf, sizeof(strBuf), "H:- %%");
}
2024-06-24 18:34:24 +07:00
ag->display.setCursor(0, 36);
ag->display.setText(strBuf);
2024-04-03 21:26:04 +07:00
2024-06-24 18:34:24 +07:00
ag->display.show();
}
2024-04-03 21:26:04 +07:00
}
2024-04-25 06:49:14 +07:00
2024-05-01 21:25:35 +07:00
void OledDisplay::setBrightness(int percent) {
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
if (percent == 0) {
isDisplayOff = true;
2024-06-24 18:34:24 +07:00
// Clear display.
DISP()->firstPage();
do {
} while (DISP()->nextPage());
2024-06-24 18:34:24 +07:00
} else {
isDisplayOff = false;
DISP()->setContrast((127 * percent) / 100);
}
} else if (ag->isBasic()) {
if (percent == 0) {
isDisplayOff = true;
// Clear display.
ag->display.clear();
ag->display.show();
}
else {
isDisplayOff = false;
ag->display.setContrast((255 * percent) / 100);
}
}
2024-05-01 21:25:35 +07:00
}
2024-06-24 18:34:24 +07:00
#ifdef ESP32
2024-05-17 20:17:49 +07:00
void OledDisplay::showFirmwareUpdateVersion(String version) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
setCentralText(40, "New version");
setCentralText(60, version.c_str());
} while (DISP()->nextPage());
}
2024-05-17 20:17:49 +07:00
void OledDisplay::showFirmwareUpdateProgress(int percent) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
2024-05-17 20:17:49 +07:00
setCentralText(50, String("Updating... ") + String(percent) + String("%"));
} while (DISP()->nextPage());
}
void OledDisplay::showFirmwareUpdateSuccess(int count) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
setCentralText(40, "Success");
setCentralText(60, String("Rebooting... ") + String(count));
} while (DISP()->nextPage());
}
2024-05-17 20:17:49 +07:00
void OledDisplay::showFirmwareUpdateFailed(void) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
setCentralText(40, "fail, will retry");
// setCentralText(60, "will retry");
} while (DISP()->nextPage());
}
2024-05-17 20:17:49 +07:00
void OledDisplay::showFirmwareUpdateSkipped(void) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
setCentralText(40, "skipped");
} while (DISP()->nextPage());
}
2024-05-17 20:17:49 +07:00
void OledDisplay::showFirmwareUpdateUpToDate(void) {
if (isDisplayOff) {
return;
}
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
setCentralText(20, "Firmware Update");
setCentralText(40, "up to date");
} while (DISP()->nextPage());
}
2024-06-24 18:34:24 +07:00
#else
#endif
void OledDisplay::showRebooting(void) {
2024-06-25 16:43:50 +07:00
if (ag->isOne() || ag->isPro3_3() || ag->isPro4_2()) {
2024-06-24 18:34:24 +07:00
DISP()->firstPage();
do {
DISP()->setFont(u8g2_font_t0_16_tf);
// setCentralText(20, "Firmware Update");
setCentralText(40, "Rebooting...");
2024-06-24 18:34:24 +07:00
// setCentralText(60, String("Retry after 24h"));
} while (DISP()->nextPage());
} else if (ag->isBasic()) {
ag->display.clear();
ag->display.setCursor(0, 20);
ag->display.setText("Rebooting...");
ag->display.show();
}
}