From 041031709fd3f90c7ca301f1a6204638217794e0 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Thu, 3 Sep 2020 14:10:52 +0200 Subject: [PATCH] More configuration options - wifi provisioning - auto start/stop of sessions --- firmware/deploy.py | 22 ++- firmware/lib/esphttp/EspHttp.h | 2 +- firmware/lib/scale/Scale.h | 30 ++- firmware/lib/session/AutoStartStop.h | 79 ++++++++ firmware/lib/session/SessionManager.h | 119 ++++++++++-- firmware/lib/wifimanager/WifiManager.cpp | 105 ++++++++++ firmware/lib/wifimanager/WifiManager.h | 51 +++++ firmware/platformio.ini | 1 + firmware/src/SwimTrackerConfig.h | 28 +-- firmware/src/firmware_main.cpp | 236 +++++++++++++---------- hardware/OtherBoards.md | 2 - hardware/ResearchOwnDesign.md | 29 +++ 12 files changed, 547 insertions(+), 157 deletions(-) create mode 100644 firmware/lib/session/AutoStartStop.h create mode 100644 firmware/lib/wifimanager/WifiManager.cpp create mode 100644 firmware/lib/wifimanager/WifiManager.h delete mode 100644 hardware/OtherBoards.md create mode 100644 hardware/ResearchOwnDesign.md diff --git a/firmware/deploy.py b/firmware/deploy.py index 8643342..0662eac 100644 --- a/firmware/deploy.py +++ b/firmware/deploy.py @@ -4,8 +4,10 @@ from collections import namedtuple from datetime import datetime import subprocess from distutils.version import StrictVersion +import subprocess +import os -locale.setlocale(locale.LC_ALL, 'en_US') +#locale.setlocale(locale.LC_ALL, 'en_US') # sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) APP_DESC_OFFSET = 32 @@ -113,9 +115,17 @@ def add_info_to_firmware(firmware_file, version): if __name__ == "__main__": - fimware_file = ".pio/build/esp32/firmware.bin" - print(read_app_description(fimware_file)) + firmware_file = ".pio/build/esp32/firmware.bin" + version_file_name = "VERSION" - patch_app_description(fimware_file, version="myversion", - project_name="swimtracker.bauer.tech", time="22:16:12", date="Feb 20 2020") - print(read_app_description(fimware_file)) + version = version_number_from_git() + add_info_to_firmware(firmware_file, version) + print(read_app_description(firmware_file)) + with open(version_file_name, "w") as version_file: + print(version, file=version_file) + subprocess.run(["scp", firmware_file, "root@server:/volumes/swimtracker-update"]) + subprocess.run(["scp", version_file_name, "root@server:/volumes/swimtracker-update"]) + + os.unlink(firmware_file) + os.unlink(version_file_name) + \ No newline at end of file diff --git a/firmware/lib/esphttp/EspHttp.h b/firmware/lib/esphttp/EspHttp.h index d7ccf61..07e0c3e 100644 --- a/firmware/lib/esphttp/EspHttp.h +++ b/firmware/lib/esphttp/EspHttp.h @@ -4,7 +4,7 @@ #include #include "Dtypes.h" -constexpr int MAX_URI_HANDLERS = 20; +constexpr int MAX_URI_HANDLERS = 25; esp_err_t rawCallback(httpd_req_t *req); diff --git a/firmware/lib/scale/Scale.h b/firmware/lib/scale/Scale.h index 90ce534..2578ecf 100644 --- a/firmware/lib/scale/Scale.h +++ b/firmware/lib/scale/Scale.h @@ -2,32 +2,23 @@ #include "SwimTrackerConfig.h" #include -template class Scale { public: bool measure(uint16_t &measurementOut) { - //if (hx711_.is_ready()) - //{ - long value = hx711_.read_average(CONFIG_MEASUREMENT_AVG_COUNT) - offset_; - if (value < 0) - measurementOut = (int16_t)(-value / DIVIDER); - else - measurementOut = 0; - return true; - //} - //else { - // long value = hx711_.read_average(CONFIG_MEASUREMENT_AVG_COUNT) - offset_; -// - // Serial.printf("Measurement failed %ld\n", value); - // return false; - //} + long value = hx711_.read_average(CONFIG_MEASUREMENT_AVG_COUNT) - offset_; + if (value < 0) + measurementOut = (int16_t)(-(value >> valueRightShift_ )); + else + measurementOut = 0; + return true; } - void begin(uint32_t pinDOUT, uint32_t pinSCK) + void begin(uint32_t pinDOUT, uint32_t pinSCK, int valueRightShift) { hx711_.begin(pinDOUT, pinSCK); + valueRightShift_ = valueRightShift; }; void tare(uint32_t numMeasurementsToAverage = 50) @@ -36,10 +27,11 @@ public: offset_ = hx711_.read_average(numMeasurementsToAverage); Serial.printf("Tare offset %ld\n", offset_); } - - const long &offset() const { return offset_; } + const long &offset() const { return offset_; } + int valueRightShift() const { return valueRightShift_; } private: HX711 hx711_; long offset_ = 0; + int valueRightShift_; }; diff --git a/firmware/lib/session/AutoStartStop.h b/firmware/lib/session/AutoStartStop.h new file mode 100644 index 0000000..a5a696c --- /dev/null +++ b/firmware/lib/session/AutoStartStop.h @@ -0,0 +1,79 @@ +#pragma once + +template +class AutoStart +{ +public: + void begin(MeasurementT minThreshold, MeasurementT maxThreshold, uint32_t maxNumMeasurementsBetweenPeeks) + { + minThreshold_ = minThreshold; + maxThreshold_ = maxThreshold; + maxNumMeasurementsBetweenPeeks_ = maxNumMeasurementsBetweenPeeks; + measurementsSinceLastPeek_ = maxNumMeasurementsBetweenPeeks + 1; + up_ = false; + } + + bool autoStart(MeasurementT measurement) + { + measurementsSinceLastPeek_ += 1; + if (!up_ && measurement > maxThreshold_) + { + up_ = true; + bool doAutoStart = measurementsSinceLastPeek_ < maxNumMeasurementsBetweenPeeks_; + measurementsSinceLastPeek_ = 0; + return doAutoStart; + } + if (up_ && measurement < minThreshold_) + up_ = false; + + return false; + } + +private: + MeasurementT minThreshold_; + MeasurementT maxThreshold_; + uint32_t maxNumMeasurementsBetweenPeeks_; + + // time of last peak + uint32_t measurementsSinceLastPeek_; + // if measurements have been below min threshold (false) or above max threshold (true) + bool up_; +}; + +// ------------------------------------------------------------------------------------------ + +template +class AutoStop +{ +public: + void begin(MeasurementT threshold, uint32_t numMeasurementsBelowThreshold) + { + threshold_ = threshold; + numMeasurementsBelowThreshold_ = numMeasurementsBelowThreshold; + counter_ = 0; + } + + bool autoStop(MeasurementT measurement) + { + if (measurement > threshold_) + { + counter_ = 0; + return false; + } + else + { + ++counter_; + if (counter_ >= numMeasurementsBelowThreshold_) + { + counter_ = 0; + return true; + } + return false; + } + } + +private: + MeasurementT threshold_; + uint32_t numMeasurementsBelowThreshold_; + uint32_t counter_; +}; diff --git a/firmware/lib/session/SessionManager.h b/firmware/lib/session/SessionManager.h index 790658e..84498d3 100644 --- a/firmware/lib/session/SessionManager.h +++ b/firmware/lib/session/SessionManager.h @@ -4,18 +4,23 @@ #include #include +#include "AutoStartStop.h" + template class SessionManager { public: using MeasurementType = typename SessionT::MeasurementType; - - SessionManager(int scaleDoutPin, int scaleSckPin, uint8_t tareAvgCount); - void begin(); + SessionManager(); + + void begin(int scaleDoutPin, int scaleSckPin, uint8_t tareAvgCount, int valueRightShift, MeasurementType autoStartMinTh, MeasurementType autoStartMaxTh, uint32_t autoStartMeasuresBetweenPeaks, + MeasurementType autoStopTh, uint32_t autoStopNumMeasures); void tare(); long tareOffset() const { return scale_.offset(); }; + int valueRightShift() const { return scale_.valueRightShift(); } + void startMeasurements(); void stopMeasurements(); bool isMeasuring() const { return measuring_; } @@ -24,10 +29,12 @@ public: void iteration(); private: + void onMeasurementTaken(MeasurementType measurement); + WiFiUDP ntpUDP_; NTPClient timeClient_; - Scale scale_; + Scale scale_; //MockScale scale; SessionT session_; bool measuring_; @@ -36,45 +43,55 @@ private: int scaleDoutPin_; int scaleSckPin_; uint8_t tareAvgCount_; + int valueRightShift_; + + AutoStart autoStart_; + AutoStop autoStop_; }; // ------------------------------------------------------------------------------------------------ template -SessionManager::SessionManager(int scaleDoutPin, int scaleSckPin, uint8_t tareAvgCount) +SessionManager::SessionManager() : timeClient_(ntpUDP_, "pool.ntp.org"), measuring_(false), - lastCallTime_(0), - scaleDoutPin_(scaleDoutPin), - scaleSckPin_(scaleSckPin), - tareAvgCount_(tareAvgCount) + lastCallTime_(0) { } template void SessionManager::tare() { - if(measuring_) + if (measuring_) stopMeasurements(); Serial.println("Beginning tare"); - scale_.begin(scaleDoutPin_, scaleSckPin_); + scale_.begin(scaleDoutPin_, scaleSckPin_, valueRightShift_); scale_.tare(CONFIG_TARE_AVG_COUNT); Serial.println("Finished tare"); } template -void SessionManager::begin() +void SessionManager::begin(int scaleDoutPin, int scaleSckPin, uint8_t tareAvgCount, int valueRightShift, + MeasurementType autoStartMinTh, MeasurementType autoStartMaxTh, uint32_t autoStartMeasuresBetweenPeaks, + MeasurementType autoStopTh, uint32_t autoStopNumMeasures) { + scaleDoutPin_ = scaleDoutPin; + scaleSckPin_ = scaleSckPin; + tareAvgCount_ = tareAvgCount; + timeClient_.begin(); timeClient_.update(); tare(); session_.init(timeClient_.getEpochTime()); + + autoStart_.begin(autoStartMinTh, autoStartMaxTh, autoStartMeasuresBetweenPeaks); + autoStop_.begin(autoStopTh, autoStopNumMeasures); } template void SessionManager::startMeasurements() { - if(measuring_ == true) + if (measuring_ == true) return; measuring_ = true; lastCallTime_ = 0; @@ -84,23 +101,25 @@ void SessionManager::startMeasurements() template void SessionManager::stopMeasurements() { - if(measuring_ == false) + if (measuring_ == false) return; session_.finalize(); measuring_ = false; } +/* template void SessionManager::iteration() { - if (!measuring_) { + if (!measuring_) + { delay(1); return; // give control to HTTP server thread } - uint16_t measurement = -1; + MeasurementType measurement = -1; bool measurementDone = false; - while(!measurementDone) + while (!measurementDone) measurementDone = scale_.measure(measurement); bool addPointSuccessful = session_.addPoint(measurement); //Serial.printf("Measured: %d\n", measurement); @@ -130,4 +149,68 @@ void SessionManager::iteration() } } lastCallTime_ = millis(); -} \ No newline at end of file +} +*/ + +template +void SessionManager::iteration() +{ + MeasurementType measurement = -1; + bool measurementDone = false; + while (!measurementDone) + measurementDone = scale_.measure(measurement); + + onMeasurementTaken(measurement); + + if (lastCallTime_ != 0) + { + const long cycleDuration = millis() - lastCallTime_; + if (cycleDuration <= CONFIG_MEASURE_DELAY) + { + delay(CONFIG_MEASURE_DELAY - cycleDuration); + } + else + { + const long skipped = (cycleDuration / CONFIG_MEASURE_DELAY); + Serial.printf("Measurements skipped: %ld, cycleDuration %ld\n", skipped, cycleDuration); + + for (int i = 0; i < skipped; ++i) + onMeasurementTaken(measurement); + + delay(CONFIG_MEASURE_DELAY * (skipped + 1) - cycleDuration); + } + } + lastCallTime_ = millis(); +} + +template +void SessionManager::onMeasurementTaken(MeasurementType measurement) +{ + if (measuring_) + { + bool autoStop = autoStop_.autoStop(measurement); + if (autoStop) + { + Serial.println("Auto stop"); + stopMeasurements(); + return; + } + + bool addPointSuccessful = session_.addPoint(measurement); + if (!addPointSuccessful) + { + Serial.println("Maximum time of session reached - stopping"); + stopMeasurements(); + return; + } + } + else + { + if (autoStart_.autoStart(measurement)) + { + Serial.println("Auto start"); + startMeasurements(); + return; + } + } +} diff --git a/firmware/lib/wifimanager/WifiManager.cpp b/firmware/lib/wifimanager/WifiManager.cpp new file mode 100644 index 0000000..12214ab --- /dev/null +++ b/firmware/lib/wifimanager/WifiManager.cpp @@ -0,0 +1,105 @@ +#include "WifiManager.h" + +void WifiManager::begin(const String &hostname) +{ + hostname_ = hostname; + prefs_.begin("st_wifi_manager"); + startWifi(); +} + +void WifiManager::startWifi() +{ + WiFi.disconnect(); + + const String apPassword = prefs_.getString("apPassword", ""); + const String staSSID = prefs_.getString("staSSID", ""); + const String staPassword = prefs_.getString("staPassword", ""); + + if (staSSID.length() > 0 && staPassword.length() > 0) + { + // station mode + WiFi.mode(WIFI_STA); + WiFi.begin(staSSID.c_str(), staPassword.c_str()); + WiFi.setHostname(hostname_.c_str()); + int connectCounter = 0; + bool successful = true; + while (WiFi.status() != WL_CONNECTED) + { + delay(2000); + WiFi.begin(staSSID.c_str(), staPassword.c_str()); + connectCounter += 1; + if (connectCounter >= 60) // for two minutes no connection + { + successful = false; + break; // fallback to AP mode + } + } + state_ = STA; + if (successful) + return; + } + + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(IPAddress(192, 168, 42, 1), IPAddress(192, 168, 42, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAPsetHostname(hostname_.c_str()); + if (apPassword.length() > 0) + { + WiFi.softAP(hostname_.c_str(), apPassword.c_str()); + state_ = AP_SECURE; + } + else + { + WiFi.softAP(hostname_.c_str()); + state_ = AP_PROVISIONING; + } +} + +void WifiManager::setStaCredentials(const char *wifiName, const char *password) +{ + assert(state_ != INVALID); + + prefs_.putString("staSSID", wifiName); + prefs_.putString("staPassword", password); +} + +void WifiManager::setApCredentials(const char *password) +{ + assert(state_ != INVALID); + + prefs_.remove("staSSID"); + prefs_.remove("staPassword"); + prefs_.putString("apPassword", password); +} + +void WifiManager::resetToApProvisioning() +{ + prefs_.remove("apPassword"); + prefs_.remove("staSSID"); + prefs_.remove("staPassword"); +} + +void WifiManager::wifiWatchdog() +{ + if (state_ == STA && WiFi.status() != WL_CONNECTED) { + startWifi(); + Serial.println("Connection lost - Restarting WIFI"); + } +} + + +const char * WifiManager::stateToString(WifiManager::State state) +{ + switch (state) + { + case INVALID: + return "INVALID"; + case STA: + return "STATION_MODE"; + case AP_PROVISIONING: + return "AP_PROVISIONING"; + case AP_SECURE: + return "AP_SECURE"; + default: + return ""; + }; +} \ No newline at end of file diff --git a/firmware/lib/wifimanager/WifiManager.h b/firmware/lib/wifimanager/WifiManager.h new file mode 100644 index 0000000..501e256 --- /dev/null +++ b/firmware/lib/wifimanager/WifiManager.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Preferences.h" +#include + +/** + * Manages connection and provisioning of WiFI + * + * Starts in state AP_PROVISIONING, where device is an access point without password. + * Device should not work normally in this mode, only API is served to set password. + * Two options: + * - connect to existing WiFi (station mode), via setStaCredentials + * - create secured access point, via setApCredentials + * + * When operating in access point mode, the device IP is 192.168.42.1 + * + * call wifiWatchdog regularly to reconnect in station mode if connection was lost + */ +class WifiManager +{ +public: + enum State + { + INVALID, + STA, + AP_PROVISIONING, + AP_SECURE, + }; + WifiManager() : state_(INVALID) {} + + void begin(const String &hostname); + + void setStaCredentials(const char *wifiName, const char *password); + void setApCredentials(const char *password); + void resetToApProvisioning(); + + void wifiWatchdog(); + + bool inProvisioningMode() const { return state_ == INVALID || state_ == AP_PROVISIONING; } + + State state() const { return state_; } + const char *stateStr() const { return stateToString(state_); } + + static const char *stateToString(State state); + +private: + void startWifi(); + Preferences prefs_; + State state_; + String hostname_; +}; diff --git a/firmware/platformio.ini b/firmware/platformio.ini index a92f29d..153b24f 100644 --- a/firmware/platformio.ini +++ b/firmware/platformio.ini @@ -27,6 +27,7 @@ monitor_speed = 115200 lib_deps = NTPClient HX711@0.7.4 + ArduinoJson https://github.com/gilmaimon/ArduinoWebsockets.git src_filter = +<*> - board_build.partitions = partitions_custom.csv diff --git a/firmware/src/SwimTrackerConfig.h b/firmware/src/SwimTrackerConfig.h index d72e525..4265a76 100644 --- a/firmware/src/SwimTrackerConfig.h +++ b/firmware/src/SwimTrackerConfig.h @@ -7,18 +7,11 @@ //#define _HW_V_20 #define NEW_HEAVY_LOAD_CELL -// ------------------------------------------ WiFi --------------------------------------------------------------------------------- -const char *CONFIG_WIFI_SSID = "WLAN"; -//const char *CONFIG_WIFI_SSID = "MartinHandy"; -const char *CONFIG_WIFI_PASSWORD = "Bau3rWLAN"; -//const char *CONFIG_WIFI_PASSWORD = "35cbbd203afe"; const char *CONFIG_HOSTNAME = "swimtracker"; - // ------------------------------------- Hardware & Measurement Settings ------------------------------------------------------------ - const uint8_t CONFIG_MEASUREMENT_AVG_COUNT = 1; // number of measurements in normal phase const uint8_t CONFIG_TARE_AVG_COUNT = 20; // number of measurements in tare-phase (to find 0 ) const int CONFIG_MEASURE_DELAY = 100; // interval in ms between measurements @@ -26,10 +19,24 @@ const uint32_t CONFIG_SESSION_MAX_LENGTH_HOURS = 3; // maximum length of one ses const char *CONFIG_DATA_PATH = "/dat"; // folder in SPIFFS file system to store measurement data using MeasurementT = uint16_t; // data type for one measurement #ifdef NEW_HEAVY_LOAD_CELL -const int CONFIG_VALUE_DIVIDER = 8; // uint32 measurements are divided by this factor, before stored in uint16_t +const int CONFIG_VALUE_RIGHT_SHIFT = 3; // uint32 measurements are divided by this power, before stored in uint16_t #else -const int CONFIG_VALUE_DIVIDER = 128; // uint32 measurements are divided by this factor, before stored in uint16_t +const int CONFIG_VALUE_RIGHT_SHIFT = 7; #endif +const MeasurementT CONFIG_KG_FACTOR_INV = 701; // after shifting - how many "measurement units" are one kg + + +const char * UPDATE_URL = "https://swimtracker-update.bauer.tech/firmware.bin"; + + +// auto start/stop +MeasurementT CONFIG_AUTO_START_MIN_THRESHOLD = CONFIG_KG_FACTOR_INV * 1; +MeasurementT CONFIG_AUTO_START_MAX_THRESHOLD = CONFIG_KG_FACTOR_INV * 3; +uint32_t CONFIG_AUTO_START_MAX_MEASUREMENTS_BETWEEN_PEAKS = (1000 / CONFIG_MEASURE_DELAY) * 6; + +MeasurementT CONFIG_AUTO_STOP_THRESHOLD = CONFIG_KG_FACTOR_INV * 1; +//uint32_t CONFIG_AUTO_STOP_NUM_MEASUREMENTS = (1000 / CONFIG_MEASURE_DELAY) * 60 * 15; +uint32_t CONFIG_AUTO_STOP_NUM_MEASUREMENTS = (1000 / CONFIG_MEASURE_DELAY) * 30; // ------------------------------------- Derived Settings ----------------------------------------------------------------------------- @@ -37,11 +44,10 @@ const int CONFIG_VALUE_DIVIDER = 128; // uint32 measurements are d const uint32_t CONFIG_SESSION_MAX_SIZE = CONFIG_SESSION_MAX_LENGTH_HOURS * 3600 * (1000 / CONFIG_MEASURE_DELAY) * sizeof(uint16_t); static_assert(CONFIG_SESSION_MAX_SIZE < 1024 * 1024, "Measurement data takes more than 1MiB space"); -// HX711 load cell #ifdef _HW_V_20 const int CONFIG_SCALE_DOUT_PIN = 23; const int CONFIG_SCALE_SCK_PIN = 22; #else const int CONFIG_SCALE_DOUT_PIN = 22; const int CONFIG_SCALE_SCK_PIN = 23; -#endif \ No newline at end of file +#endif diff --git a/firmware/src/firmware_main.cpp b/firmware/src/firmware_main.cpp index 738ea82..a9b410c 100644 --- a/firmware/src/firmware_main.cpp +++ b/firmware/src/firmware_main.cpp @@ -3,10 +3,14 @@ #include "Dtypes.h" #include "SwimTrackerConfig.h" -#include +#include #include +#include "esp_https_ota.h" +#include "ESPmDNS.h" + // Own libs +#include "WifiManager.h" #include "MockScale.h" #include "Scale.h" #include "MeasurementSession.h" @@ -17,15 +21,14 @@ #include "WebDAV.h" #include "WebsocketServer.h" -#include "esp_https_ota.h" -#include "ESPmDNS.h" - using Session_T = SimpleMeasurementSession; -SessionManager sessionManager(CONFIG_SCALE_DOUT_PIN, CONFIG_SCALE_SCK_PIN, CONFIG_TARE_AVG_COUNT); +SessionManager sessionManager; EspHttp espHttpServer; WebsocketServer webSocketServer(sessionManager, 81); +WifiManager wifiManager; + extern const uint8_t certificate_pem[] asm("_binary_certificate_pem_start"); bool firmwareUpdate() @@ -33,7 +36,7 @@ bool firmwareUpdate() esp_http_client_config_t config; Serial.println((char *)certificate_pem); memset(&config, 0, sizeof(esp_http_client_config_t)); - config.url = "https://swimtracker-update.bauer.tech/firmware.bin"; + config.url = UPDATE_URL; config.cert_pem = (char *)certificate_pem; Serial.println("Starting firmware upgrade"); esp_err_t ret = esp_https_ota(&config); @@ -51,7 +54,7 @@ bool firmwareUpdate() } template -void httpSetup(SessionManager *sessionManager) +void httpSetup(SessionManager *sessionManager, WifiManager *wifiManager) { auto cbStartSession = [sessionManager](httpd_req_t *req) { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); @@ -79,63 +82,58 @@ void httpSetup(SessionManager *sessionManager) firmwareUpdate(); }; auto cbStatus = [sessionManager](httpd_req_t *req) { - String result; - result.reserve(512); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_set_hdr(req, "Content-Type", "application/json"); + StaticJsonDocument<1024> json; // session { - result += "{ \"session\": "; + JsonObject sessionObj = json.createNestedObject("session"); if (sessionManager->isMeasuring()) { const auto &session = sessionManager->session(); - result += "{ \"started\":" + String(session.getStartTime()) + ", "; - result += "\"num_measurements\":" + String(session.numMeasurements()) + "},\n"; + sessionObj["started"] = session.getStartTime(); + sessionObj["num_measurements"] = session.numMeasurements(); } - else - result += "{},\n"; } // scale { - const String tareOffset(sessionManager->tareOffset()); - const String divider(CONFIG_VALUE_DIVIDER); - result += "\"scale\": { \"tare_offset\": " + tareOffset + ", \"divider\":" + divider + "},\n"; + JsonObject scaleObj = json.createNestedObject("scale"); + scaleObj["tare_offset"] = sessionManager->tareOffset(); + scaleObj["value_right_shift"] = sessionManager->valueRightShift(); } // flash { - const String usedBytes(portablefs::usedBytes()); - const String freeBytes(portablefs::totalBytes() - portablefs::usedBytes()); - result += "\"file_system\": { \"used\": " + usedBytes + ", \"free\":" + freeBytes + "},\n"; + JsonObject fsObj = json.createNestedObject("flash"); + fsObj["used"] = portablefs::usedBytes(); + fsObj["free"] = portablefs::totalBytes() - portablefs::usedBytes(); } // RAM { - const String freeBytes(ESP.getFreeHeap()); - const String usedBytes(ESP.getHeapSize() - ESP.getFreeHeap()); - result += "\"ram\": { \"used\": " + usedBytes + ", \"free\":" + freeBytes + "},\n"; + JsonObject ramObj = json.createNestedObject("ram"); + ramObj["used"] = ESP.getHeapSize() - ESP.getFreeHeap(); + ramObj["free"] = ESP.getFreeHeap(); } // PSRAM { - const String freeBytes(ESP.getFreePsram()); - const String usedBytes(ESP.getPsramSize() - ESP.getFreePsram()); - result += "\"psram\": { \"used\": " + usedBytes + ", \"free\":" + freeBytes + "},\n"; + JsonObject psramObj = json.createNestedObject("psram"); + psramObj["used"] = ESP.getPsramSize() - ESP.getFreePsram(); + psramObj["free"] = ESP.getFreePsram(); } // firmware { auto descr = esp_ota_get_app_description(); - const String projectName(descr->project_name); - const String versionStr(descr->version); - const String idfVersion(descr->idf_ver); - const String compileDate(descr->date); - const String compileTime(descr->time); - result += "\"firmware\": { \"name\" : \"" + - projectName + "\", \"version\": \"" + - versionStr + "\", \"idfVersion\": \"" + - idfVersion + "\", \"compile_date\": \"" + - compileDate + +"\", \"compile_time\": \"" + - compileTime + "\" }\n"; + + JsonObject firmware = json.createNestedObject("firmware"); + firmware["name"] = descr->project_name; + firmware["version"] = descr->version; + firmware["idf_version"] = descr->idf_ver; + firmware["compile_date"] = descr->date; + firmware["compile_time"] = descr->time; } - result += "}"; - httpd_resp_send(req, result.c_str(), result.length()); + char jsonText[512]; + auto bytesWritten = serializeJson(json, jsonText); + httpd_resp_send(req, jsonText, bytesWritten); }; auto cbGetData = [sessionManager](httpd_req_t *req) { auto sessionId = sessionManager->session().getStartTime(); @@ -166,36 +164,88 @@ void httpSetup(SessionManager *sessionManager) httpd_resp_send(req, buf, totalSize); free(buf); }; + auto cbWifiGet = [wifiManager](httpd_req_t *req) { + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + StaticJsonDocument<128> json; + json["state"] = wifiManager->stateStr(); + char jsonText[128]; + auto bytesWritten = serializeJson(json, jsonText); + httpd_resp_send(req, jsonText, bytesWritten); + }; + auto cbWifiPost = [wifiManager](httpd_req_t *req) { + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + StaticJsonDocument<1024> json; + char content[512]; + size_t recvSize = min(req->content_len, sizeof(content)); //truncate if too long + int ret = httpd_req_recv(req, content, recvSize); + if (ret <= 0) + { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) + httpd_resp_send_408(req); + return; + } + DeserializationError err = deserializeJson(json, content); + if (err) + { + httpd_resp_set_status(req, "400 Bad Request"); + httpd_resp_send(req, "JSON parse error", -1); + return; + } + if (json.containsKey("reset_to_provisioning") && json["reset_to_provisioning"].as()) + { + wifiManager->resetToApProvisioning(); + httpd_resp_send(req, "OK", -1); + ESP.restart(); + return; + } + if (json.containsKey("ap_password")) + { + httpd_resp_send(req, "OK", -1); + wifiManager->setApCredentials(json["ap_password"].as()); + ESP.restart(); + return; + } + if (json.containsKey("sta_ssid") && json.containsKey("sta_password")) + { + wifiManager->setStaCredentials(json["sta_ssid"].as(), // + json["sta_password"].as()); + httpd_resp_send(req, "OK", -1); + ESP.restart(); + return; + } + httpd_resp_set_status(req, "400 Bad Request"); + httpd_resp_send(req, "Invalid keys in JSON", -1); + }; espHttpServer.start(); - espHttpServer.on("/api/session/start", HTTP_GET, cbStartSession); - espHttpServer.on("/api/session/start", HTTP_POST, cbStartSession); - espHttpServer.on("/api/session/stop", HTTP_GET, cbStopSession); - espHttpServer.on("/api/session/stop", HTTP_POST, cbStopSession); - - espHttpServer.on("/api/session/data", HTTP_GET, cbGetData); - espHttpServer.on("/api/status", HTTP_GET, cbStatus); - espHttpServer.on("/api/tare", HTTP_GET, cbTare); + espHttpServer.on("/api/wifi", HTTP_GET, cbWifiGet); + espHttpServer.on("/api/wifi", HTTP_POST, cbWifiPost); espHttpServer.on("/api/restart", HTTP_GET, cbRestart); - espHttpServer.on("/api/firmwareupdate", HTTP_GET, cbFirmwareUpdate); - auto webdav = webdavHandler("/webdav/", "/dat"); - espHttpServer.on("/webdav/*?", HTTP_GET, webdav); - espHttpServer.on("/webdav/*?", HTTP_PROPFIND, webdav); - espHttpServer.on("/webdav/*?", HTTP_DELETE, webdav); - espHttpServer.on("/webdav/*?", HTTP_OPTIONS, webdav); - - Serial.println("HTTP setup done"); -} - -void checkWifi() -{ - while (WiFi.status() != WL_CONNECTED) + if (!wifiManager->inProvisioningMode()) { - Serial.println("WiFi disconnected. Try to reconnect"); - WiFi.reconnect(); - delay(2000); + espHttpServer.on("/api/session/start", HTTP_GET, cbStartSession); + espHttpServer.on("/api/session/start", HTTP_POST, cbStartSession); + + espHttpServer.on("/api/session/stop", HTTP_GET, cbStopSession); + espHttpServer.on("/api/session/stop", HTTP_POST, cbStopSession); + + espHttpServer.on("/api/session/data", HTTP_GET, cbGetData); + espHttpServer.on("/api/status", HTTP_GET, cbStatus); + espHttpServer.on("/api/tare", HTTP_GET, cbTare); + espHttpServer.on("/api/firmwareupdate", HTTP_GET, cbFirmwareUpdate); + + auto webdav = webdavHandler("/webdav/", "/dat"); + espHttpServer.on("/webdav/*?", HTTP_GET, webdav); + espHttpServer.on("/webdav/*?", HTTP_PROPFIND, webdav); + espHttpServer.on("/webdav/*?", HTTP_DELETE, webdav); + espHttpServer.on("/webdav/*?", HTTP_OPTIONS, webdav); + Serial.println("HTTP setup done"); + } + else + { + Serial.println("HTTP setup with limited API in provisioning mode"); } } @@ -213,25 +263,13 @@ void mdnsSetup(const String &fullHostname) if (!MDNS.begin(fullHostname.c_str())) { Serial.println("Error setting up MDNS responder!"); - while (1) + while (true) { delay(1000); } } Serial.printf("mDNS started %s\n", fullHostname.c_str()); - MDNS.addService("_swimtracker", "tcp", 80); - /* - // mDNS - esp_err_t err = mdns_init(); - if (err) - Serial.printf("MDNS Init failed: %d\n", err); - else { - Serial.printf("Setting up zeroconf hostname %s\n", fullHostname.c_str()); - mdns_hostname_set(fullHostname.c_str()); - mdns_service_add(NULL, "_swimtracker", "_tcp", 81, NULL, 0); - mdns_instance_name_set("SwimTracker by bauer.tech"); - } - */ + MDNS.addService("swimtracker", "tcp", 81); } void setup() @@ -241,7 +279,6 @@ void setup() while (!Serial) { } - Serial.printf("Starting SwimTracker Firmware - connecting to %s\n", CONFIG_WIFI_SSID); // File system bool spiffsResult = SPIFFS.begin(true); @@ -251,41 +288,40 @@ void setup() ESP_ERROR_CHECK(esp_event_loop_create_default()); // WiFi - WiFi.disconnect(); - WiFi.mode(WIFI_STA); - WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD); String fullHostname = String(CONFIG_HOSTNAME) + getIdSuffix(); - WiFi.setHostname(fullHostname.c_str()); - Serial.println(F("Waiting for WIFI connection...")); - int connectCounter = 0; - while (WiFi.status() != WL_CONNECTED) - { - delay(1000); - connectCounter += 1; - if (connectCounter > 5) - { - Serial.println("Couldn't obtain WIFI - retrying.."); - WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD); - } - } + wifiManager.begin(fullHostname); Serial.print("Connected to WiFi. IP:"); Serial.println(WiFi.localIP()); + Serial.printf("WIFI state: %s\n", wifiManager.stateStr()); mdnsSetup(fullHostname); + Preferences scalePrefs; + scalePrefs.begin("st_prefs"); + int valueRightShift = scalePrefs.getInt("valueRightShift", CONFIG_VALUE_RIGHT_SHIFT); + uint8_t tareAvgCount = scalePrefs.getUInt("tareAvgCount", CONFIG_TARE_AVG_COUNT); + MeasurementT autoStartMinThreshold = scalePrefs.getUInt("autoStartMinThreshold", CONFIG_AUTO_START_MIN_THRESHOLD); + MeasurementT autoStartMaxThreshold = scalePrefs.getUInt("autoStartMinThreshold", CONFIG_AUTO_START_MAX_THRESHOLD); + uint32_t autoStartMaxMeasurementsBetweenPeaks = scalePrefs.getUInt("autoStartMaxMeasurementsBetweenPeaks", CONFIG_AUTO_START_MAX_MEASUREMENTS_BETWEEN_PEAKS); + MeasurementT autoStopThreshold = scalePrefs.getUInt("autoStopThreshold", CONFIG_AUTO_STOP_THRESHOLD); + uint32_t autoStopNumMeasurements = scalePrefs.getUInt("autoStopNumMeasurements", CONFIG_AUTO_STOP_NUM_MEASUREMENTS); + + // Session - sessionManager.begin(); + sessionManager.begin(CONFIG_SCALE_DOUT_PIN, CONFIG_SCALE_SCK_PIN, + tareAvgCount, valueRightShift, + autoStartMinThreshold, autoStartMaxThreshold, autoStartMaxMeasurementsBetweenPeaks, + autoStopThreshold, autoStopNumMeasurements); // HTTP & Websocket server - httpSetup(&sessionManager); - webSocketServer.begin(); + httpSetup(&sessionManager, &wifiManager); + if (!wifiManager.inProvisioningMode()) + webSocketServer.begin(); } -int measurementsSent = 0; - void loop() { sessionManager.iteration(); webSocketServer.iteration(); - checkWifi(); + wifiManager.wifiWatchdog(); } diff --git a/hardware/OtherBoards.md b/hardware/OtherBoards.md deleted file mode 100644 index e0d66a5..0000000 --- a/hardware/OtherBoards.md +++ /dev/null @@ -1,2 +0,0 @@ - -https://github.com/adafruit/Adafruit-HUZZAH32-ESP32-Feather-PCB.git diff --git a/hardware/ResearchOwnDesign.md b/hardware/ResearchOwnDesign.md new file mode 100644 index 0000000..46fd076 --- /dev/null +++ b/hardware/ResearchOwnDesign.md @@ -0,0 +1,29 @@ + + +Components +========== + https://lcsc.com/ + + + - ESP32-WROVER-E (has PSRAM compared to wroom, the IE version doesn't have pcb antenna) + https://www.mouser.de/ProductDetail/Espressif-Systems/ESP32-WROVER-EM213EH2864PH3Q0?qs=sGAEpiMZZMu3sxpa5v1qrgqRbH4gaXhhyqOoBsYWKak%3D + - HX711 ADC for weight scale + mouser doesn't have it? + + +Tools +===== + - easyeda has lcsc library, to import in kicad use: + https://wokwi.com/easyeda2kicad + + +Related open source PCBs +======================== + + https://github.com/adafruit/Adafruit-HUZZAH32-ESP32-Feather-PCB.git + + + +Requirements HX711 +================== + - \ No newline at end of file