diff --git a/data/default_conn.json b/data/default_conn.json index a3eacd6b..0888b9d4 100644 --- a/data/default_conn.json +++ b/data/default_conn.json @@ -4,7 +4,7 @@ "user":"admin", "pwd":"admin", "ota_enabled":false, - "ota_password":"changeit!", + "ota_password":"changeit", "accesspoint":false, "ap_ssid":"esp32cam", "ap_pass":"123456789", diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 00000000..30f41a55 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1A0000, +app1, app, ota_1, , 0x1A0000, +spiffs, data, spiffs, , 0x0A0000, +coredump, data, coredump,0x3F0000, 0x10000, \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 35d770e8..63c2a4a9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,53 +1,65 @@ ; PlatformIO Project Configuration File ; https://docs.platformio.org/page/projectconf.html -; ================================================================ -; = = -; = DO NOT USE PLATFORMIO FOR THIS PROJECT = -; = = -; = 14 March 2022 : When built with Platformio this project = -; = does not run properly; any streams you start will fail = -; = with 'ESP_ERR_HTTPD_RESP_SEND' in the serial console and = -; = ERR_INVALID_CHUNK_ENCODING 200 (OK) in the browser console.= -; = = -; = This is a difficult issue, and currently unresolved. = -; = See: = -; = https://github.com/easytarget/esp32-cam-webserver/issues/218 = -; = = -; = The focus of thei project is to show a expanded example = -; = using the official Arduino IDE. PlatformIO is, and has = -; = always been, a nice-to-have; so I will be releasing 4.x = -; = without platformio support. See the above github issue if = -; = you are able to help fixing this. = -; = = -; ================================================================ -; -; The esp32-cam-webserver project is intended to be easily compilable -; with the stock Arduino IDE. -; - Maintaining compatibility with other development environments -; is important, but I wont accept changes to the PlatformIO build that -; break compatibilty with the stock IDE. Eg by using non-standard -; partition schemes or overriding Arduino defined limits, etc. +[env] +monitor_speed = 115200 +upload_speed = 921600 +platform = espressif32 +framework = arduino +monitor_rts = 0 +monitor_dtr = 0 +; https://registry.platformio.org/tools/espressif/toolchain-riscv32-esp/versions +;platform_packages = espressif/toolchain-riscv32-esp +board_build.partitions = partitions.csv +;board_build.partitions = min_spiffs.csv +board_build.filesystem = littlefs +monitor_filters = esp32_exception_decoder +build_flags = + !python scripts/build_flags.py git_branch + !python scripts/build_flags.py git_repo + !python scripts/build_flags.py git_owner + -D GITHUB_RUN=\"${sysenv.GITHUB_RUN}\" +; -D WIFISSID=\"gast\" ; use fixed WiFi credentials if Improv-WiFi-Library isn´t supported by your board +; -D WIFIPASSWORD=\"12345678\" ; use fixed WiFi credentials if Improv-WiFi-Library isn´t supported by your board +lib_deps = + https://github.com/mathieucarbou/ESPAsyncWebServer ; installing by ElegantOTA + https://github.com/espressif/json_generator.git + https://github.com/espressif/json_parser.git + bblanchon/ArduinoJson@>6.19.0 + ;https://github.com/tobiasfaust/Improv-WiFi-Library.git + ;https://github.com/tobiasfaust/ElegantOTA.git + + +[env:firmware_ESP32] +board = esp32dev +build_flags = ${env.build_flags} + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue + -D USE_LittleFS=1 + -D CAMERA_MODEL_AI_THINKER=1 -[platformio] -src_dir = ./ [env:esp32dev] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream -platform_packages = framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#2.0.3 +platform = espressif32 board = esp32dev -board_build.partitions = min_spiffs.csv framework = arduino +board_build.filesystem = littlefs build_flags = -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue + -D USE_LittleFS=1 + -D CAMERA_MODEL_AI_THINKER=1 +; -D CAMERA_MODEL_WROVER_KIT=1 # default +; -D CAMERA_MODEL_ESP_EYE=1 +; -D CAMERA_MODEL_M5STACK_PSRAM=1 +; -D CAMERA_MODEL_M5STACK_V2_PSRAM=1 +; -D CAMERA_MODEL_M5STACK_WIDE=1 +; -D CAMERA_MODEL_M5STACK_ESP32CAM=1 # Originally: CAMERA_MODEL_M5STACK_NO_PSRAM +; -D CAMERA_MODEL_TTGO_T_JOURNAL=1 +; -D CAMERA_MODEL_ARDUCAM_ESP32S_UNO=1 +; -D CAMERA_MODEL_LILYGO_T_SIMCAM=1 -; using the latest version of libraries -lib_deps = - https://github.com/me-no-dev/ESPAsyncWebServer.git - https://github.com/espressif/json_generator.git - https://github.com/espressif/json_parser.git ; For OTA uploading uncomment the next lines and add the IP address or mDNS name of the camera module, and the OTA password ;upload_protocol = espota diff --git a/scripts/build_flags.py b/scripts/build_flags.py new file mode 100644 index 00000000..6dd920f9 --- /dev/null +++ b/scripts/build_flags.py @@ -0,0 +1,21 @@ +import subprocess; +import sys; +import os, re; + +def git_branch(): + print('-D GIT_BRANCH=\\"%s\\"' % subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip().decode()) + +def git_repo(): + output = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']) + repo = os.path.basename(output).strip().decode() + print('-D GIT_REPO=\\"%s\\"' % repo); + +def git_owner(): + # get the github owner of the origin repo + output = subprocess.check_output(['git', 'config', '--get', 'remote.origin.url']) + owner = re.search(r'github.com[:/](.*)/', output.decode()).group(1) + print('-D GIT_OWNER=\\"%s\\"' % owner); + + +if __name__ == '__main__': + globals()[sys.argv[1]]() diff --git a/src/app_cam.cpp b/src/app_cam.cpp index 556f1201..2c2d109d 100644 --- a/src/app_cam.cpp +++ b/src/app_cam.cpp @@ -21,8 +21,8 @@ int CLAppCam::start() { config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; - config.pin_sscb_sda = SIOD_GPIO_NUM; - config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_sccb_sda = SIOD_GPIO_NUM; + config.pin_sccb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = xclk * 1000000; @@ -81,91 +81,85 @@ int CLAppCam::stop() { } int CLAppCam::loadPrefs() { - jparse_ctx_t jctx; - int ret = parsePrefs(&jctx); + JsonDocument json; + int ret = parsePrefs(json); if(ret != OS_SUCCESS) { return ret; } // process local settings - json_obj_get_int(&jctx, (char*)"frame_rate", &frameRate); - json_obj_get_int(&jctx, (char*)"xclk", &xclk); - json_obj_get_int(&jctx, (char*)"rotate", &myRotation); + this->frameRate = json["frame_rate"]; + this->xclk = json["xclk"]; + this->myRotation = json["rotate"]; // get sensor reference sensor_t * s = esp_camera_sensor_get(); // process camera settings if(s) { - s->set_framesize(s, (framesize_t)readJsonIntVal(&jctx, "framesize")); - s->set_quality(s, readJsonIntVal(&jctx, "quality")); + s->set_framesize(s, (framesize_t)json["framesize"]); + s->set_quality(s, json["quality"]); s->set_xclk(s, LEDC_TIMER_0, xclk); - s->set_brightness(s, readJsonIntVal(&jctx, "brightness")); - s->set_contrast(s, readJsonIntVal(&jctx, "contrast")); - s->set_saturation(s, readJsonIntVal(&jctx, "saturation")); - s->set_sharpness(s, readJsonIntVal(&jctx, "sharpness")); - s->set_denoise(s, readJsonIntVal(&jctx, "denoise")); - s->set_special_effect(s, readJsonIntVal(&jctx, "special_effect")); - s->set_wb_mode(s, readJsonIntVal(&jctx, "wb_mode")); - s->set_whitebal(s, readJsonIntVal(&jctx, "awb")); - s->set_awb_gain(s, readJsonIntVal(&jctx, "awb_gain")); - s->set_exposure_ctrl(s, readJsonIntVal(&jctx, "aec")); - s->set_aec2(s, readJsonIntVal(&jctx, "aec2")); - s->set_ae_level(s, readJsonIntVal(&jctx, "ae_level")); - s->set_aec_value(s, readJsonIntVal(&jctx, "aec_value")); - s->set_gain_ctrl(s, readJsonIntVal(&jctx, "agc")); - s->set_agc_gain(s, readJsonIntVal(&jctx, "agc_gain")); - s->set_gainceiling(s, (gainceiling_t)readJsonIntVal(&jctx, "gainceiling")); - s->set_bpc(s, readJsonIntVal(&jctx, "bpc")); - s->set_wpc(s, readJsonIntVal(&jctx, "wpc")); - s->set_raw_gma(s, readJsonIntVal(&jctx, "raw_gma")); - s->set_lenc(s, readJsonIntVal(&jctx, "lenc")); - s->set_vflip(s, readJsonIntVal(&jctx, "vflip")); - s->set_hmirror(s, readJsonIntVal(&jctx, "hmirror")); - s->set_dcw(s, readJsonIntVal(&jctx, "dcw")); - s->set_colorbar(s, readJsonIntVal(&jctx, "colorbar")); + s->set_brightness(s, json["brightness"]); + s->set_contrast(s, json["contrast"]); + s->set_saturation(s, json["saturation"]); + s->set_sharpness(s, json["sharpness"]); + s->set_denoise(s, json["denoise"]); + s->set_special_effect(s, json["special_effect"]); + s->set_wb_mode(s, json["wb_mode"]); + s->set_whitebal(s, json["awb"]); + s->set_awb_gain(s, json["awb_gain"]); + s->set_exposure_ctrl(s, json["aec"]); + s->set_aec2(s, json["aec2"]); + s->set_ae_level(s, json["ae_level"]); + s->set_aec_value(s, json["aec_value"]); + s->set_gain_ctrl(s, json["agc"]); + s->set_agc_gain(s, json["agc_gain"]); + s->set_gainceiling(s, (gainceiling_t)json["gainceiling"]); + s->set_bpc(s, json["bpc"]); + s->set_wpc(s, json["wpc"]); + s->set_raw_gma(s, json["raw_gma"]); + s->set_lenc(s, json["lenc"]); + s->set_vflip(s, json["vflip"]); + s->set_hmirror(s, json["hmirror"]); + s->set_dcw(s, json["dcw"]); + s->set_colorbar(s, json["colorbar"]); + + if(json["debug_mode"]) setDebugMode(json["debug_mode"]); - bool dbg; - if(json_obj_get_bool(&jctx, (char*)"debug_mode", &dbg) == OS_SUCCESS) - setDebugMode(dbg); } else { Serial.println("Failed to get camera handle. Camera settings skipped"); } - - // close the file - json_parse_end(&jctx); + return ret; } int CLAppCam::savePrefs(){ - char * prefs_file = getPrefsFileName(true); + JsonDocument json; + char* prefs_file = getPrefsFileName(true); if (Storage.exists(prefs_file)) { - Serial.print("Updating "); + Serial.printf("Updating %s\r\n", prefs_file); } else { - Serial.print("Creating "); + Serial.printf("Creating %s\r\n", prefs_file); } - Serial.println(prefs_file); - - char buf[CAM_DUMP_BUFFER_SIZE]; - json_gen_str_t jstr; - json_gen_str_start(&jstr, buf, sizeof(buf), NULL, NULL); - json_gen_start_object(&jstr); - - dumpStatusToJson(&jstr); - json_gen_end_object(&jstr); - json_gen_str_end(&jstr); + dumpStatusToJson(json); File file = Storage.open(prefs_file, FILE_WRITE); if(file) { - file.print(buf); + serializeJson(json, file); + serializeJsonPretty(json, Serial); + Serial.println(); + file.close(); + Serial.printf("File %s updated\r\n", prefs_file); + return OK; } else { - Serial.print("Failed to save camera preferences to file "); Serial.println(prefs_file); + Serial.printf("Failed to save camery preferences to file %s\r\n", prefs_file); return FAIL; } @@ -184,50 +178,47 @@ void IRAM_ATTR CLAppCam::releaseBuffer() { } } -void CLAppCam::dumpStatusToJson(json_gen_str_t * jstr, bool full_status) { - +void CLAppCam::dumpStatusToJson(JsonDocument& json, bool full_status) { - json_gen_obj_set_int(jstr, (char*)"rotate", myRotation); + json["rotate"] = this->myRotation; if(getLastErr()) return; sensor_t * s = esp_camera_sensor_get(); - json_gen_obj_set_int(jstr, (char*)"cam_pid", s->id.PID); - json_gen_obj_set_int(jstr, (char*)"cam_ver", s->id.VER); - json_gen_obj_set_int(jstr, (char*)"framesize", s->status.framesize); - json_gen_obj_set_int(jstr, (char*)"frame_rate", frameRate); + json["cam_pid"] = s->id.PID; + json["cam_ver"] = s->id.VER; + json["framesize"] = s->status.framesize; + json["frame_rate"] = this->frameRate; if(!full_status) return; - json_gen_obj_set_int(jstr, (char*)"quality", s->status.quality); - json_gen_obj_set_int(jstr, (char*)"brightness", s->status.brightness); - json_gen_obj_set_int(jstr, (char*)"contrast", s->status.contrast); - json_gen_obj_set_int(jstr, (char*)"saturation", s->status.saturation); - json_gen_obj_set_int(jstr, (char*)"sharpness", s->status.sharpness); - json_gen_obj_set_int(jstr, (char*)"denoise", s->status.denoise); - json_gen_obj_set_int(jstr, (char*)"special_effect", s->status.special_effect); - json_gen_obj_set_int(jstr, (char*)"wb_mode", s->status.wb_mode); - json_gen_obj_set_int(jstr, (char*)"awb", s->status.awb); - json_gen_obj_set_int(jstr, (char*)"awb_gain", s->status.awb_gain); - json_gen_obj_set_int(jstr, (char*)"aec", s->status.aec); - json_gen_obj_set_int(jstr, (char*)"aec2", s->status.aec2); - json_gen_obj_set_int(jstr, (char*)"ae_level", s->status.ae_level); - json_gen_obj_set_int(jstr, (char*)"aec_value", s->status.aec_value); - json_gen_obj_set_int(jstr, (char*)"agc", s->status.agc); - json_gen_obj_set_int(jstr, (char*)"agc_gain", s->status.agc_gain); - json_gen_obj_set_int(jstr, (char*)"gainceiling", s->status.gainceiling); - json_gen_obj_set_int(jstr, (char*)"bpc", s->status.bpc); - json_gen_obj_set_int(jstr, (char*)"wpc", s->status.wpc); - json_gen_obj_set_int(jstr, (char*)"raw_gma", s->status.raw_gma); - json_gen_obj_set_int(jstr, (char*)"lenc", s->status.lenc); - json_gen_obj_set_int(jstr, (char*)"vflip", s->status.vflip); - json_gen_obj_set_int(jstr, (char*)"hmirror", s->status.hmirror); - json_gen_obj_set_int(jstr, (char*)"dcw", s->status.dcw); - json_gen_obj_set_int(jstr, (char*)"colorbar", s->status.colorbar); - json_gen_obj_set_bool(jstr, (char*)"debug_mode", isDebugMode()); - - json_gen_obj_set_int(jstr, (char*)"xclk", xclk); - + json["quality"] = s->status.quality; + json["brightness"] = s->status.brightness; + json["contrast"] = s->status.contrast; + json["saturation"] = s->status.saturation; + json["sharpness"] = s->status.sharpness; + json["denoise"] = s->status.denoise; + json["special_effect"] = s->status.special_effect; + json["wb_mode"] = s->status.wb_mode; + json["awb"] = s->status.awb; + json["awb_gain"] = s->status.awb_gain; + json["aec"] = s->status.aec; + json["aec2"] = s->status.aec2; + json["ae_level"] = s->status.ae_level; + json["aec_value"] = s->status.aec_value; + json["agc"] = s->status.agc; + json["agc_gain"] = s->status.agc_gain; + json["gainceiling"] = s->status.gainceiling; + json["bpc"] = s->status.bpc; + json["wpc"] = s->status.wpc; + json["raw_gma"] = s->status.raw_gma; + json["lenc"] = s->status.lenc; + json["vflip"] = s->status.vflip; + json["hmirror"] = s->status.hmirror; + json["dcw"] = s->status.dcw; + json["colorbar"] = s->status.colorbar; + json["debug_mode"] = this->isDebugMode(); + json["xclk"] = this->xclk; } diff --git a/src/app_cam.h b/src/app_cam.h index ef180e57..a5071a48 100644 --- a/src/app_cam.h +++ b/src/app_cam.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "app_component.h" #include "camera_pins.h" @@ -44,7 +45,7 @@ class CLAppCam : public CLAppComponent { bool IRAM_ATTR isJPEGinBuffer() {return (fb?fb->format == PIXFORMAT_JPEG:false);}; void releaseBuffer(); - void dumpStatusToJson(json_gen_str_t * jstr, bool full_status = true); + void dumpStatusToJson(JsonDocument& json, bool full_status = true); private: // Camera config structure diff --git a/src/app_component.cpp b/src/app_component.cpp index da719f29..7f854bda 100644 --- a/src/app_component.cpp +++ b/src/app_component.cpp @@ -54,6 +54,33 @@ int CLAppComponent::removePrefs() { return OS_SUCCESS; } +int CLAppComponent::parsePrefs(JsonDocument& json) { + char* conn_file = getPrefsFileName(); + + if (Storage.exists(conn_file)) { + //file exists, reading and loading + Serial.printf("Open config file %s \r\n", conn_file); + + File configFile = Storage.open(conn_file); + if (configFile) { + Serial.printf("Config file %s opened \r\n", conn_file); + + DeserializationError error = deserializeJson(json, configFile); + + if (!error) { + serializeJsonPretty(json, Serial); + Serial.println(); + return OS_SUCCESS; + } + } else { + Serial.printf("Failed to open the connection settings from %s \r\n", conn_file); + } + } else { + Serial.printf("Preference file %s not exists.\r\n", conn_file); + } + return OS_FAIL; +} + int CLAppComponent::parsePrefs(jparse_ctx_t *jctx) { char *conn_file = getPrefsFileName(); @@ -75,7 +102,71 @@ int CLAppComponent::parsePrefs(jparse_ctx_t *jctx) { return ret; } -int CLAppComponent::urlDecode(char * decoded, char * source, size_t len) { +unsigned char CLAppComponent::hex2int(char c) { + if (c >= '0' && c <='9'){ + return((unsigned char)c - '0'); + } + if (c >= 'a' && c <='f'){ + return((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <='F'){ + return((unsigned char)c - 'A' + 10); + } + return(0); +} + +int CLAppComponent::urlDecode(String& decoded, const char* source) { + int len = strlen(source); + decoded.reserve(len); + for (int i = 0; i < len; i++) { + if (source[i] == '%') { + if (i + 2 < len) { + char hex[3] = { source[i + 1], source[i + 2], '\0' }; + decoded += static_cast(strtol(hex, nullptr, 16)); + i += 2; + } + } else if (source[i] == '+') { + decoded += ' '; + } else { + decoded += source[i]; + } + } + return OS_SUCCESS; +} + +int CLAppComponent::urlEncode(String& encoded, const char* source) { + char c; + char code0; + char code1; + char code2; + for (int i = 0; i < strlen(source); i++){ + c=source[i]; + if (c == ' '){ + encoded += '+'; + } else if (isalnum(c)){ + encoded +=c; + } else{ + code1=(c & 0xf)+'0'; + if ((c & 0xf) >9){ + code1=(c & 0xf) - 10 + 'A'; + } + c=(c>>4)&0xf; + code0=c+'0'; + if (c > 9){ + code0=c - 10 + 'A'; + } + code2='\0'; + encoded +='%'; + encoded +=code0; + encoded +=code1; + //encodedString+=code2; + } + yield(); + } + return OS_SUCCESS; +} + +int CLAppComponent::urlDecode(char* decoded, char * source, size_t len) { char temp[] = "0x00"; int i=0; char * ptr = decoded; diff --git a/src/app_component.h b/src/app_component.h index 8ca44944..18c7d9d2 100644 --- a/src/app_component.h +++ b/src/app_component.h @@ -3,6 +3,7 @@ #include "json_generator.h" #include "json_parser.h" +#include #if __has_include("../myconfig.h") #include "../myconfig.h" @@ -51,10 +52,15 @@ class CLAppComponent { int readJsonIntVal(jparse_ctx_t *jctx, const char* token); int parsePrefs(jparse_ctx_t *jctx); + int parsePrefs(JsonDocument& json); int urlDecode(char * decoded, char * source, size_t len); int urlEncode(char * encoded, char * source, size_t len); + unsigned char hex2int(char c); + int urlDecode(String& decoded, const char* source); + int urlEncode(String& encoded, const char* source); + private: // prefix for forming preference file name of this class diff --git a/src/app_config.h b/src/app_config.h index 9cb2ac5a..d288d165 100644 --- a/src/app_config.h +++ b/src/app_config.h @@ -26,13 +26,9 @@ // #define LED_DISABLE // Uncomment this line to use LittleFS instead of SD. -// NOTE! -// LittleFS is still experimental, not recommended. The 'official' library installed from the Library Manager -// seems to be broken, but fixed in this PR: https://github.com/lorol/LITTLEFS/pull/56 -// To install it, please navigate to you /libraries sub-folder of your sketch location and then execute -// git clone https://github.com/Michael2MacDonald/LITTLEFS. +// --> to defined in platformio.ini -// #define USE_LittleFS +//#define USE_LittleFS /* * Camera Hardware Selection @@ -42,6 +38,8 @@ * This is not optional */ +// --> to defined in platformio.ini + // #define CAMERA_MODEL_AI_THINKER // default // #define CAMERA_MODEL_WROVER_KIT // #define CAMERA_MODEL_ESP_EYE diff --git a/src/app_conn.cpp b/src/app_conn.cpp index 63b3a088..c69683bf 100644 --- a/src/app_conn.cpp +++ b/src/app_conn.cpp @@ -6,13 +6,15 @@ CLAppConn::CLAppConn() { int CLAppConn::start() { + stationList = new Station[MAX_KNOWN_STATIONS]; + if(loadPrefs() != OK) { return WiFi.status(); } Serial.println("Starting WiFi"); - WiFi.setHostname(mdnsName); + WiFi.setHostname(this->mdnsName.c_str()); WiFi.mode(WIFI_STA); @@ -46,8 +48,8 @@ int CLAppConn::start() { Serial.printf("%3i : [%s] %s (%i)", i + 1, thisBSSID.c_str(), thisSSID.c_str(), thisRSSI); // Scan our list of known external stations for (int sta = 0; sta < stationCount; sta++) { - if ((strcmp(stationList[sta]->ssid, thisSSID.c_str()) == 0) || - (strcmp(stationList[sta]->ssid, thisBSSID.c_str()) == 0)) { + if (stationList[sta].ssid == thisSSID || + stationList[sta].ssid == thisBSSID) { Serial.print(" - Known!"); // Chose the strongest RSSI seen if (thisRSSI > bestRSSI) { @@ -89,7 +91,8 @@ int CLAppConn::start() { // WiFi.setHostname(mdnsName); // Initiate network connection request (3rd argument, channel = 0 is 'auto') - WiFi.begin(bestSSID, stationList[bestStation]->password, 0, bestBSSID); + //WiFi.begin(bestBSSID, stationList[bestStation]->password.c_str(), 0, bestBSSID); + WiFi.begin(stationList[bestStation].ssid.c_str(), stationList[bestStation].password.c_str()); // Wait to connect, or timeout unsigned long start = millis(); @@ -100,7 +103,7 @@ int CLAppConn::start() { // If we have connected, inform user if (WiFi.status() == WL_CONNECTED) { setSSID(WiFi.SSID().c_str()); - setPassword(stationList[bestStation]->password); + setPassword(stationList[bestStation].password); Serial.println(); // Print IP details Serial.printf("IP address: %s\r\n",WiFi.localIP().toString()); @@ -117,7 +120,7 @@ int CLAppConn::start() { if (accesspoint && (WiFi.status() != WL_CONNECTED)) { // The accesspoint has been enabled, and we have not connected to any existing networks - WiFi.softAPsetHostname(mdnsName); + WiFi.softAPsetHostname(this->mdnsName.c_str()); WiFi.mode(WIFI_AP); // reset ap_status @@ -142,7 +145,7 @@ int CLAppConn::start() { // WiFi.softAPsetHostname(mdnsName); - if(!WiFi.softAP(apName, apPass, ap_channel)) { + if(!WiFi.softAP(this->apName.c_str(), this->apPass.c_str(), this->ap_channel)) { Serial.println("Access Point init failed!"); ap_status = WL_CONNECT_FAILED; return wifiStatus(); @@ -169,22 +172,29 @@ int CLAppConn::start() { } void CLAppConn::calcURLs() { + char s[100] = {0}; + memset(s, 0, sizeof(s)); + // Set the URL's // if host name is not defined or access point mode is activated, use local IP for url - if(!strcmp(hostName, "")) { + if(this->hostName != "") { if(accesspoint) - strcpy(hostName, WiFi.softAPIP().toString().c_str()); + this-> hostName = WiFi.softAPIP().toString(); else - strcpy(hostName, WiFi.localIP().toString().c_str()); + this-> hostName = WiFi.localIP().toString(); } if (httpPort != 80) { - snprintf(httpURL, sizeof(httpURL), "http://%s:%d/", hostName, httpPort); - snprintf(streamURL, sizeof(streamURL), "http://%s:%d/view?mode=stream", hostName, httpPort); + snprintf(s, sizeof(s), "http://%s:%d/", hostName, httpPort); + this->httpURL = s; + snprintf(s, sizeof(s), "http://%s:%d/view?mode=stream", hostName, httpPort); + this->streamURL = s; } else { - snprintf(httpURL, sizeof(httpURL), "http://%s/", hostName); - snprintf(streamURL, sizeof(streamURL), "http://%s/view?mode=stream", hostName); + snprintf(s, sizeof(s), "http://%s/", hostName); + this->httpURL = s; + snprintf(s, sizeof(s), "http://%s/view?mode=stream", hostName); + this->streamURL = s; } @@ -192,21 +202,20 @@ void CLAppConn::calcURLs() { int CLAppConn::loadPrefs() { - jparse_ctx_t jctx; - int ret = parsePrefs(&jctx); + JsonDocument json; + int ret = parsePrefs(json); if(ret != OS_SUCCESS) { return ret; } - ret = json_obj_get_string(&jctx, (char*)"mdns_name", mdnsName, sizeof(mdnsName)); + this->mdnsName = json["mdns_name"].as(); - if(ret != OS_SUCCESS) + if(this->mdnsName) { + this->hostName = json["host_name"].as(); + this->httpPort = json["http_port"].as(); + this->dhcp = json["dhcp"].as(); + } else { Serial.println("MDNS Name is not defined!"); - - if(ret == OS_SUCCESS) { - json_obj_get_string(&jctx, (char*)"host_name", hostName, sizeof(hostName)); - json_obj_get_int(&jctx, (char*)"http_port", &httpPort); - json_obj_get_bool(&jctx, (char*)"dhcp", &dhcp); } char sbuf[64]; @@ -214,80 +223,74 @@ int CLAppConn::loadPrefs() { int count; stationCount = 0; - if (ret == OS_SUCCESS && json_obj_get_array(&jctx, (char*)"stations", &count) == OS_SUCCESS) { + if (this->mdnsName && json["stations"]) { Serial.print("Known external SSIDs: "); - if(count>0) + count = json["stations"].as().size(); + if(count > 0) for(int i=0; i < count && i < MAX_KNOWN_STATIONS; i++) { - if(json_arr_get_object(&jctx, i) == OS_SUCCESS) { - if(json_obj_get_string(&jctx, (char*)"ssid", sbuf, sizeof(sbuf)) == OS_SUCCESS && - json_obj_get_string(&jctx, (char*)"pass", dbuf, sizeof(dbuf)) == OS_SUCCESS && - strcmp(sbuf, "") != 0) { - Station *s = (Station*) malloc(sizeof(Station)); - snprintf(s->ssid, sizeof(s->ssid), sbuf); - urlDecode(s->password, dbuf, sizeof(dbuf)); - Serial.println(s->ssid); + if(json["stations"].as()[i]) { + if(json["stations"].as()[i]["ssid"] && + json["stations"].as()[i]["pass"] ) { + Station s; + s.ssid = json["stations"].as()[i]["ssid"].as(); + this->urlDecode(s.password, json["stations"].as()[i]["pass"].as().c_str()); + Serial.println(s.ssid); stationList[i] = s; stationCount++; } - json_arr_leave_object(&jctx); } } else Serial.println("None"); - json_obj_leave_array(&jctx); } // read static IP - if(ret == OS_SUCCESS && json_obj_get_object(&jctx, (char*)"static_ip") == OS_SUCCESS) { - readIPFromJSON(&jctx, &staticIP.ip, (char*)"ip"); - readIPFromJSON(&jctx, &staticIP.netmask, (char*)"netmask"); - readIPFromJSON(&jctx, &staticIP.gateway, (char*)"gateway"); - readIPFromJSON(&jctx, &staticIP.dns1, (char*)"dns1"); - readIPFromJSON(&jctx, &staticIP.dns2, (char*)"dns2"); - json_obj_leave_object(&jctx); + if(this->mdnsName && json["static_ip"]) { + staticIP.ip->fromString(json["static_ip"]["ip"].as()); + staticIP.netmask->fromString(json["static_ip"]["netmask"].as()); + staticIP.gateway->fromString(json["static_ip"]["gateway"].as()); + staticIP.dns1->fromString(json["static_ip"]["dns1"].as()); + staticIP.dns2->fromString(json["static_ip"]["dns2"].as()); } - json_obj_get_bool(&jctx, (char*)"accesspoint", &load_as_ap); - json_obj_get_string(&jctx, (char*)"ap_ssid", apName, sizeof(apName)); - json_obj_get_string(&jctx, (char*)"ap_pass", dbuf, sizeof(dbuf)); - urlDecode(apPass, dbuf, sizeof(dbuf)); - if(json_obj_get_int(&jctx, (char*)"ap_channel", &ap_channel) != OS_SUCCESS) - ap_channel = 1; - if(json_obj_get_bool(&jctx, (char*)"ap_dhcp", &ap_dhcp) != OS_SUCCESS) - ap_dhcp = true; + load_as_ap = json["accesspoint"].as(); + this->apName = json["ap_ssid"].as().c_str(); + + String apPassStr = json["ap_pass"].as(); + this->urlDecode(this->apPass, json["ap_pass"].as().c_str()); + + if(json["ap_channel"]) { this->ap_channel = json["ap_channel"].as(); } else { ap_channel = 1; } + if(json["ap_dhcp"]) { this->ap_dhcp = json["ap_dhcp"].as(); } else { ap_dhcp = true; } // read AP IP - if(ret == OS_SUCCESS && json_obj_get_object(&jctx, (char*)"ap_ip") == OS_SUCCESS) { - readIPFromJSON(&jctx, &apIP.ip, (char*)"ip"); - readIPFromJSON(&jctx, &apIP.netmask, (char*)"netmask"); - json_obj_leave_object(&jctx); + if(this->mdnsName && json["ap_ip"]) { + + IPAddress ip; + ip.fromString(json["ap_ip"]["ip"].as()); + apIP.ip = &ip; + + IPAddress nm; + nm.fromString(json["ap_ip"]["netmask"].as()); + apIP.netmask = &nm; } // User name and password - json_obj_get_string(&jctx, (char*)"user", user, sizeof(user)); - json_obj_get_string(&jctx, (char*)"pwd", pwd, sizeof(pwd)); + this->user = json["user"].as(); + this->pwd = json["pwd"].as(); // OTA - json_obj_get_bool(&jctx, (char*)"ota_enabled", &otaEnabled); - json_obj_get_string(&jctx, (char*)"ota_password", dbuf, sizeof(dbuf)); - urlDecode(otaPassword, dbuf, sizeof(dbuf)); + this->otaEnabled = json["ota_enabled"].as(); + this->urlDecode(this->otaPassword, json["ota_password"].as().c_str()); // NTP - json_obj_get_string(&jctx, (char*)"ntp_server", ntpServer, sizeof(ntpServer)); - int64_t gmtOffset; - if(json_obj_get_int64(&jctx, (char*)"gmt_offset", &gmtOffset) == OS_SUCCESS) { - gmtOffset_sec = (long) gmtOffset; - } - json_obj_get_int(&jctx, (char*)"dst_offset", &daylightOffset_sec); - - bool dbg; - if(json_obj_get_bool(&jctx, (char*)"debug_mode", &dbg) == OS_SUCCESS) - setDebugMode(dbg); - - // close the file - json_parse_end(&jctx); + this->ntpServer = json["ntp_server"].as(); + this->gmtOffset_sec = json["gmt_offset"].as(); + this->daylightOffset_sec = json["dst_offset"].as(); + + this->setDebugMode(json["debug_mode"].as()); + return ret; } @@ -306,102 +309,98 @@ void CLAppConn::readIPFromJSON (jparse_ctx_t * context, IPAddress ** ip_address, } int CLAppConn::savePrefs() { - - char * prefs_file = getPrefsFileName(true); - - if (Storage.exists(prefs_file)) { - Serial.printf("Updating %s\r\n", prefs_file); - } else { - Serial.printf("Creating %s\r\n", prefs_file); + JsonDocument json; + char ebuf[254]; + + char * prefs_file = getPrefsFileName(true); + + if (Storage.exists(prefs_file)) { + Serial.printf("Updating %s\r\n", prefs_file); + } else { + Serial.printf("Creating %s\r\n", prefs_file); + } + + json["mdns_name"] = this->mdnsName; + + int count = stationCount; + int index = getSSIDIndex(); + if(index < 0 && count == MAX_KNOWN_STATIONS) { + count--; + } + + if(index < 0 || count > 0) { + json["stations"].to(); + uint8_t i=0; + if(index < 0 && this->ssid != "") { + json["stations"][i]["ssid"] = this->ssid; + this->urlEncode(this->password, json["stations"][i]["pass"].as().c_str()); + i++; } - char buf[1024]; - json_gen_str_t jstr; - json_gen_str_start(&jstr, buf, sizeof(buf), NULL, NULL); - json_gen_start_object(&jstr); - json_gen_obj_set_string(&jstr, "mdns_name", mdnsName); - - int count = stationCount; - int index = getSSIDIndex(); - if(index < 0 && count == MAX_KNOWN_STATIONS) { - count--; - } - - char ebuf[254]; - - if(index < 0 || count > 0) { - json_gen_push_array(&jstr, "stations"); - if(index < 0 && strcmp(ssid, "") != 0) { - json_gen_start_object(&jstr); - json_gen_obj_set_string(&jstr, "ssid", ssid); - urlEncode(ebuf, password, sizeof(password)); - json_gen_obj_set_string(&jstr, "pass", ebuf); - json_gen_end_object(&jstr); - } - - for(int i=0; i < count && stationList[i]; i++) { - json_gen_start_object(&jstr); - json_gen_obj_set_string(&jstr, "ssid", stationList[i]->ssid); - if(index >= 0 && i == index) { - urlEncode(ebuf, password, sizeof(password)); - json_gen_obj_set_string(&jstr, "pass", ebuf); - } - else { - Serial.println(ebuf); - urlEncode(ebuf, stationList[i]->password, sizeof(stationList[i]->password)); - json_gen_obj_set_string(&jstr, "pass", ebuf); - } - json_gen_end_object(&jstr); - } - json_gen_pop_array(&jstr); + for(int i=0; i < count; i++) { + json["stations"][i]["ssid"] = stationList[i].ssid; + + if(index >= 0 && i == index) { + String encString(""); + this->urlEncode(encString, this->password.c_str()); + json["stations"][i]["pass"] = encString; + } + else { + String encString(""); + this->urlEncode(encString, stationList[i].password.c_str()); + json["stations"][i]["pass"] = encString; + } } - - json_gen_obj_set_bool(&jstr, "dhcp", dhcp); - json_gen_push_object(&jstr, "static_ip"); - if(staticIP.ip) json_gen_obj_set_string(&jstr, "ip", staticIP.ip->toString().c_str()); - if(staticIP.netmask) json_gen_obj_set_string(&jstr, "netmask", staticIP.netmask->toString().c_str()); - if(staticIP.gateway) json_gen_obj_set_string(&jstr, "gateway", staticIP.gateway->toString().c_str()); - if(staticIP.dns1) json_gen_obj_set_string(&jstr, "dns1", staticIP.dns1->toString().c_str()); - if(staticIP.dns2) json_gen_obj_set_string(&jstr, "dns2", staticIP.dns2->toString().c_str()); - json_gen_pop_object(&jstr); - json_gen_obj_set_int(&jstr, "http_port", httpPort); - json_gen_obj_set_string(&jstr, (char*)"user", user); - json_gen_obj_set_string(&jstr, (char*)"pwd", pwd); - json_gen_obj_set_bool(&jstr, "ota_enabled", otaEnabled); - urlEncode(ebuf, otaPassword, sizeof(otaPassword)); - json_gen_obj_set_string(&jstr, "ota_password", ebuf); + } + + json["dhcp"] = this->dhcp; + json["static_ip"].to(); + if(staticIP.ip) json["static_ip"]["ip"] = staticIP.ip->toString(); + if (staticIP.netmask) json["static_ip"]["netmask"] = staticIP.netmask->toString(); + if (staticIP.gateway) json["static_ip"]["gateway"] = staticIP.gateway->toString(); + if (staticIP.dns1) json["static_ip"]["dns1"] = staticIP.dns1->toString(); + if (staticIP.dns2) json["static_ip"]["dns2"] = staticIP.dns2->toString(); + + json["http_port"] = this->httpPort; + json["user"] = this->user; + json["pwd"] = this->pwd; + json["ota_enabled"] = this->otaEnabled; + + String t(""); + this->urlEncode(t, this->otaPassword.c_str()); + json["ota_password"] = t; - json_gen_obj_set_bool(&jstr, "accesspoint", load_as_ap); - json_gen_obj_set_string(&jstr, "ap_ssid", apName); - urlEncode(ebuf, apPass, sizeof(apPass)); - json_gen_obj_set_string(&jstr, "ap_pass", ebuf); - json_gen_obj_set_bool(&jstr, "ap_dhcp", ap_dhcp); - json_gen_push_object(&jstr, "ap_ip"); - if(apIP.ip) json_gen_obj_set_string(&jstr, "ip", apIP.ip->toString().c_str()); - if(apIP.netmask) json_gen_obj_set_string(&jstr, "netmask", apIP.netmask->toString().c_str()); - json_gen_pop_object(&jstr); - - json_gen_obj_set_string(&jstr, "ntp_server", ntpServer); - json_gen_obj_set_int(&jstr, "gmt_offset", gmtOffset_sec); - json_gen_obj_set_int(&jstr, "dst_offset", daylightOffset_sec); - - json_gen_obj_set_bool(&jstr, "debug_mode", isDebugMode()); - json_gen_end_object(&jstr); - json_gen_str_end(&jstr); - - File file = Storage.open(prefs_file, FILE_WRITE); - if(file) { - file.print(buf); - file.close(); - Serial.printf("File %s updated\r\n", prefs_file); - return OK; - } - else { - Serial.printf("Failed to save connection preferences to file %s\r\n", prefs_file); - return FAIL; - } - return OS_SUCCESS; -} + json["accesspoint"] = this->load_as_ap; + json["ap_ssid"] = this-> apName; + + t = ""; + this->urlEncode(t, this->otaPassword.c_str()); + json["ap_pass"] = t; + + json["ap_dhcp"] = this->ap_dhcp; + if(apIP.ip) json["ap_ip"]["ip"] = apIP.ip->toString(); + if(apIP.netmask) json["ap_ip"]["netmask"] = apIP.netmask->toString(); + + json["ntp_server"] = this->ntpServer; + json["gmt_offset"] = this->gmtOffset_sec; + json["dst_offset"] = this->daylightOffset_sec; + json["debug_mode"] = this->isDebugMode(); + + File file = Storage.open(prefs_file, FILE_WRITE); + if(file) { + serializeJson(json, file); + file.close(); + Serial.printf("File %s updated\r\n", prefs_file); + serializeJsonPretty(json, Serial); + Serial.println(); + return OK; + } + else { + Serial.printf("Failed to save connection preferences to file %s\r\n", prefs_file); + return FAIL; + } + return OS_SUCCESS; +} void CLAppConn::startOTA() { // Set up OTA @@ -411,11 +410,11 @@ void CLAppConn::startOTA() { // Port defaults to 3232 // ArduinoOTA.setPort(3232); // Hostname defaults to esp3232-[MAC] - ArduinoOTA.setHostname(mdnsName); + ArduinoOTA.setHostname(this->mdnsName.c_str()); - if (strlen(otaPassword) != 0) { - ArduinoOTA.setPassword(otaPassword); - Serial.printf("OTA Password: %s\n\r", otaPassword); + if (otaPassword.length() > 0) { + ArduinoOTA.setPassword(this->otaPassword.c_str()); + Serial.printf("OTA Password: %s\n\r", this->otaPassword.c_str()); } else { Serial.printf("\r\nNo OTA password has been set! (insecure)\r\n\r\n"); @@ -466,20 +465,20 @@ void CLAppConn::startOTA() { void CLAppConn::configMDNS() { // if(!otaEnabled) { - if (!MDNS.begin(mdnsName)) { + if (!MDNS.begin(this->mdnsName.c_str())) { Serial.println("Error setting up MDNS responder!"); } else Serial.println("mDNS responder started"); // } //MDNS Config -- note that if OTA is NOT enabled this needs prior steps! - MDNS.addService("http", "tcp", httpPort); + MDNS.addService("http", "tcp", this->httpPort); Serial.println("Added HTTP service to MDNS server"); } void CLAppConn::configNTP() { - configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer.c_str()); } void CLAppConn::printLocalTime(bool extraData) { @@ -496,28 +495,33 @@ void CLAppConn::printLocalTime(bool extraData) { void CLAppConn::updateTimeStr() { if(!accesspoint) { tm timeinfo; - if(getLocalTime(&timeinfo)) - strftime(localTimeString, sizeof(localTimeString), "%H:%M:%S, %A, %B %d %Y", &timeinfo); + if(getLocalTime(&timeinfo)) { + char buffer[80]; + strftime(buffer, sizeof(buffer), "%H:%M:%S, %A, %B %d %Y", &timeinfo); + this->localTimeString = (String)buffer; + } } else { - snprintf(localTimeString, sizeof(localTimeString), "N/A"); + this->localTimeString = "N/A"; } int64_t sec = esp_timer_get_time() / 1000000; int64_t upDays = int64_t(floor(sec/86400)); int upHours = int64_t(floor(sec/3600)) % 24; int upMin = int64_t(floor(sec/60)) % 60; int upSec = sec % 60; - snprintf(upTimeString, sizeof(upTimeString), "%" PRId64 ":%02i:%02i:%02i (d:h:m:s)", upDays, upHours, upMin, upSec); + + char buffer[80]; + snprintf(buffer, sizeof(buffer), "%" PRId64 ":%02i:%02i:%02i (d:h:m:s)", upDays, upHours, upMin, upSec); + this->upTimeString = (String)buffer; } int CLAppConn::getSSIDIndex() { - for(int i=0; i < stationCount; i++) { - if(!stationList[i]) break; - if(strcmp(ssid, stationList[i]->ssid) == 0) { - return i; - } + for(int i=0; i < stationCount; i++) { + if(ssid == stationList[i].ssid) { + return i; } - return -1; + } + return -1; } CLAppConn AppConn; \ No newline at end of file diff --git a/src/app_conn.h b/src/app_conn.h index 06f44a8d..3bd28f00 100644 --- a/src/app_conn.h +++ b/src/app_conn.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "parsebytes.h" #include "app_component.h" @@ -20,7 +21,7 @@ * @brief WiFi connectivity details (SSID/password). * */ -struct Station { char ssid[64]; char password[64]; }; +struct Station { String ssid; String password; }; /** * @brief Static IP structure for configuring AP and WiFi parameters @@ -48,62 +49,62 @@ class CLAppConn : public CLAppComponent { void handleOTA() {if(otaEnabled) ArduinoOTA.handle();}; bool isOTAEnabled() {return otaEnabled;}; void setOTAEnabled(bool val) {otaEnabled = val;}; - void setOTAPassword(const char * str) {snprintf(otaPassword, sizeof(otaPassword), str);}; + void setOTAPassword(const String val) {otaPassword = val;}; void configMDNS(); void handleDNSRequest(){if (captivePortal) dnsServer.processNextRequest();}; - char * getMDNSname() {return mdnsName;}; - void setMDNSName(const char * str) {snprintf(mdnsName, sizeof(mdnsName), str);}; + const String& getMDNSname() {return mdnsName;}; + void setMDNSName(String val) {mdnsName = val;}; void configNTP(); - char * getNTPServer() { return ntpServer;}; - void setNTPServer(const char * str) {snprintf(ntpServer, sizeof(ntpServer), str);}; + const String& getNTPServer() { return ntpServer;}; + void setNTPServer(const String val) {ntpServer = val;}; long getGmtOffset_sec() {return gmtOffset_sec;}; void setGmtOffset_sec(long sec) {gmtOffset_sec = sec;}; int getDaylightOffset_sec() {return daylightOffset_sec;}; void setDaylightOffset_sec(int sec) {daylightOffset_sec = sec;}; - char * getSSID() {return ssid;}; - void setSSID(const char * str) {snprintf(ssid, sizeof(ssid), str);}; - void setPassword(const char * str) {snprintf(password, sizeof(password), str);};; + const String& getSSID() {return ssid;}; + void setSSID(const String val) {ssid = val;}; + void setPassword(const String val) {password = val;};; bool isDHCPEnabled() {return dhcp;}; void setDHCPEnabled(bool val) {dhcp = val;}; - StaticIP * getStaticIP() {return &staticIP;}; + StaticIP* getStaticIP() {return &staticIP;}; void setStaticIP(IPAddress ** address, const char * strval); wl_status_t wifiStatus() {return (accesspoint?ap_status:WiFi.status());}; - char * getHTTPUrl(){ return httpURL;}; + const String& getHTTPUrl(){ return httpURL;}; // char * getStreamUrl(){ return streamURL;}; int getPort() {return httpPort;}; void setPort(int port) {httpPort = port;}; - char * getApName() {return apName;}; - void setApName(const char * str) {snprintf(apName, sizeof(apName), str);}; - void setApPass(const char * str) {snprintf(apPass, sizeof(apPass), str);}; + const String& getApName() {return apName;}; + void setApName(const String val) {apName = val;}; + void setApPass(const String val) {apPass = val;}; bool isAccessPoint() {return accesspoint;}; void setAccessPoint(bool val) {accesspoint = val;}; void setLoadAsAP(bool val) {load_as_ap = val;} bool getAPDHCP() {return ap_dhcp;}; void setAPDHCP(bool val) {ap_dhcp = val;}; - StaticIP * getAPIP() {return &apIP;}; + StaticIP* getAPIP() {return &apIP;}; int getAPChannel() {return ap_channel;}; void setAPChannel(int channel) {ap_channel = channel;}; bool isCaptivePortal() {return captivePortal;}; - char * getLocalTimeStr() {return localTimeString;}; - char * getUpTimeStr() {return upTimeString;}; + const String& getLocalTimeStr() {return localTimeString;}; + const String& getUpTimeStr() {return upTimeString;}; void updateTimeStr(); void printLocalTime(bool extraData=false); - char * getUser() {return user;}; - char * getPwd() {return pwd;}; - void setUser(const char * val) {snprintf(user, sizeof(user), val);}; - void setPwd(const char * val) {snprintf(pwd, sizeof(pwd), val);} + const String& getUser() {return user;}; + const String& getPwd() {return pwd;}; + void setUser(const String val) {user = val;}; + void setPwd(const String val) {pwd = val;} private: int getSSIDIndex(); @@ -111,7 +112,7 @@ class CLAppConn : public CLAppComponent { void readIPFromJSON(jparse_ctx_t * context, IPAddress ** ip_address, char * token); // Known networks structure. Max number of known stations limited for memory considerations - Station *stationList[MAX_KNOWN_STATIONS]; + Station* stationList; // number of known stations int stationCount = 0; @@ -120,16 +121,16 @@ class CLAppConn : public CLAppComponent { bool dhcp=false; - char ssid[64]; - char password[64]; + String ssid; + String password; - char mdnsName[32]; + String mdnsName; bool accesspoint = false; bool load_as_ap = false; - char apName[20]; - char apPass[20]; + String apName; + String apPass; int ap_channel=1; StaticIP apIP; bool ap_dhcp=true; @@ -141,30 +142,30 @@ class CLAppConn : public CLAppComponent { bool captivePortal = false; // HOST_NAME - char hostName[64]; + String hostName; // The app and stream URLs (initialized during WiFi setup) - char httpURL[64]; - char streamURL[64]; + String httpURL; + String streamURL; // HTTP Port. Can be overriden during IP setup - int httpPort = 80; + uint16_t httpPort = 80; // user name and password - char user[CREDENTIALS_SIZE] = "admin"; - char pwd[CREDENTIALS_SIZE] = "admin"; + String user = "admin"; + String pwd = "admin"; // OTA parameters bool otaEnabled = false; - char otaPassword[20] = ""; + String otaPassword = ""; // NTP parameters - char ntpServer[20] = ""; + String ntpServer = ""; long gmtOffset_sec; int daylightOffset_sec; - char localTimeString[50]; - char upTimeString[50]; + String localTimeString; + String upTimeString; }; diff --git a/src/app_httpd.cpp b/src/app_httpd.cpp index 71f19e77..019007cd 100644 --- a/src/app_httpd.cpp +++ b/src/app_httpd.cpp @@ -7,7 +7,7 @@ CLAppHttpd::CLAppHttpd() { sketchMD5 = ESP.getSketchMD5(); setTag("httpd"); #ifdef CAMERA_MODEL_AI_THINKER - setPrefix("aithinker"); + setPrefix("ai_thinker"); #endif } @@ -23,7 +23,7 @@ int CLAppHttpd::start() { ws = new AsyncWebSocket("/ws"); server->on("/", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(AppConn.getUser(), AppConn.getPwd())) + if(!request->authenticate(AppConn.getUser().c_str(), AppConn.getPwd().c_str())) return request->requestAuthentication(); if(AppConn.isConfigured()) request->send(Storage.getFS(), "/www/camera.html", "", false, processor); @@ -32,25 +32,25 @@ int CLAppHttpd::start() { }); server->on("/camera", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(AppConn.getUser(), AppConn.getPwd())) + if(!request->authenticate(AppConn.getUser().c_str(), AppConn.getPwd().c_str())) return request->requestAuthentication(); request->send(Storage.getFS(), "/www/camera.html", "", false, processor); }); server->on("/setup", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(AppConn.getUser(), AppConn.getPwd())) + if(!request->authenticate(AppConn.getUser().c_str(), AppConn.getPwd().c_str())) return request->requestAuthentication(); request->send(Storage.getFS(), "/www/setup.html", "", false, processor); }); server->on("/dump", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(AppConn.getUser(), AppConn.getPwd())) + if(!request->authenticate(AppConn.getUser().c_str(), AppConn.getPwd().c_str())) return request->requestAuthentication(); request->send(Storage.getFS(), "/www/dump.html", "", false, processor); }); server->on("/view", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(AppConn.getUser(), AppConn.getPwd())) + if(!request->authenticate(AppConn.getUser().c_str(), AppConn.getPwd().c_str())) return request->requestAuthentication(); if(request->arg("mode") == "stream" || request->arg("mode") == "still") { @@ -75,6 +75,21 @@ int CLAppHttpd::start() { server->on("/system", HTTP_GET, onSystemStatus).setAuthentication(AppConn.getUser(), AppConn.getPwd()); server->on("/info", HTTP_GET, onInfo).setAuthentication(AppConn.getUser(), AppConn.getPwd()); + // make a snapshot and send it to the client + server->on("/capture", HTTP_GET, [](AsyncWebServerRequest *request){ + if(AppCam.isConfigured()) { + if(AppCam.snapToBuffer() == ESP_OK) { + request->send(200, "image/jpeg", AppCam.getBuffer(), AppCam.getBufferSize()); + AppCam.releaseBuffer(); + } + else { + request->send(500, "text/plain", "Camera not ready"); + } + } + else { + request->send(500, "text/plain", "Camera not configured"); + } + }).setAuthentication(AppConn.getUser(), AppConn.getPwd()); // adding WebSocket handler ws->onEvent(onWsEvent); @@ -83,6 +98,7 @@ int CLAppHttpd::start() { snap_timer = xTimerCreate("SnapTimer", 1000/AppCam.getFrameRate()/portTICK_PERIOD_MS, pdTRUE, 0, onSnapTimer); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); + // TODO: if WiFi is not up, server->begin() produces a crash server->begin(); if(isDebugMode()) { @@ -470,159 +486,151 @@ void CLAppHttpd::updateSnapTimer(int tps) { void onInfo(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - char buf[CAM_DUMP_BUFFER_SIZE]; + JsonDocument json; + String jsonStr; - AppHttpd.dumpCameraStatusToJson(buf, sizeof(buf), false); - - response->print(buf); - request->send(response); + AppHttpd.dumpCameraStatusToJson(json); + serializeJson(json, jsonStr); + response->print(jsonStr); + request->send(response); } void onStatus(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); // Do not get attempt to get sensor when in error; causes a panic.. + + JsonDocument json; + String jsonStr; - char buf[CAM_DUMP_BUFFER_SIZE]; - - AppHttpd.dumpCameraStatusToJson(buf, sizeof(buf)); - - response->print(buf); + AppHttpd.dumpCameraStatusToJson(json); + serializeJson(json, jsonStr); + response->print(jsonStr); request->send(response); } void onSystemStatus(AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - char buf[1280]; - AppHttpd.dumpSystemStatusToJson(buf, sizeof(buf)); - - response->print(buf); + JsonDocument json; + String jsonStr; + + AppHttpd.dumpSystemStatusToJson(json); + serializeJson(json, jsonStr); + response->print(jsonStr); if(AppHttpd.isDebugMode()) { Serial.println(); Serial.println("Dump requested through web"); - Serial.println(buf); + serializeJsonPretty(json, Serial); } request->send(response); } -void CLAppHttpd::dumpCameraStatusToJson(char * buf, size_t size, bool full_status) { +void CLAppHttpd::dumpCameraStatusToJson(JsonDocument& json, bool full_status) { - json_gen_str_t jstr; - json_gen_str_start(&jstr, buf, size, NULL, NULL); - json_gen_start_object(&jstr); - - json_gen_obj_set_string(&jstr, (char*)"cam_name", getName()); - // json_gen_obj_set_string(&jstr, (char*)"stream_url", AppConn.getStreamUrl()); + json["cam_name"] = this->getName(); + // json["stream_url"] = this->AppConn.getStreamUrl(); AppConn.updateTimeStr(); - json_gen_obj_set_string(&jstr, (char*)"local_time", AppConn.getLocalTimeStr()); - json_gen_obj_set_string(&jstr, (char*)"up_time", AppConn.getUpTimeStr()); - json_gen_obj_set_int(&jstr, (char*)"rssi", (!AppConn.isAccessPoint()?WiFi.RSSI():(uint8_t)0)); - json_gen_obj_set_int(&jstr, (char*)"esp_temp", getTemp()); - json_gen_obj_set_string(&jstr, (char*)"serial_buf", getSerialBuffer()); + json["local_time"] = AppConn.getLocalTimeStr(); + json["up_time"] = AppConn.getUpTimeStr(); + json["rssi"] = (!AppConn.isAccessPoint()?WiFi.RSSI():(uint8_t)0); + json["esp_temp"] = this->getTemp(); + json["serial_buf"] = this->getSerialBuffer(); - AppCam.dumpStatusToJson(&jstr, full_status); + AppCam.dumpStatusToJson(json, full_status); if(full_status) { - json_gen_obj_set_int(&jstr, (char*)"lamp", getLamp()); - json_gen_obj_set_bool(&jstr, (char*)"autolamp", isAutoLamp()); - json_gen_obj_set_int(&jstr, (char*)"lamp", getLamp()); - json_gen_obj_set_int(&jstr, (char*)"flashlamp", getFlashLamp()); + json["lamp"] = this->getLamp(); + json["autolamp"] = this->isAutoLamp(); + json["lamp"] = this->getLamp(); + json["flashlamp"] = this->getFlashLamp(); - json_gen_obj_set_string(&jstr, (char*)"code_ver", getVersion()); + json["code_ver"] = this->getVersion(); } - json_gen_end_object(&jstr); - json_gen_str_end(&jstr); } -void CLAppHttpd::dumpSystemStatusToJson(char * buf, size_t size) { - - json_gen_str_t jstr; - json_gen_str_start(&jstr, buf, size, NULL, NULL); - json_gen_start_object(&jstr); - - json_gen_obj_set_string(&jstr, (char*)"cam_name", getName()); - json_gen_obj_set_string(&jstr, (char*)"code_ver", getVersion()); - json_gen_obj_set_string(&jstr, (char*)"base_version", BASE_VERSION); - json_gen_obj_set_int(&jstr, (char*)"sketch_size", getSketchSize()); - json_gen_obj_set_int(&jstr, (char*)"sketch_space", getSketchSpace()); - json_gen_obj_set_string(&jstr, (char*)"sketch_md5", getSketchMD5()); - json_gen_obj_set_string(&jstr, (char*)"esp_sdk", ESP.getSdkVersion()); +void CLAppHttpd::dumpSystemStatusToJson(JsonDocument& json) { + + json["cam_name"] = this->getName(); + json["code_ver"] = this->getVersion(); + json["base_version"] = BASE_VERSION; + json["sketch_size"] = this->getSketchSize(); + json["sketch_space"] = this->getSketchSpace(); + json["sketch_md5"] = this->getSketchMD5(); + json["esp_sdk"] = ESP.getSdkVersion(); - json_gen_obj_set_bool(&jstr,(char*)"accesspoint", AppConn.isAccessPoint()); - json_gen_obj_set_bool(&jstr,(char*)"captiveportal", AppConn.isCaptivePortal()); - json_gen_obj_set_string(&jstr, (char*)"ap_name", AppConn.getApName()); - json_gen_obj_set_string(&jstr, (char*)"ssid", AppConn.getSSID()); - - json_gen_obj_set_int(&jstr, (char*)"rssi", (!AppConn.isAccessPoint()?WiFi.RSSI():(uint8_t)0)); - json_gen_obj_set_string(&jstr, (char*)"bssid", (!AppConn.isAccessPoint()?WiFi.BSSIDstr().c_str():(char*)"")); - json_gen_obj_set_int(&jstr, (char*)"dhcp", AppConn.isDHCPEnabled()); + json["accesspoint"] = AppConn.isAccessPoint(); + json["captiveportal"] = AppConn.isCaptivePortal(); + json["ap_name"] = AppConn.getApName(); + json["ssid"] = AppConn.getSSID(); + + json["rssi"] = (!AppConn.isAccessPoint()?WiFi.RSSI():(uint8_t)0); + json["bssid"] = (!AppConn.isAccessPoint()?WiFi.BSSIDstr():""); + json["dhcp"] = AppConn.isDHCPEnabled(); - json_gen_obj_set_string(&jstr, (char*)"ip_address", (AppConn.isAccessPoint()?WiFi.softAPIP().toString().c_str():WiFi.localIP().toString().c_str())); - json_gen_obj_set_string(&jstr, (char*)"subnet", (!AppConn.isAccessPoint()?WiFi.subnetMask().toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"gateway", (!AppConn.isAccessPoint()?WiFi.gatewayIP().toString().c_str():(char*)"")); + json["ip_address"] = (AppConn.isAccessPoint()?WiFi.softAPIP().toString():WiFi.localIP().toString()); + json["subnet"] = (!AppConn.isAccessPoint()?WiFi.subnetMask().toString():""); + json["gateway"] = (!AppConn.isAccessPoint()?WiFi.gatewayIP().toString():""); - json_gen_obj_set_string(&jstr, (char*)"st_ip", (AppConn.getStaticIP()->ip?AppConn.getStaticIP()->ip->toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"st_subnet", (AppConn.getStaticIP()->netmask?AppConn.getStaticIP()->netmask->toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"st_gateway", (AppConn.getStaticIP()->gateway?AppConn.getStaticIP()->gateway->toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"dns1", (AppConn.getStaticIP()->dns1?AppConn.getStaticIP()->dns1->toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"dns2", (AppConn.getStaticIP()->dns2?AppConn.getStaticIP()->dns2->toString().c_str():(char*)"")); + json["st_ip"] = (AppConn.getStaticIP()->ip?AppConn.getStaticIP()->ip->toString():""); + json["st_subnet"] = (AppConn.getStaticIP()->netmask?AppConn.getStaticIP()->netmask->toString():""); + json["st_gateway"] = (AppConn.getStaticIP()->gateway?AppConn.getStaticIP()->gateway->toString():""); + json["dns1"] = (AppConn.getStaticIP()->dns1?AppConn.getStaticIP()->dns1->toString():""); + json["dns2"] = (AppConn.getStaticIP()->dns2?AppConn.getStaticIP()->dns2->toString():""); - json_gen_obj_set_string(&jstr, (char*)"ap_ip", (AppConn.getAPIP()->ip?AppConn.getAPIP()->ip->toString().c_str():(char*)"")); - json_gen_obj_set_string(&jstr, (char*)"ap_subnet", (AppConn.getAPIP()->netmask?AppConn.getAPIP()->netmask->toString().c_str():(char*)"")); + json["ap_ip"] = (AppConn.getAPIP()->ip?AppConn.getAPIP()->ip->toString():""); + json["ap_subnet"] = (AppConn.getAPIP()->netmask?AppConn.getAPIP()->netmask->toString():""); - json_gen_obj_set_int(&jstr, (char*)"ap_channel", AppConn.getAPChannel()); - json_gen_obj_set_int(&jstr, (char*)"ap_dhcp", AppConn.getAPDHCP()); + json["ap_channel"] = AppConn.getAPChannel(); + json["ap_dhcp"] = AppConn.getAPDHCP(); - json_gen_obj_set_string(&jstr, (char*)"mdns_name", AppConn.getMDNSname()); - json_gen_obj_set_int(&jstr, (char*)"port", AppConn.getPort()); + json["mdns_name"] = AppConn.getMDNSname(); + json["port"] = AppConn.getPort(); - json_gen_obj_set_string(&jstr, (char*)"user", AppConn.getUser()); + json["user"] = AppConn.getUser(); byte mac[6]; WiFi.macAddress(mac); char mac_buf[18]; snprintf(mac_buf, sizeof(mac_buf), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - json_gen_obj_set_string(&jstr, (char*)"mac_address", mac_buf); + json["mac_address"] = mac_buf; AppConn.updateTimeStr(); - json_gen_obj_set_string(&jstr, (char*)"local_time", AppConn.getLocalTimeStr()); - json_gen_obj_set_string(&jstr, (char*)"up_time", AppConn.getUpTimeStr()); - json_gen_obj_set_string(&jstr, (char*)"ntp_server", AppConn.getNTPServer()); - json_gen_obj_set_int(&jstr, (char*)"gmt_offset", AppConn.getGmtOffset_sec()); - json_gen_obj_set_int(&jstr, (char*)"dst_offset", AppConn.getDaylightOffset_sec()); + json["local_time"] = AppConn.getLocalTimeStr(); + json["up_time"] = AppConn.getUpTimeStr(); + json["ntp_server"] = AppConn.getNTPServer(); + json["gmt_offset"] = AppConn.getGmtOffset_sec(); + json["dst_offset"] = AppConn.getDaylightOffset_sec(); - json_gen_obj_set_int(&jstr, (char*)"active_streams", AppHttpd.getStreamCount()); - json_gen_obj_set_int(&jstr, (char*)"prev_streams", AppHttpd.getStreamsServed()); - json_gen_obj_set_int(&jstr, (char*)"img_captured", AppHttpd.getImagesServed()); + json["active_streams"] = AppHttpd.getStreamCount(); + json["prev_streams"] = AppHttpd.getStreamsServed(); + json["img_captured"] = AppHttpd.getImagesServed(); - json_gen_obj_set_int(&jstr, (char*)"ota_enabled", AppConn.isOTAEnabled()); + json["ota_enabled"] = AppConn.isOTAEnabled(); - json_gen_obj_set_int(&jstr, (char*)"cpu_freq", ESP.getCpuFreqMHz()); - json_gen_obj_set_int(&jstr, (char*)"num_cores", ESP.getChipCores()); - json_gen_obj_set_int(&jstr, (char*)"esp_temp", getTemp()); // Celsius - json_gen_obj_set_int(&jstr, (char*)"heap_avail", ESP.getHeapSize()); - json_gen_obj_set_int(&jstr, (char*)"heap_free", ESP.getFreeHeap()); - json_gen_obj_set_int(&jstr, (char*)"heap_min_free", ESP.getMinFreeHeap()); - json_gen_obj_set_int(&jstr, (char*)"heap_max_bloc", ESP.getMaxAllocHeap()); + json["cpu_freq"] = ESP.getCpuFreqMHz(); + json["num_cores"] = ESP.getChipCores(); + json["esp_temp"] = this->getTemp(); // Celsius + json["heap_avail"] = ESP.getHeapSize(); + json["heap_free"] = ESP.getFreeHeap(); + json["heap_min_free"] = ESP.getMinFreeHeap(); + json["heap_max_bloc"] = ESP.getMaxAllocHeap(); - json_gen_obj_set_bool(&jstr, (char*)"psram_found", psramFound()); - json_gen_obj_set_int(&jstr, (char*)"psram_size", (psramFound()?ESP.getPsramSize():0)); - json_gen_obj_set_int(&jstr, (char*)"psram_free", (psramFound()?ESP.getFreePsram():0)); - json_gen_obj_set_int(&jstr, (char*)"psram_min_free", (psramFound()?ESP.getMinFreePsram():0)); - json_gen_obj_set_int(&jstr, (char*)"psram_max_bloc", (psramFound()?ESP.getMaxAllocPsram():0)); + json["psram_found"] = psramFound(); + json["psram_size"] = (psramFound()?ESP.getPsramSize():0); + json["psram_free"] = (psramFound()?ESP.getFreePsram():0); + json["psram_min_free"] = (psramFound()?ESP.getMinFreePsram():0); + json["psram_max_bloc"] = (psramFound()?ESP.getMaxAllocPsram():0); - json_gen_obj_set_int(&jstr, (char*)"xclk", AppCam.getXclk()); + json["xclk"] = AppCam.getXclk(); - json_gen_obj_set_int(&jstr, (char*)"storage_size", Storage.getSize()); - json_gen_obj_set_int(&jstr, (char*)"storage_used", Storage.getUsed()); - json_gen_obj_set_string(&jstr, (char*)"storage_units", (Storage.capacityUnits()==STORAGE_UNITS_MB?(char*)"MB":(char*)"")); + json["storage_size"] = Storage.getSize(); + json["storage_used"] = Storage.getUsed(); + json["storage_units"] = (Storage.capacityUnits()==STORAGE_UNITS_MB?(char*)"MB":(char*)""); - json_gen_obj_set_string(&jstr, (char*)"serial_buf", getSerialBuffer()); + json["serial_buf"] = getSerialBuffer(); - json_gen_end_object(&jstr); - json_gen_str_end(&jstr); } void CLAppHttpd::serialSendCommand(const char *cmd) { @@ -703,54 +711,52 @@ int CLAppHttpd::loadPrefs() { } int CLAppHttpd::savePrefs() { - char * prefs_file = getPrefsFileName(true); - char buf[1024]; - json_gen_str_t jstr; - json_gen_str_start(&jstr, buf, sizeof(buf), NULL, NULL); - json_gen_start_object(&jstr); + JsonDocument json; + char* prefs_file = getPrefsFileName(true); - json_gen_obj_set_string(&jstr, (char*)"my_name", myName); + if (Storage.exists(prefs_file)) { + Serial.printf("Updating %s\r\n", prefs_file); + } else { + Serial.printf("Creating %s\r\n", prefs_file); + } - json_gen_obj_set_int(&jstr, (char*)"lamp", lampVal); - json_gen_obj_set_bool(&jstr, (char*)"autolamp", autoLamp); - json_gen_obj_set_int(&jstr, (char*)"flashlamp", flashLamp); - json_gen_obj_set_int(&jstr, (char*)"max_streams", max_streams); + json["my_name"] = myName; + + json["lamp"] = lampVal; + json["autolamp"] = autoLamp; + json["flashlamp"] = flashLamp; + json["max_streams"] = max_streams; if(pwmCount > 0) { - json_gen_push_array(&jstr, (char*)"pwm"); + json["pwm"].as(); for(int i=0; i < pwmCount; i++) if(pwm[i]) { - json_gen_start_object(&jstr); - json_gen_obj_set_int(&jstr, (char*)"pin", pwm[i]->getPin()); - json_gen_obj_set_int(&jstr, (char*)"frequency", pwm[i]->getFreq()); - json_gen_obj_set_int(&jstr, (char*)"resolution", pwm[i]->getResolutionBits()); + json["pwm"][i]["pin"] = pwm[i]->getPin(); + json["pwm"][i]["frequency"] = pwm[i]->getFreq(); + json["pwm"][i]["resolution"] = pwm[i]->getResolutionBits(); if(pwm[i]->getDefaultDuty()) - json_gen_obj_set_int(&jstr, (char*)"default", pwm[i]->getDefaultDuty()); - json_gen_end_object(&jstr); + json["pwm"][i]["default"] = pwm[i]->getDefaultDuty(); } - - json_gen_pop_array(&jstr); } if(mappingCount > 0) { - json_gen_push_array(&jstr, (char*)"mapping"); + json["mapping"].as(); for(int i=0; i < mappingCount; i++) { - json_gen_start_object(&jstr); - json_gen_obj_set_string(&jstr, (char*)"uri", mappingList[i]->uri); - json_gen_obj_set_string(&jstr, (char*)"path", mappingList[i]->path); - json_gen_end_object(&jstr); + json["mapping"][i]["uri"] = mappingList[i]->uri; + json["mapping"][i]["path"] = mappingList[i]->path; } - json_gen_pop_array(&jstr); } - json_gen_obj_set_bool(&jstr, (char*)"debug_mode", isDebugMode()); - - json_gen_end_object(&jstr); - json_gen_str_end(&jstr); + json["debug_mode"] = isDebugMode(); File file = Storage.open(prefs_file, FILE_WRITE); if(file) { - file.print(buf); + serializeJsonPretty(json, Serial); + Serial.println(); + + serializeJson(json, file); file.close(); + + Serial.printf("File %s updated\r\n", prefs_file); return OK; } else { diff --git a/src/app_httpd.h b/src/app_httpd.h index 8dd9f6f9..621f07e5 100644 --- a/src/app_httpd.h +++ b/src/app_httpd.h @@ -5,11 +5,12 @@ #include #include -#include "esp32pwm.h" -#include "ESPAsyncWebServer.h" -#include "storage.h" -#include "app_conn.h" -#include "app_cam.h" +#include +#include +#include +#include +#include +#include #define MAX_URI_MAPPINGS 32 @@ -112,8 +113,8 @@ class CLAppHttpd : public CLAppComponent { void setLamp(int newVal = DEFAULT_FLASH); int getLamp() {return lampVal;}; - void dumpSystemStatusToJson(char * buf, size_t size); - void dumpCameraStatusToJson(char * buf, size_t size, bool full = true); + void dumpSystemStatusToJson(JsonDocument& json); + void dumpCameraStatusToJson(JsonDocument& json, bool full = true); /** * @brief attaches a new PWM/servo and returns its ID in case of success, or OS_FAIL otherwise diff --git a/esp32-cam-webserver.ino b/src/main.cpp similarity index 92% rename from esp32-cam-webserver.ino rename to src/main.cpp index 972c535c..5238072b 100644 --- a/esp32-cam-webserver.ino +++ b/src/main.cpp @@ -1,21 +1,88 @@ -#include "src/app_config.h" // global definitions -#include "src/storage.h" // Filesystem -#include "src/app_conn.h" // Conectivity -#include "src/app_cam.h" // Camera -#include "src/app_httpd.h" // Web server -#include "src/camera_pins.h" // Pin Mappings +#include // global definitions +#include // Filesystem +#include // Conectivity +#include // Camera +#include // Web server +#include // Pin Mappings /* * This sketch is a extension/expansion/rework of the ESP32 Camera webserer example. * */ +// Flash LED if LED pin defined +void flashLED(int flashtime) { +#ifdef LED_PIN + digitalWrite(LED_PIN, LED_ON); + delay(flashtime); + digitalWrite(LED_PIN, LED_OFF); +#endif +} + +// @brief tries to initialize the filesystem until success, otherwise loops indefinitely +void filesystemStart() { + Serial.println("Starting filesystem"); + while ( !Storage.init() ) { + // if we sit in this loop something is wrong; + Serial.println("Filesystem mount failed"); + for (int i=0; i<10; i++) { + flashLED(100); // Show filesystem failure + delay(100); + } + delay(1000); + Serial.println("Retrying.."); + } + + // Storage.listDir("/", 0); +} + +// Serial input +void handleSerial() { + if(Serial.available()) { + char cmd = Serial.read(); + + // Rceiving commands and data from serial. Any input, which doesnt start from '#' is ignored. + if (cmd == '#' ) { + String rsp = Serial.readStringUntil('\n'); + rsp.trim(); + snprintf(AppHttpd.getSerialBuffer(), SERIAL_BUFFER_SIZE, rsp.c_str()); + } + } +} + +void notifyConnect() { + for (int i = 0; i < 5; i++) { + flashLED(150); + delay(50); + } + AppHttpd.serialSendCommand("Connected"); +} + +void notifyDisconnect() { + AppHttpd.serialSendCommand("Disconnected"); +} + +void scheduleReboot(int delay) { + esp_task_wdt_init(delay,true); + esp_task_wdt_add(NULL); +} + +// Reset the I2C bus.. may help when rebooting. +void resetI2CBus() { + periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly in case that is the problem + periph_module_disable(PERIPH_I2C1_MODULE); + periph_module_reset(PERIPH_I2C0_MODULE); + periph_module_reset(PERIPH_I2C1_MODULE); +} void setup() { Serial.begin(115200); Serial.setDebugOutput(true); + Serial.println("Start ESP32 Cam Webserver"); + Serial.println("Initialize...."); + // Warn if no PSRAM is detected (typically user error with board selection in the IDE) if(!psramFound()){ Serial.println("\r\nFatal Error; Halting"); @@ -123,69 +190,3 @@ void loop() { } } - -/// @brief tries to initialize the filesystem until success, otherwise loops indefinitely -void filesystemStart(){ - Serial.println("Starting filesystem"); - while ( !Storage.init() ) { - // if we sit in this loop something is wrong; - Serial.println("Filesystem mount failed"); - for (int i=0; i<10; i++) { - flashLED(100); // Show filesystem failure - delay(100); - } - delay(1000); - Serial.println("Retrying.."); - } - - // Storage.listDir("/", 0); -} - -// Serial input -void handleSerial() { - if(Serial.available()) { - char cmd = Serial.read(); - - // Rceiving commands and data from serial. Any input, which doesnt start from '#' is ignored. - if (cmd == '#' ) { - String rsp = Serial.readStringUntil('\n'); - rsp.trim(); - snprintf(AppHttpd.getSerialBuffer(), SERIAL_BUFFER_SIZE, rsp.c_str()); - } - } -} - -void notifyConnect() { - for (int i = 0; i < 5; i++) { - flashLED(150); - delay(50); - } - AppHttpd.serialSendCommand("Connected"); -} - -void notifyDisconnect() { - AppHttpd.serialSendCommand("Disconnected"); -} - -// Flash LED if LED pin defined -void flashLED(int flashtime) { -#ifdef LED_PIN - digitalWrite(LED_PIN, LED_ON); - delay(flashtime); - digitalWrite(LED_PIN, LED_OFF); -#endif -} - -void scheduleReboot(int delay) { - esp_task_wdt_init(delay,true); - esp_task_wdt_add(NULL); -} - -// Reset the I2C bus.. may help when rebooting. -void resetI2CBus() { - periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly in case that is the problem - periph_module_disable(PERIPH_I2C1_MODULE); - periph_module_reset(PERIPH_I2C0_MODULE); - periph_module_reset(PERIPH_I2C1_MODULE); -} - diff --git a/src/storage.h b/src/storage.h index 1738f30b..f23a7b88 100644 --- a/src/storage.h +++ b/src/storage.h @@ -13,16 +13,16 @@ #define STORAGE_UNITS_MB 2 #ifdef USE_LittleFS -#include -#define FORMAT_LITTLEFS_IF_FAILED true -#define STORAGE_UNITS STORAGE_UNITS_BT + #include + #define FORMAT_LITTLEFS_IF_FAILED true + #define STORAGE_UNITS STORAGE_UNITS_BT #elif defined(CAMERA_MODEL_LILYGO_T_SIMCAM) -#include "camera_pins.h" -#include "SD.h" -#define STORAGE_UNITS STORAGE_UNITS_MB + #include "camera_pins.h" + #include "SD.h" + #define STORAGE_UNITS STORAGE_UNITS_MB #else -#include "SD_MMC.h" -#define STORAGE_UNITS STORAGE_UNITS_MB + #include "SD_MMC.h" + #define STORAGE_UNITS STORAGE_UNITS_MB #endif /** @@ -54,7 +54,7 @@ class CLStorage { bool remove(const String &path) {return fsStorage->remove(path);}; #ifdef USE_LittleFS - fs::LITTLEFSFS & getFS() {return *fsStorage;}; + fs::LittleFSFS & getFS() {return *fsStorage;}; #elif defined(CAMERA_MODEL_LILYGO_T_SIMCAM) fs::SDFS & getFS() {return *fsStorage;}; #else @@ -63,7 +63,7 @@ class CLStorage { private: #ifdef USE_LittleFS - fs::LITTLEFSFS * const fsStorage = &LITTLEFS; + fs::LittleFSFS * const fsStorage = &LittleFS; #elif defined(CAMERA_MODEL_LILYGO_T_SIMCAM) fs::SDFS * const fsStorage = &SD; #else