diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 032873f..aa51da8 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -18,7 +18,7 @@ jobs: core: "esp8266:esp8266@3.1.2" core_url: "https://arduino.esp8266.com/stable/package_esp8266com_index.json" - fqbn: "esp32:esp32:esp32c3" - board_options: "JTAGAdapter=default,CDCOnBoot=cdc,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=verbose,EraseFlash=none" + board_options: "JTAGAdapter=default,CDCOnBoot=cdc,PartitionScheme=min_spiffs,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=verbose,EraseFlash=none" core: "esp32:esp32@2.0.11" exclude: - example: "BASIC" diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 3bb6fc8..240cfaf 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -414,6 +414,7 @@ static void wdgFeedUpdate(void) { static void ledBarEnabledUpdate(void) { if (ag->isOne()) { + ag->ledBar.setBrighness(configuration.getLedBarBrightness()); ag->ledBar.setEnable(configuration.getLedBarMode() != LedBarModeOff); } } @@ -721,6 +722,17 @@ static void configUpdateHandle() { } } + if (configuration.isLedBarBrightnessChanged()) { + ag->ledBar.setBrighness(configuration.getLedBarBrightness()); + Serial.println("Set 'LedBarBrightness' brightness: " + + String(configuration.getLedBarBrightness())); + } + if (configuration.isDisplayBrightnessChanged()) { + oledDisplay.setBrightness(configuration.getDisplayBrightness()); + Serial.println("Set 'DisplayBrightness' brightness: " + + String(configuration.getDisplayBrightness())); + } + appDispHandler(); appLedHandler(); } diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index 5f75d8b..067d5bf 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -1,144 +1,146 @@ #ifndef _OTA_HANDLER_H_ #define _OTA_HANDLER_H_ -#include -#include -#include #include +#include +#include +#include #define OTA_BUF_SIZE 512 #define URL_BUF_SIZE 256 enum OtaUpdateOutcome { - UPDATE_PERFORMED, - ALREADY_UP_TO_DATE, - UPDATE_FAILED, - UDPATE_SKIPPED + UPDATE_PERFORMED, + ALREADY_UP_TO_DATE, + UPDATE_FAILED, + UDPATE_SKIPPED }; - class OtaHandler { public: - void updateFirmwareIfOutdated(String deviceId) { + void updateFirmwareIfOutdated(String deviceId) { - String url = "http://hw.airgradient.com/sensors/airgradient:" - + deviceId + "/generic/os/firmware.bin"; - url += "?current_firmware="; - url += GIT_VERSION; - char urlAsChar[URL_BUF_SIZE]; - url.toCharArray(urlAsChar, URL_BUF_SIZE); - Serial.printf("checking for new OTA update @ %s\n", urlAsChar); + String url = "http://hw.airgradient.com/sensors/airgradient:" + deviceId + + "/generic/os/firmware.bin"; + url += "?current_firmware="; + url += GIT_VERSION; + char urlAsChar[URL_BUF_SIZE]; + url.toCharArray(urlAsChar, URL_BUF_SIZE); + Serial.printf("checking for new OTA update @ %s\n", urlAsChar); - esp_http_client_config_t config = {}; - config.url = urlAsChar; - esp_err_t ret = attemptToPerformOta(&config); - Serial.println(ret); - if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { - Serial.println("OTA update performed, restarting ..."); - esp_restart(); - } + esp_http_client_config_t config = {}; + config.url = urlAsChar; + esp_err_t ret = attemptToPerformOta(&config); + Serial.println(ret); + if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { + Serial.println("OTA update performed, restarting ..."); + esp_restart(); } + } private: + OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config) { + esp_http_client_handle_t client = esp_http_client_init(config); + if (client == NULL) { + Serial.println("Failed to initialize HTTP connection"); + return OtaUpdateOutcome::UPDATE_FAILED; + } - OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config) { - esp_http_client_handle_t client = esp_http_client_init(config); - if (client == NULL) { - Serial.println("Failed to initialize HTTP connection"); - return OtaUpdateOutcome::UPDATE_FAILED; - } + esp_err_t err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + esp_http_client_cleanup(client); + Serial.printf("Failed to open HTTP connection: %s\n", + esp_err_to_name(err)); + return OtaUpdateOutcome::UPDATE_FAILED; + } + esp_http_client_fetch_headers(client); - esp_err_t err = esp_http_client_open(client, 0); - if (err != ESP_OK) { - esp_http_client_cleanup(client); - Serial.printf("Failed to open HTTP connection: %s\n", esp_err_to_name(err)); - return OtaUpdateOutcome::UPDATE_FAILED; - } - esp_http_client_fetch_headers(client); + int httpStatusCode = esp_http_client_get_status_code(client); + if (httpStatusCode == 304) { + Serial.println("Firmware is already up to date"); + cleanupHttp(client); + return OtaUpdateOutcome::ALREADY_UP_TO_DATE; + } else if (httpStatusCode != 200) { + Serial.printf("Firmware update skipped, the server returned %d\n", + httpStatusCode); + cleanupHttp(client); + return OtaUpdateOutcome::UDPATE_SKIPPED; + } - int httpStatusCode = esp_http_client_get_status_code(client); - if (httpStatusCode == 304) { - Serial.println("Firmware is already up to date"); - cleanupHttp(client); - return OtaUpdateOutcome::ALREADY_UP_TO_DATE; - } else if (httpStatusCode != 200) { - Serial.printf("Firmware update skipped, the server returned %d\n", httpStatusCode); - cleanupHttp(client); - return OtaUpdateOutcome::UDPATE_SKIPPED; - } + esp_ota_handle_t update_handle = 0; + const esp_partition_t *update_partition = NULL; + Serial.println("Starting OTA update ..."); + update_partition = esp_ota_get_next_update_partition(NULL); + if (update_partition == NULL) { + Serial.println("Passive OTA partition not found"); + cleanupHttp(client); + return OtaUpdateOutcome::UPDATE_FAILED; + } + Serial.printf("Writing to partition subtype %d at offset 0x%x\n", + update_partition->subtype, update_partition->address); - esp_ota_handle_t update_handle = 0; - const esp_partition_t *update_partition = NULL; - Serial.println("Starting OTA update ..."); - update_partition = esp_ota_get_next_update_partition(NULL); - if (update_partition == NULL) { - Serial.println("Passive OTA partition not found"); - cleanupHttp(client); - return OtaUpdateOutcome::UPDATE_FAILED; - } - Serial.printf("Writing to partition subtype %d at offset 0x%x\n", - update_partition->subtype, update_partition->address); + err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); + if (err != ESP_OK) { + Serial.printf("esp_ota_begin failed, error=%d\n", err); + cleanupHttp(client); + return OtaUpdateOutcome::UPDATE_FAILED; + } - err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); - if (err != ESP_OK) { - Serial.printf("esp_ota_begin failed, error=%d\n", err); - cleanupHttp(client); - return OtaUpdateOutcome::UPDATE_FAILED; - } - - esp_err_t ota_write_err = ESP_OK; - char *upgrade_data_buf = (char *)malloc(OTA_BUF_SIZE); - if (!upgrade_data_buf) { - Serial.println("Couldn't allocate memory for data buffer"); - return OtaUpdateOutcome::UPDATE_FAILED; - } + esp_err_t ota_write_err = ESP_OK; + char *upgrade_data_buf = (char *)malloc(OTA_BUF_SIZE); + if (!upgrade_data_buf) { + Serial.println("Couldn't allocate memory for data buffer"); + return OtaUpdateOutcome::UPDATE_FAILED; + } - int binary_file_len = 0; - while (1) { - int data_read = esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE); - if (data_read == 0) { - Serial.println("Connection closed, all data received"); - break; - } - if (data_read < 0) { - Serial.println("Data read error"); - break; - } - if (data_read > 0) { - ota_write_err = esp_ota_write( update_handle, (const void *)upgrade_data_buf, data_read); - if (ota_write_err != ESP_OK) { - break; - } - binary_file_len += data_read; - // Serial.printf("Written image length %d\n", binary_file_len); - } - } - free(upgrade_data_buf); - cleanupHttp(client); - Serial.printf("# of bytes written: %d\n", binary_file_len); - - esp_err_t ota_end_err = esp_ota_end(update_handle); + int binary_file_len = 0; + while (1) { + int data_read = + esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE); + if (data_read == 0) { + Serial.println("Connection closed, all data received"); + break; + } + if (data_read < 0) { + Serial.println("Data read error"); + break; + } + if (data_read > 0) { + ota_write_err = esp_ota_write( + update_handle, (const void *)upgrade_data_buf, data_read); if (ota_write_err != ESP_OK) { - Serial.printf("Error: esp_ota_write failed! err=0x%d\n", err); - return OtaUpdateOutcome::UPDATE_FAILED; - } else if (ota_end_err != ESP_OK) { - Serial.printf("Error: esp_ota_end failed! err=0x%d. Image is invalid", ota_end_err); - return OtaUpdateOutcome::UPDATE_FAILED; + break; } + binary_file_len += data_read; + // Serial.printf("Written image length %d\n", binary_file_len); + } + } + free(upgrade_data_buf); + cleanupHttp(client); + Serial.printf("# of bytes written: %d\n", binary_file_len); - err = esp_ota_set_boot_partition(update_partition); - if (err != ESP_OK) { - Serial.printf("esp_ota_set_boot_partition failed! err=0x%d\n", err); - return OtaUpdateOutcome::UPDATE_FAILED; - } - return OtaUpdateOutcome::UPDATE_PERFORMED; + esp_err_t ota_end_err = esp_ota_end(update_handle); + if (ota_write_err != ESP_OK) { + Serial.printf("Error: esp_ota_write failed! err=0x%d\n", err); + return OtaUpdateOutcome::UPDATE_FAILED; + } else if (ota_end_err != ESP_OK) { + Serial.printf("Error: esp_ota_end failed! err=0x%d. Image is invalid", + ota_end_err); + return OtaUpdateOutcome::UPDATE_FAILED; } - void cleanupHttp(esp_http_client_handle_t client) { - esp_http_client_close(client); - esp_http_client_cleanup(client); + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + Serial.printf("esp_ota_set_boot_partition failed! err=0x%d\n", err); + return OtaUpdateOutcome::UPDATE_FAILED; } - + return OtaUpdateOutcome::UPDATE_PERFORMED; + } + + void cleanupHttp(esp_http_client_handle_t client) { + esp_http_client_close(client); + esp_http_client_cleanup(client); + } }; #endif diff --git a/platformio.ini b/platformio.ini index af8e14f..fed429c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,7 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -[env:esp32-c3-devkitm-1] +[env:esp32-c3] platform = espressif32 board = esp32-c3-devkitm-1 framework = arduino @@ -17,8 +17,34 @@ board_build.partitions = partitions.csv monitor_speed = 115200 lib_deps = aglib=symlink://../arduino + EEPROM + WebServer + ESPmDNS + FS + SPIFFS + HTTPClient + WiFiClientSecure + Update + DNSServer +monitor_filters = time + +[env:esp8266] +platform = espressif8266 +board = d1_mini +framework = arduino +monitor_speed = 115200 +lib_deps = + aglib=symlink://../arduino + EEPROM + ESP8266HTTPClient + ESP8266WebServer + DNSServer monitor_filters = time [platformio] src_dir = examples/OneOpenAir +; src_dir = examples/BASIC +; src_dir = examples/TestCO2 +; src_dir = examples/TestPM +; src_dir = examples/TestSht diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 09e3883..7027895 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -1,6 +1,14 @@ #include "AgConfigure.h" -#include "EEPROM.h" #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" +#if ESP32 +#include "FS.h" +#include "SPIFFS.h" +#else +#include "EEPROM.h" +#endif + +#define EEPROM_CONFIG_SIZE 512 +#define CONFIG_FILE_NAME "/cfg.bin" const char *CONFIGURATION_CONTROL_NAME[] = { [ConfigurationControlLocal] = "local", @@ -54,10 +62,19 @@ void Configuration::saveConfig(void) { for (int i = 0; i < sizeof(config); i++) { EEPROM.write(i, data[i]); } -#else - EEPROM.writeBytes(0, &config, sizeof(config)); -#endif EEPROM.commit(); +#else + File file = SPIFFS.open(CONFIG_FILE_NAME, "w", true); + if (file && !file.isDirectory()) { + if (file.write((const uint8_t *)&config, sizeof(config)) != + sizeof(config)) { + logError("Write SPIFFS file failed"); + } + file.close(); + } else { + logError("Open SPIFFS file to write failed"); + } +#endif logInfo("Save Config"); } @@ -70,8 +87,12 @@ void Configuration::loadConfig(void) { } readSuccess = true; #else - if (EEPROM.readBytes(0, &config, sizeof(config)) == sizeof(config)) { - readSuccess = true; + File file = SPIFFS.open(CONFIG_FILE_NAME); + if (file && !file.isDirectory()) { + if (file.readBytes((char *)&config, sizeof(config)) == sizeof(config)) { + readSuccess = true; + } + file.close(); } #endif @@ -104,6 +125,15 @@ void Configuration::loadConfig(void) { changed = true; logError("LedBarMode invalid, set default: co2"); } + if (config.ledBarBrightness > 100) { + config.ledBarBrightness = 100; + changed = true; + } + if (config.displayBrightness > 100) { + config.displayBrightness = 100; + changed = true; + } + if (changed) { saveConfig(); } @@ -131,6 +161,8 @@ void Configuration::defaultConfig(void) { config.tvocLearningOffset = 12; config.noxLearningOffset = 12; config.temperatureUnit = 'c'; + config.ledBarBrightness = 100; + config.displayBrightness = 100; saveConfig(); } @@ -162,7 +194,20 @@ Configuration::~Configuration() {} * @return false Failure */ bool Configuration::begin(void) { - EEPROM.begin(512); + if (sizeof(config) > EEPROM_CONFIG_SIZE) { + logError("Configuration over EEPROM_CONFIG_SIZE"); + return false; + } + +#ifdef ESP32 + if (!SPIFFS.begin(true)) { + logError("Init SPIFFS failed"); + return false; + } +#else + EEPROM.begin(EEPROM_CONFIG_SIZE); +#endif + loadConfig(); printConfig(); @@ -298,7 +343,8 @@ bool Configuration::parse(String data, bool isLocal) { } if (inUSAQI != config.inUSAQI) { - configLogInfo("pmStandard", getPMStandardString(config.inUSAQI), pmStandard); + configLogInfo("pmStandard", getPMStandardString(config.inUSAQI), + pmStandard); config.inUSAQI = inUSAQI; changed = true; } @@ -312,7 +358,7 @@ bool Configuration::parse(String data, bool isLocal) { if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") { co2CalibrationRequested = root["co2CalibrationRequested"]; - if(co2CalibrationRequested) { + if (co2CalibrationRequested) { logInfo("co2CalibrationRequested: " + String(co2CalibrationRequested ? "True" : "False")); } @@ -327,7 +373,7 @@ bool Configuration::parse(String data, bool isLocal) { if (JSON.typeof_(root["ledBarTestRequested"]) == "boolean") { ledBarTestRequested = root["ledBarTestRequested"]; - if(ledBarTestRequested){ + if (ledBarTestRequested) { logInfo("ledBarTestRequested: " + String(ledBarTestRequested ? "True" : "False")); } @@ -384,7 +430,8 @@ bool Configuration::parse(String data, bool isLocal) { if (displayMode != config.displayMode) { changed = true; - configLogInfo("displayMode", getDisplayModeString(config.displayMode), mode); + configLogInfo("displayMode", getDisplayModeString(config.displayMode), + mode); config.displayMode = displayMode; } } else { @@ -476,9 +523,9 @@ bool Configuration::parse(String data, bool isLocal) { if (JSON.typeof_(root["temperatureUnit"]) == "string") { String unit = root["temperatureUnit"]; unit.toLowerCase(); - if ((unit == "c") || (unit == "celsius")) { + if (unit == "c") { temperatureUnit = 'c'; - } else if ((unit == "f") || (unit == "fahrenheit")) { + } else if (unit == "f") { temperatureUnit = 'f'; } else { failedMessage = "'temperatureUnit' value '" + unit + "' invalid"; @@ -545,6 +592,52 @@ bool Configuration::parse(String data, bool isLocal) { } } + if (JSON.typeof_(root["ledbarBrightness"]) == "number") { + int brightnress = root["ledbarBrightness"]; + if (brightnress != config.ledBarBrightness) { + if (brightnress <= 100) { + changed = true; + configLogInfo("ledbarBrightness", String(config.ledBarBrightness), + String(brightnress)); + ledBarBrightnessChanged = true; + config.ledBarBrightness = (uint8_t)brightnress; + } else { + failedMessage = + "\"ledbarBrightness\" value invalid: " + String(brightnress); + return false; + } + } + } else { + if (jsonTypeInvalid(root["ledbarBrightness"], "number")) { + failedMessage = jsonTypeInvalidMessage("ledbarBrightness", "number"); + jsonInvalid(); + return false; + } + } + + if (JSON.typeof_(root["displayBrightness"]) == "number") { + int brightness = root["displayBrightness"]; + if (brightness != config.displayBrightness) { + if (brightness <= 100) { + changed = true; + displayBrightnessChanged = true; + configLogInfo("displayBrightness", String(config.displayBrightness), + String(brightness)); + config.displayBrightness = (uint8_t)brightness; + } else { + failedMessage = + "\"displayBrightness\" value invalid: " + String(brightness); + return false; + } + } + } else { + if (jsonTypeInvalid(root["displayBrightness"], "number")) { + failedMessage = jsonTypeInvalidMessage("displayBrightness", "number"); + jsonInvalid(); + return false; + } + } + if (changed) { udpated = true; saveConfig(); @@ -607,6 +700,12 @@ String Configuration::toString(void) { /** "postDataToAirGradient" */ root["postDataToAirGradient"] = config.postDataToAirGradient; + /** Led bar brightness */ + root["ledbarBrightness"] = config.ledBarBrightness; + + /** Display brightness */ + root["displayBrightness"] = config.displayBrightness; + return JSON.stringify(root); } @@ -770,15 +869,15 @@ String Configuration::getPMStandardString(bool usaqi) { return "ugm3"; } -String Configuration::getDisplayModeString(bool dispMode) { - if(dispMode){ +String Configuration::getDisplayModeString(bool dispMode) { + if (dispMode) { return String("on"); } return String("off"); } -String Configuration::getAbcDayString(int value) { - if(value <= 0){ +String Configuration::getAbcDayString(int value) { + if (value <= 0) { return String("off"); } return String(value); @@ -816,10 +915,26 @@ int Configuration::getNoxLearningOffset(void) { return config.noxLearningOffset; } -String Configuration::wifiSSID(void) { - return "airgradient-" + ag->deviceId(); -} +String Configuration::wifiSSID(void) { return "airgradient-" + ag->deviceId(); } String Configuration::wifiPass(void) { return String("cleanair"); } -void Configuration::setAirGradient(AirGradient *ag) { this->ag = ag;} +void Configuration::setAirGradient(AirGradient *ag) { this->ag = ag; } + +int Configuration::getLedBarBrightness(void) { return config.ledBarBrightness; } + +bool Configuration::isLedBarBrightnessChanged(void) { + bool changed = ledBarBrightnessChanged; + ledBarBrightnessChanged = false; + return changed; +} + +int Configuration::getDisplayBrightness(void) { + return config.displayBrightness; +} + +bool Configuration::isDisplayBrightnessChanged(void) { + bool changed = displayBrightnessChanged; + displayBrightnessChanged = false; + return changed; +} diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 4de0c0c..6759ba6 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -23,6 +23,8 @@ private: bool displayMode; /** true if enable display */ uint8_t useRGBLedBar; uint8_t abcDays; + uint8_t ledBarBrightness; + uint8_t displayBrightness; int tvocLearningOffset; int noxLearningOffset; char temperatureUnit; // 'f' or 'c' @@ -36,6 +38,8 @@ private: String failedMessage; bool _noxLearnOffsetChanged; bool _tvocLearningOffsetChanged; + bool ledBarBrightnessChanged = false; + bool displayBrightnessChanged = false; AirGradient* ag; @@ -89,6 +93,10 @@ public: String wifiSSID(void); String wifiPass(void); void setAirGradient(AirGradient *ag); + bool isLedBarBrightnessChanged(void); + int getLedBarBrightness(void); + bool isDisplayBrightnessChanged(void); + int getDisplayBrightness(void); }; #endif /** _AG_CONFIG_H_ */ diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 1f5670f..498599c 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -93,6 +93,8 @@ bool OledDisplay::begin(void) { return false; } + setBrightness(config.getDisplayBrightness()); + isBegin = true; logInfo("begin"); return true; @@ -285,3 +287,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(85, 63, strBuf); } while (DISP()->nextPage()); } + +void OledDisplay::setBrightness(int percent) { + DISP()->setContrast((127 * percent) / 100); +} diff --git a/src/AgOledDisplay.h b/src/AgOledDisplay.h index 7def823..ee4af77 100644 --- a/src/AgOledDisplay.h +++ b/src/AgOledDisplay.h @@ -31,6 +31,7 @@ public: const char *line4); void showDashboard(void); void showDashboard(const char *status); + void setBrightness(int percent); }; #endif /** _AG_OLED_DISPLAY_H_ */ diff --git a/src/Main/LedBar.cpp b/src/Main/LedBar.cpp index 2627d55..b00f36c 100644 --- a/src/Main/LedBar.cpp +++ b/src/Main/LedBar.cpp @@ -62,13 +62,13 @@ void LedBar::setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum) { /** * @brief Set LED brightness apply for all LED bar * - * @param brightness Brightness (0 - 255) + * @param brightness Brightness (0 - 100)% */ void LedBar::setBrighness(uint8_t brightness) { if (this->isBegin() == false) { return; } - pixel()->setBrightness(brightness); + pixel()->setBrightness((brightness * 0xff) / 100); } /**