First untested version with user storage

This commit is contained in:
Martin Bauer 2021-05-16 13:45:00 +02:00
parent 06fdda1d93
commit 5984a233af
14 changed files with 174 additions and 49 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
__pycache__
*.st
/espidf
/venv

View File

@ -7,13 +7,13 @@ template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T &val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::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;

View File

@ -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();

View File

@ -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

View File

@ -1,6 +1,7 @@
#pragma once
#include "Dtypes.h"
#include "UserDB.h"
#include <ArduinoWebsockets.h>
template <typename T>
@ -13,8 +14,8 @@ template <typename SessionT>
class WebsocketServer
{
public:
WebsocketServer(SessionManager<SessionT> &sessionManager, int port)
: sessionManager_(sessionManager), nextFreeClient_(0), port_(port),
WebsocketServer(SessionManager<SessionT> &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<SessionT> &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 <typename SessionT>
void WebsocketServer<SessionT>::iteration()
{
using namespace websockets;
auto onMessage = [this](WebsocketsClient &client, WebsocketsMessage message) {
if (message.isPing())
client.pong();
@ -162,6 +174,18 @@ void WebsocketServer<SessionT>::iteration()
case TARE:
this->sessionManager_.tare();
break;
case QUERY_USER_LIST:
this->sendUserList(client);
break;
case QUERY_SESSION_LIST:
{
StaticJsonDocument<USER_STRING_ID_MAX_LEN + 16> doc;
deserializeMsgPack(doc, data, length);
String userId = doc.as<String>();
if (userId.length() > 0)
this->sendSessionList(client, userId);
}
break;
default:
client.close(CloseReason_UnsupportedData);
return;
@ -200,10 +224,53 @@ void WebsocketServer<SessionT>::reportSessionUpdate()
for (int i = 0; i < MAX_CONNECTIONS; ++i)
numSentMeasurements_[i] = 0;
}
sendNewDataMessages();
}
template <typename SessionT>
void WebsocketServer<SessionT>::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<JsonArray>();
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 <typename SessionT>
void WebsocketServer<SessionT>::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<JsonArray>();
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<JsonArray>();
char buffer[32];
size_t bytesWritten = serializeMsgPack(result, buffer, sizeof(buffer));
client.sendBinary(buffer, bytesWritten);
}
}
template <typename SessionT>
void WebsocketServer<SessionT>::sendSessionStartMessages()
{

View File

@ -7,6 +7,7 @@
#include <HTTPClient.h>
#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<MeasurementT, CONFIG_SESSION_MAX_SIZE>;
SessionManager<Session_T> sessionManager;
UserStorage userStorage;
EspHttp espHttpServer;
WebsocketServer<Session_T> webSocketServer(sessionManager, 81);
WebsocketServer<Session_T> webSocketServer(sessionManager, userStorage, 81);
WifiManager wifiManager;
@ -287,23 +291,23 @@ void httpSetup(SessionManager<SessionT> *sessionManager, WifiManager *wifiManage
Preferences prefs;
prefs.begin("st_prefs");
if(json.containsKey("valueRightShift"))
if (json.containsKey("valueRightShift"))
prefs.putInt("valueRightShift", json["valueRightShift"].as<int>());
if(json.containsKey("tareAvgCount"))
if (json.containsKey("tareAvgCount"))
prefs.putUInt("tareAvgCount", json["tareAvgCount"].as<unsigned int>());
if(json.containsKey("autoStartMinThreshold"))
if (json.containsKey("autoStartMinThreshold"))
prefs.putUInt("aStartMinTh", json["autoStartMinThreshold"].as<unsigned int>());
if(json.containsKey("autoStartMaxThreshold"))
if (json.containsKey("autoStartMaxThreshold"))
prefs.putUInt("aStartMaxTh", json["autoStartMaxThreshold"].as<unsigned int>());
if(json.containsKey("autoStartMaxMeasurementsBetweenPeaks"))
if (json.containsKey("autoStartMaxMeasurementsBetweenPeaks"))
prefs.putUInt("aStartCount", json["autoStartMaxMeasurementsBetweenPeaks"].as<unsigned int>());
if(json.containsKey("autoStopThreshold"))
if (json.containsKey("autoStopThreshold"))
prefs.putUInt("aStopTh", json["autoStopThreshold"].as<unsigned int>());
if(json.containsKey("autoStopNumMeasurements"))
if (json.containsKey("autoStopNumMeasurements"))
prefs.putUInt("aStopCount", json["autoStopNumMeasurements"].as<unsigned int>());
if(json.containsKey("autoStartEnabled"))
if (json.containsKey("autoStartEnabled"))
prefs.putBool("aStartEnabled", json["autoStartEnabled"].as<bool>());
if(json.containsKey("autoStopEnabled"))
if (json.containsKey("autoStopEnabled"))
prefs.putBool("aStopEnabled", json["autoStopEnabled"].as<bool>());
sessionManagerSetup();
@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

View File

@ -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

Binary file not shown.