diff --git a/.gitignore b/.gitignore index df0bf16..19e2589 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ __pycache__ *.st /espidf - +/venv diff --git a/firmware/lib/userdb/UserDB.cpp b/firmware/lib/userdb/UserDB.cpp index f34c3b8..f5d1a78 100644 --- a/firmware/lib/userdb/UserDB.cpp +++ b/firmware/lib/userdb/UserDB.cpp @@ -7,13 +7,13 @@ template ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T &val) { ForwardIterator it; - iterator_traits::difference_type count, step; - count = distance(first, last); + int count, step; + count = last - first; while (count > 0) { it = first; step = count / 2; - advance(it, step); + it += step; if (*it < val) { first = ++it; @@ -30,11 +30,11 @@ static String userFileName(const String &userName) return userDir + userName; } -bool User::load(const String &name) +bool User::load(const String &stringId) { - name_ = name; - assert(name_.length() > 0); - const auto fileName = userFileName(name_); + stringId_ = stringId; + assert(stringId_.length() > 0); + const auto fileName = userFileName(stringId_); if (!portablefs::exists(fileName.c_str())) return false; @@ -43,7 +43,7 @@ bool User::load(const String &name) size_t sessionsInFile; file.read((uint8_t *)&sessionsInFile, sizeof(numSessions_)); - init(name, sessionsInFile * 2); + init(stringId, sessionsInFile * 2); size_t expectedSize = sizeof(SessionIdType) * numSessions_; auto bytesRead = file.read((uint8_t *)sessionIds_, expectedSize); @@ -52,14 +52,14 @@ bool User::load(const String &name) assert(expectedSize == bytesRead); } -void User::init(const String &name, size_t sessionAllocateSize) +void User::init(const String &stringId, size_t sessionAllocateSize) { if (sessionIds_ != nullptr) { free(sessionIds_); sessionIds_ = nullptr; } - name_ = name; + stringId_ = stringId; numSessionsAllocated_ = sessionAllocateSize; sessionIds_ = (SessionIdType *)ps_malloc(sizeof(SessionIdType) * numSessionsAllocated_); numSessions_ = 0; @@ -70,7 +70,7 @@ void User::save() if (!portablefs::exists(userDir.c_str())) portablefs::mkdir(userDir.c_str()); - auto file = portablefs::open(userFileName(name_).c_str(), "w"); + auto file = portablefs::open(userFileName(stringId_).c_str(), "w"); file.write((uint8_t *)&numSessions_, sizeof(numSessions_)); file.write((uint8_t *)sessionIds_, sizeof(SessionIdType) * numSessions_); } @@ -86,9 +86,9 @@ void User::freeResources() void User::remove() { - portablefs::remove(userFileName(name_).c_str()); + portablefs::remove(userFileName(stringId_).c_str()); freeResources(); - name_ = ""; + stringId_ = ""; numSessions_ = 0; numSessionsAllocated_ = 0; } @@ -134,11 +134,11 @@ bool User::removeSession(SessionIdType sessionIdToRemove) // -------------------------------------------------------------------------------------------------------------------- -User *UserStorage::getUserInfo(const String &userName) +User *UserStorage::getUserInfo(const String &stringId) { // index 0 is the unassigned user for (size_t i = 1; i < numUsers_; ++i) - if (users_[i].name() == userName) + if (users_[i].stringId() == stringId) return &users_[i]; return nullptr; } @@ -148,20 +148,23 @@ User *UserStorage::getUnassignedUser() return &users_[0]; } -User *UserStorage::addNewUser(const String &userName) +User *UserStorage::addNewUser(const String &stringId) { if (numUsers_ >= MAX_USERS) return nullptr; + + if(stringId.length() >= USER_STRING_ID_MAX_LEN) + return nullptr; auto userIdx = numUsers_; numUsers_++; assert(numUsers_ < MAX_USERS); - users_[userIdx].init(userName); + users_[userIdx].init(stringId); } -bool UserStorage::deleteUser(const String &userName) +bool UserStorage::deleteUser(const String &stringId) { - User *userPtr = getUserInfo(userName); + User *userPtr = getUserInfo(stringId); if (userPtr == nullptr) return false; diff --git a/firmware/lib/userdb/UserDB.h b/firmware/lib/userdb/UserDB.h index 30b5247..7977ce9 100644 --- a/firmware/lib/userdb/UserDB.h +++ b/firmware/lib/userdb/UserDB.h @@ -5,13 +5,14 @@ using SessionIdType = uint32_t; constexpr size_t MAX_USERS = 64; constexpr size_t INITIAL_SESSIONS_PER_USER = 128; +constexpr size_t USER_STRING_ID_MAX_LEN = 63; struct User { public: User() : numSessions_(0), numSessionsAllocated_(0), sessionIds_(nullptr) {} - void init(const String &name, size_t sessionAllocateSize = INITIAL_SESSIONS_PER_USER); + void init(const String &stringId_, size_t sessionAllocateSize = INITIAL_SESSIONS_PER_USER); void freeResources(); bool load(const String &name); @@ -24,19 +25,19 @@ public: bool hasSession(SessionIdType sessionId) const; - const String &name() const { return name_; } + const String &stringId() const { return stringId_; } // session access SessionIdType *sessionBegin() { return sessionIds_; } SessionIdType *sessionEnd() { return sessionIds_ + numSessions_; } - + size_t numSessions() const { return numSessions_; } const SessionIdType *sessionBegin() const { return sessionIds_; } const SessionIdType *sessionEnd() const { return sessionIds_ + numSessions_; } private: void growSessionArrayIfNecessary(); - String name_; + String stringId_; size_t numSessions_; size_t numSessionsAllocated_; SessionIdType *sessionIds_; @@ -48,20 +49,29 @@ public: UserStorage() : numUsers_(0) { + } + + void init() + { + numUsers_ = 0; fillFromFileSystem(); } - User *getUserInfo(const String &userName); + + User *getUserInfo(const String &stringId); User *getUnassignedUser(); - User *addNewUser(const String &userName); - bool deleteUser(const String &userName); + User *addNewUser(const String &stringId); + bool deleteUser(const String &stringId); User *begin() { return &users_[0]; } + User *beginWithoutUnassigned() { return &users_[1]; } User *end() { return &users_[numUsers_]; } const User *begin() const { return &users_[0]; } const User *end() const { return &users_[numUsers_]; } + size_t numUsers() const { return numUsers_; } + private: void fillFromFileSystem(); diff --git a/firmware/platformio.ini b/firmware/platformio.ini index 153b24f..3d3df69 100644 --- a/firmware/platformio.ini +++ b/firmware/platformio.ini @@ -13,8 +13,8 @@ data_dir = data [env:esp32] platform = espressif32 -platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32 +#platform_packages = +# framework-arduinoespressif32@https://github.com/espressif/arduino-esp32 board = esp-wrover-kit #platform = espressif8266 #board = esp_wroom_02 diff --git a/firmware/src/WebsocketServer.h b/firmware/src/WebsocketServer.h index 40ed804..ee49939 100644 --- a/firmware/src/WebsocketServer.h +++ b/firmware/src/WebsocketServer.h @@ -1,6 +1,7 @@ #pragma once #include "Dtypes.h" +#include "UserDB.h" #include template @@ -13,8 +14,8 @@ template class WebsocketServer { public: - WebsocketServer(SessionManager &sessionManager, int port) - : sessionManager_(sessionManager), nextFreeClient_(0), port_(port), + WebsocketServer(SessionManager &sessionManager, UserStorage &userStorage, int port) + : sessionManager_(sessionManager), userStorage_(userStorage), nextFreeClient_(0), port_(port), running_(false) { } @@ -34,7 +35,12 @@ private: void sendSessionStopMessages(); void sendNewDataMessages(); + void sendUserList(websockets::WebsocketsClient &client); + void sendSessionList(websockets::WebsocketsClient &client, const String &userId); + SessionManager &sessionManager_; + UserStorage &userStorage_; + int nextFreeClient_; int port_; @@ -58,11 +64,15 @@ enum MessageType SESSION_STARTED = 2, SESSION_STOPPED = 3, SESSION_NEW_DATA = 4, + ANSWER_USER_LIST = 5, + ANSWER_SESSION_LIST = 6, // from frontend to device - START_SESSION = 5, - STOP_SESSION = 6, - TARE = 7 + START_SESSION = 7, + STOP_SESSION = 8, + TARE = 9, + QUERY_USER_LIST = 10, + QUERY_SESSION_LIST = 11, }; #pragma pack(push, 1) @@ -129,6 +139,7 @@ private: // book-keeping size_t numMeasurements_; }; + #pragma pack(pop) // ------------------------------------- WebsocketServer members --------------------------- @@ -137,6 +148,7 @@ template void WebsocketServer::iteration() { using namespace websockets; + auto onMessage = [this](WebsocketsClient &client, WebsocketsMessage message) { if (message.isPing()) client.pong(); @@ -162,6 +174,18 @@ void WebsocketServer::iteration() case TARE: this->sessionManager_.tare(); break; + case QUERY_USER_LIST: + this->sendUserList(client); + break; + case QUERY_SESSION_LIST: + { + StaticJsonDocument doc; + deserializeMsgPack(doc, data, length); + String userId = doc.as(); + if (userId.length() > 0) + this->sendSessionList(client, userId); + } + break; default: client.close(CloseReason_UnsupportedData); return; @@ -200,10 +224,53 @@ void WebsocketServer::reportSessionUpdate() for (int i = 0; i < MAX_CONNECTIONS; ++i) numSentMeasurements_[i] = 0; } - sendNewDataMessages(); } +template +void WebsocketServer::sendUserList(websockets::WebsocketsClient &client) +{ + const auto numUsers = userStorage_.numUsers(); + constexpr size_t constantSlack = 64; + DynamicJsonDocument result(JSON_ARRAY_SIZE(numUsers) + numUsers * (USER_STRING_ID_MAX_LEN + 2) + constantSlack); + JsonArray arr = result.to(); + for (auto userIt = userStorage_.beginWithoutUnassigned(); userIt != userStorage_.end(); ++userIt) + arr.add(userIt->stringId()); + + char buffer[MAX_USERS * (USER_STRING_ID_MAX_LEN + 1) + constantSlack]; + size_t bytesWritten = serializeMsgPack(result, buffer, sizeof(buffer)); + client.sendBinary(buffer, bytesWritten); +} + +template +void WebsocketServer::sendSessionList(websockets::WebsocketsClient &client, const String &userId) +{ + + User *user = userStorage_.getUserInfo(userId); + if (user != nullptr) + { + DynamicJsonDocument result(JSON_ARRAY_SIZE(user->numSessions()) + user->numSessions() * (sizeof(SessionIdType) + 8)); + JsonArray arr = result.to(); + for (SessionIdType *sIt = user->sessionBegin(); sIt != user->sessionEnd(); ++sIt) + arr.add(*sIt); + + size_t bytesToWrite = measureMsgPack(result); + char *buffer = (char *)malloc(bytesToWrite); + size_t bytesWritten = serializeMsgPack(result, buffer, bytesToWrite); + assert(bytesWritten <= bytesToWrite); + client.sendBinary(buffer, bytesWritten); + free(buffer); + } + else + { + DynamicJsonDocument result(JSON_ARRAY_SIZE(1) + 8); + result.to(); + char buffer[32]; + size_t bytesWritten = serializeMsgPack(result, buffer, sizeof(buffer)); + client.sendBinary(buffer, bytesWritten); + } +} + template void WebsocketServer::sendSessionStartMessages() { diff --git a/firmware/src/firmware_main.cpp b/firmware/src/firmware_main.cpp index e17f717..de58b18 100644 --- a/firmware/src/firmware_main.cpp +++ b/firmware/src/firmware_main.cpp @@ -7,6 +7,7 @@ #include #include "esp_https_ota.h" +#include "esp_ota_ops.h" #include "ESPmDNS.h" // Own libs @@ -20,12 +21,15 @@ #include "EspHttp.h" #include "WebDAV.h" #include "WebsocketServer.h" +#include "UserDB.h" using Session_T = SimpleMeasurementSession; SessionManager sessionManager; +UserStorage userStorage; + EspHttp espHttpServer; -WebsocketServer webSocketServer(sessionManager, 81); +WebsocketServer webSocketServer(sessionManager, userStorage, 81); WifiManager wifiManager; @@ -253,14 +257,14 @@ void httpSetup(SessionManager *sessionManager, WifiManager *wifiManage prefs.begin("st_prefs"); json["valueRightShift"] = prefs.getInt("valueRightShift", CONFIG_VALUE_RIGHT_SHIFT); - json["tareAvgCount"] = prefs.getUInt("tareAvgCount", CONFIG_TARE_AVG_COUNT); + json["tareAvgCount"] = prefs.getUInt("tareAvgCount", CONFIG_TARE_AVG_COUNT); json["autoStartMinThreshold"] = prefs.getUInt("aStartMinTh", CONFIG_AUTO_START_MIN_THRESHOLD); json["autoStartMaxThreshold"] = prefs.getUInt("aStartMaxTh", CONFIG_AUTO_START_MAX_THRESHOLD); json["autoStartMaxMeasurementsBetweenPeaks"] = prefs.getUInt("aStartCount", CONFIG_AUTO_START_MAX_MEASUREMENTS_BETWEEN_PEAKS); json["autoStopThreshold"] = prefs.getUInt("aStopTh", CONFIG_AUTO_STOP_THRESHOLD); json["autoStopNumMeasurements"] = prefs.getUInt("aStopCount", CONFIG_AUTO_STOP_NUM_MEASUREMENTS); - json["autoStartEnabled"] = prefs.getBool("aStartEnabled", true); - json["autoStopEnabled"] = prefs.getBool("aStopEnabled", true); + json["autoStartEnabled"] = prefs.getBool("aStartEnabled", true); + json["autoStopEnabled"] = prefs.getBool("aStopEnabled", true); char jsonText[1024]; auto bytesWritten = serializeJson(json, jsonText); httpd_resp_send(req, jsonText, bytesWritten); @@ -287,25 +291,25 @@ void httpSetup(SessionManager *sessionManager, WifiManager *wifiManage Preferences prefs; prefs.begin("st_prefs"); - if(json.containsKey("valueRightShift")) + if (json.containsKey("valueRightShift")) prefs.putInt("valueRightShift", json["valueRightShift"].as()); - if(json.containsKey("tareAvgCount")) + if (json.containsKey("tareAvgCount")) prefs.putUInt("tareAvgCount", json["tareAvgCount"].as()); - if(json.containsKey("autoStartMinThreshold")) + if (json.containsKey("autoStartMinThreshold")) prefs.putUInt("aStartMinTh", json["autoStartMinThreshold"].as()); - if(json.containsKey("autoStartMaxThreshold")) + if (json.containsKey("autoStartMaxThreshold")) prefs.putUInt("aStartMaxTh", json["autoStartMaxThreshold"].as()); - if(json.containsKey("autoStartMaxMeasurementsBetweenPeaks")) + if (json.containsKey("autoStartMaxMeasurementsBetweenPeaks")) prefs.putUInt("aStartCount", json["autoStartMaxMeasurementsBetweenPeaks"].as()); - if(json.containsKey("autoStopThreshold")) + if (json.containsKey("autoStopThreshold")) prefs.putUInt("aStopTh", json["autoStopThreshold"].as()); - if(json.containsKey("autoStopNumMeasurements")) + if (json.containsKey("autoStopNumMeasurements")) prefs.putUInt("aStopCount", json["autoStopNumMeasurements"].as()); - if(json.containsKey("autoStartEnabled")) + if (json.containsKey("autoStartEnabled")) prefs.putBool("aStartEnabled", json["autoStartEnabled"].as()); - if(json.containsKey("autoStopEnabled")) + if (json.containsKey("autoStopEnabled")) prefs.putBool("aStopEnabled", json["autoStopEnabled"].as()); - + sessionManagerSetup(); httpd_resp_send(req, "OK", -1); }; @@ -399,6 +403,8 @@ void setup() ESP_ERROR_CHECK(esp_event_loop_create_default()); + userStorage.init(); + // WiFi String fullHostname = String(CONFIG_HOSTNAME) + getIdSuffix(); wifiManager.begin(fullHostname); diff --git a/hardware/swimtrackerpcb/3dview/3dview.png b/hardware/swimtrackerpcb/3dview/3dview.png new file mode 100644 index 0000000..a66b938 Binary files /dev/null and b/hardware/swimtrackerpcb/3dview/3dview.png differ diff --git a/hardware/swimtrackerpcb/board_pngs/BottomLayer.png b/hardware/swimtrackerpcb/board_pngs/BottomLayer.png new file mode 100644 index 0000000..48eb042 Binary files /dev/null and b/hardware/swimtrackerpcb/board_pngs/BottomLayer.png differ diff --git a/hardware/swimtrackerpcb/board_pngs/BottomLayerMerged.png b/hardware/swimtrackerpcb/board_pngs/BottomLayerMerged.png new file mode 100644 index 0000000..f5f0737 Binary files /dev/null and b/hardware/swimtrackerpcb/board_pngs/BottomLayerMerged.png differ diff --git a/hardware/swimtrackerpcb/board_pngs/BottomLayerWithAreas.png b/hardware/swimtrackerpcb/board_pngs/BottomLayerWithAreas.png new file mode 100644 index 0000000..3747d59 Binary files /dev/null and b/hardware/swimtrackerpcb/board_pngs/BottomLayerWithAreas.png differ diff --git a/hardware/swimtrackerpcb/board_pngs/TopLayer.png b/hardware/swimtrackerpcb/board_pngs/TopLayer.png new file mode 100644 index 0000000..b273576 Binary files /dev/null and b/hardware/swimtrackerpcb/board_pngs/TopLayer.png differ diff --git a/hardware/swimtrackerpcb/board_pngs/TopLayerHiddenAreas.png b/hardware/swimtrackerpcb/board_pngs/TopLayerHiddenAreas.png new file mode 100644 index 0000000..5a858a7 Binary files /dev/null and b/hardware/swimtrackerpcb/board_pngs/TopLayerHiddenAreas.png differ diff --git a/hardware/swimtrackerpcb/info.md b/hardware/swimtrackerpcb/info.md new file mode 100644 index 0000000..9e624ea --- /dev/null +++ b/hardware/swimtrackerpcb/info.md @@ -0,0 +1,39 @@ +Infos: +====== + + +- Ziel: An das board soll direkt eine Wägezelle angeschlossen werden und die Messwerte per WLAN über den ESP32 verschickt werden +- bisher hatte ich ein ESP32 Entwicklungsboard zusammen mit einem HX711 board [1] +- das neue board soll mit standard Outdoornetzteilen funktionieren, die zwischen 12V und 25V liefern + - als Alternative soll auch Spannungsversorgung per USB möglich sein (in aktueller Version noch nicht vorgesehen) + aber deswegen 24V -> 5V -> 3.3V +- Die Schaltungen sind zum Teil aus anderen Projekten/Beispielen die ich auch GitHub und in Datenblättern gefunden habe zusammenkopiert + ich hab im schematic.pdf die Quellen dazugeschrieben +- Ich hab das Board mit dem Webtool EasyEDA erstellt, weil ichs von dort direkt bei JLCPCB bestellen und bestücken lassen kann. + Im Ordner sind exportierte Dateien und ein paar Screenshots. Ich habe auch einen share-link erstellt [2] mit dem mans direkt im Browser anschauen können sollte. Habs noch nie probiert, wenn was nicht geht, einfach schreiben. + In EasyEDA kommt man auch leicht zu den Datenblättern, Bauteil auswählen, dann rechts in der Info Leiste + + + +Kritische Stellen an denen ich nicht so richtig wusste was ich tue ;) +===================================================================== + +- Allgemein der Buck Converter von 24 -> 5 + - von TI gibts ein Webinterface mit dem ich das ganze dimensioniert habe (im schematic.pdf steht genaueres) + - das PCB Layout des Buck Converters ist wegen der hohen Frequenzen ja etwas heikel. + Im Datenblatt des LM2596SX gibts ein Beispiel Layout, aber das passt nicht genau auf die Variante die ich verbaut habe. + In diesem Video [3] macht jemand ein Layout, das ich als Vorbild genommen hab. + Ich weiss allerdings nicht ob man der Quelle trauen kann ;) +- Die Kondensatoren zur 3.3V Stabilisierung sind glaube ich doppelt, + da ich das ESP32 Entwicklungsboard und das HX711 Board "zusammenkopiert" hab. + Zur Sicherheit hab ichs mal drauf gelassen. +- In der aktuellen Version laufen die Input Daten der Lastzelle recht nahe an der ESP32 WiFi Antenne vorbei. + Das ist wahrscheinlich keine Gute Idee, oder? Wie weit sollte man da Abstand halten? + + + + + +[1] https://www.amazon.de/SparkFun-SEN-13879-W%C3%A4gezelle-Verst%C3%A4rker-HX711/dp/B00NPZ4CPG +[2] https://easyeda.com/join?type=project&key=c92800670a9943c22ecb243ed5cb0455 +[3] https://www.youtube.com/watch?v=nKOWBungcHo&t=771s diff --git a/hardware/swimtrackerpcb/schematics.pdf b/hardware/swimtrackerpcb/schematics.pdf new file mode 100644 index 0000000..af09a12 Binary files /dev/null and b/hardware/swimtrackerpcb/schematics.pdf differ