aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore17
-rw-r--r--.vscode/launch.json3
-rw-r--r--client/Client.cpp65
-rw-r--r--client/Client.h45
-rw-r--r--client/HandleMessage.cpp47
-rw-r--r--client/HandleMessage.h26
-rw-r--r--client/client.pro14
-rw-r--r--client/client.pro.user260
-rw-r--r--client/dialog.cpp14
-rw-r--r--client/dialog.h22
-rw-r--r--client/dialog.ui18
-rw-r--r--client/main.cpp7
-rw-r--r--client/mainwindow.cpp6
-rw-r--r--client/mainwindow.h5
-rw-r--r--client/mainwindow.ui6
-rw-r--r--client/mytcpsocket.cpp44
-rw-r--r--client/mytcpsocket.h32
-rw-r--r--client/settingsmenu.cpp47
-rw-r--r--client/settingsmenu.h34
-rw-r--r--client/settingsmenu.ui144
-rw-r--r--client/timetest.cpp18
-rw-r--r--client/timetest.h27
-rw-r--r--client/ui_dbconnector.h163
-rw-r--r--client/ui_mainwindow.h110
-rw-r--r--readme.md6
-rwxr-xr-xscripts/compiledb-full-path-mingw.sh2
-rw-r--r--scripts/db-init.sql20
-rw-r--r--scripts/db-privileges.sql1
-rwxr-xr-xscripts/dummy-server.py48
-rw-r--r--shared/bin.c11
-rw-r--r--shared/bin.h13
-rw-r--r--shared/protocol.c147
-rw-r--r--shared/protocol.h151
-rw-r--r--shared/protocol.md57
-rw-r--r--shared/shared.mk1
-rw-r--r--shared/util.c7
-rw-r--r--shared/util.h6
-rw-r--r--stm32f091/backlog.c36
-rw-r--r--stm32f091/backlog.h4
-rw-r--r--stm32f091/consts.h27
-rw-r--r--stm32f091/esp8266.c240
-rw-r--r--stm32f091/esp8266.h44
-rw-r--r--stm32f091/main.c12
-rw-r--r--stm32f091/makefile17
-rw-r--r--stm32f091/protocol.c41
-rw-r--r--stm32f091/readme.md11
-rw-r--r--stm32f091/sensor.c6
-rw-r--r--stm32f091/server.c255
-rw-r--r--stm32f091/server.h99
-rw-r--r--stm32f091/setup.c97
-rw-r--r--stm32f091/setup.h17
-rw-r--r--stm32f091/util.h51
-rw-r--r--stm32f091/wifi.def.h7
-rw-r--r--todo.md28
54 files changed, 2187 insertions, 449 deletions
diff --git a/.gitignore b/.gitignore
index 1c68c8a..68c9d32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,22 @@
-copyright
+# blob
**/*.o
-stm32f091/main.elf
-stm32f091/main.bin
**/.cache
**/compile_commands.json
+
+# stm32-specific files
+stm32f091/main.elf
+stm32f091/main.bin
+stm32f091/wifi.h
+
+# client-specific files
client/makefile
client/client
client/moc_*
+
+# others
+shared/main
.qmake.stash
.vscode/.cortex-debug.registers.state.json
+copyright
+temp/
+**/.DS_Store
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 9c586dc..b104e0f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -31,9 +31,8 @@
"executable": "${workspaceFolder}/stm32f091/main.elf",
"request": "launch",
"type": "cortex-debug",
- "runToEntryPoint": "main",
"servertype": "stlink",
- "preLaunchTask": "stm32/build",
+ "preLaunchTask": "stm32/flash",
"linux": {
"stlinkPath": "/usr/bin/st-util"
},
diff --git a/client/Client.cpp b/client/Client.cpp
new file mode 100644
index 0000000..46952b6
--- /dev/null
+++ b/client/Client.cpp
@@ -0,0 +1,65 @@
+#include "Client.h"
+
+
+
+Client::Client(QObject *parent) : QObject(parent)
+{
+ // initislise timer and socket
+ socket = new QTcpSocket(this);
+ timer = new QTimer(this);
+}
+Client::~Client()
+{
+ // delete if called again
+ delete socket;
+ delete timer;
+}
+
+void Client::ClientEcho()
+{
+ QTime time1 = QTime::currentTime();
+ NextMinute = time1.minute()+1;
+
+ connect(timer, SIGNAL(timeout()),this,SLOT(timeFunction())); // connect timer to time every minute
+
+ // connect to readyread to receive data;
+ connect(socket,&QTcpSocket::readyRead, [&]() {
+ QTextStream T(socket);
+ QString text = T.readAll(); // reads all data
+ Handlemsg.ParseToSQL(Handlemsg.ParseMessage(text, (totalRecords-'0')));
+
+
+ });
+
+ timer->start(1000);
+}
+
+void Client::timeFunction()
+{
+ if(_missingRecords>1){
+ totalRecords = _missingRecords;
+ }
+ else{
+ totalRecords=1;
+ }
+ QByteArray msgToSend= (msg.toUtf8() + totalRecords + offsetRecords +'\n');
+
+ QTime time = QTime::currentTime();
+ qint16 currentMinute = time.minute();
+
+ if(currentMinute==NextMinute){
+ socket->connectToHost(networkAddress, tcpPortAddress);
+
+ socket->write(msgToSend);
+ NextMinute++;
+ }
+}
+
+void Client::missingRecords()
+{
+ QSqlQuery queryTimeData;
+ queryTimeData.exec("SELECT (unix_timestamp(now()) - unix_timestamp(`time`))/60 as delta FROM `WSdb`.`tblMain` limit 1");
+
+ _missingRecords = queryTimeData.value(0).toInt();
+
+}
diff --git a/client/Client.h b/client/Client.h
new file mode 100644
index 0000000..10af3e1
--- /dev/null
+++ b/client/Client.h
@@ -0,0 +1,45 @@
+#ifndef CLIENT_H
+#define CLIENT_H
+#include <QTcpSocket>
+#include <QTextStream>
+#include <QTimer>
+#include <QDateTime>
+#include <QSqlQuery>
+
+#include "HandleMessage.h"
+
+// class client for wheather station
+class Client : public QObject
+{
+ Q_OBJECT
+public:
+ Client(QObject *parent = 0);
+ virtual ~Client();
+
+public slots:
+ void ClientEcho(); // function to ask data from wheather station
+ void timeFunction(); // function to look every second what time currently is en handle if minute is passed
+
+private:
+ void missingRecords();
+
+ int _missingRecords;
+ QTcpSocket *socket; // tcpsocket for communicating
+ QTimer *timer; // timer to read every second what time it curruntly is.
+
+ qint16 NextMinute; // timing for next minute
+ // qint16 currentMinute; // timing for currentMinute
+ HandleMessage Handlemsg; // add HandleMessage to Client.h
+
+ int tcpPortAddress = 80; // port of communication via tcp
+ QString networkAddress = "192.168.137.76"; // network address for commincation via tcp
+
+ QString msg = "last-records "; // part of mesage to send to wheather staion
+ char totalRecords = '1'; // total records to ask wheather station
+ char offsetRecords = '0'; // offset from reqeusting records
+
+
+
+};
+
+#endif // CLIENT_H
diff --git a/client/HandleMessage.cpp b/client/HandleMessage.cpp
new file mode 100644
index 0000000..aa73828
--- /dev/null
+++ b/client/HandleMessage.cpp
@@ -0,0 +1,47 @@
+#include "HandleMessage.h"
+
+
+HandleMessage::HandleMessage(QObject *parent) : QObject(parent)
+{
+
+}
+
+QString HandleMessage::ParseMessage(const QString Msg , int totalRecords )
+{
+ QString message= Msg.section('\n',2,(3+totalRecords));
+
+ return message;
+
+}
+
+void HandleMessage::ParseToSQL(QString input)
+{
+ QSqlQuery queryInsertData;
+ QString output = "INSERT INTO `WSdb`.`tblMain` () VALUES ";
+ QStringList data;
+ QStringList list = input.split("\n");
+ for (int i = 0; i < list.size(); ++i) {
+
+ output += "(";
+
+ data=list[i].split(",");
+
+ for (int j = 1; j < data.size(); ++j) {
+ bool valid;
+ output.append(QString::number(data[j].toInt(&valid, 16)));
+ if (j <= data[j].size()) {
+ output.append(",");
+ }
+
+ }
+ output.append(")");
+
+ if (i+1 < list.size()){
+ output.append(",");
+ }
+ }
+ queryInsertData.exec(output);
+}
+
+
+
diff --git a/client/HandleMessage.h b/client/HandleMessage.h
new file mode 100644
index 0000000..bfc5063
--- /dev/null
+++ b/client/HandleMessage.h
@@ -0,0 +1,26 @@
+#ifndef HANDLEMESSAGE_H
+#define HANDLEMESSAGE_H
+
+#include <QDebug>
+#include <QObject>
+#include <QString>
+#include <QVector>
+#include <QSqlQuery>
+
+class HandleMessage : public QObject
+{
+ Q_OBJECT
+public:
+ HandleMessage(QObject *parent = 0);
+
+ QString ParseMessage(const QString , int);
+ void ParseToSQL(QString);
+
+
+
+private:
+
+};
+
+
+#endif // HANDLEMESSAGE_H
diff --git a/client/client.pro b/client/client.pro
index 71ab6d5..76f2b56 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -1,20 +1,18 @@
QT += core gui sql charts network
HEADERS += \
- csv_import.h \
+ Client.h \
+ HandleMessage.h \
dbconnector.h \
main.h \
- mainwindow.h \
- mytcpsocket.h \
- timetest.h
+ mainwindow.h
SOURCES += \
- csv_import.cpp \
+ Client.cpp \
+ HandleMessage.cpp \
dbconnector.cpp \
main.cpp \
- mainwindow.cpp \
- mytcpsocket.cpp \
- timetest.cpp
+ mainwindow.cpp
FORMS += \
diff --git a/client/client.pro.user b/client/client.pro.user
new file mode 100644
index 0000000..7ccbc7b
--- /dev/null
+++ b/client/client.pro.user
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 8.0.1, 2022-10-29T18:40:37. -->
+<qtcreator>
+ <data>
+ <variable>EnvironmentId</variable>
+ <value type="QByteArray">{aa240e53-c124-4cf0-84a8-30bfe8a2cf83}</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.ActiveTarget</variable>
+ <value type="qlonglong">0</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.EditorSettings</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+ <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+ <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+ <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+ <value type="QString" key="language">Cpp</value>
+ <valuemap type="QVariantMap" key="value">
+ <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+ </valuemap>
+ </valuemap>
+ <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+ <value type="QString" key="language">QmlJS</value>
+ <valuemap type="QVariantMap" key="value">
+ <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+ </valuemap>
+ </valuemap>
+ <value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
+ <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+ <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+ <value type="int" key="EditorConfiguration.IndentSize">4</value>
+ <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+ <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+ <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+ <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+ <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+ <value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
+ <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+ <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+ <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+ <value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
+ <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+ <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+ <value type="int" key="EditorConfiguration.TabSize">8</value>
+ <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+ <value type="bool" key="EditorConfiguration.UseIndenter">false</value>
+ <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+ <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+ <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+ <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+ <value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
+ <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+ <value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.PluginSettings</variable>
+ <valuemap type="QVariantMap">
+ <valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
+ <value type="bool" key="AutoTest.Framework.Boost">true</value>
+ <value type="bool" key="AutoTest.Framework.CTest">false</value>
+ <value type="bool" key="AutoTest.Framework.Catch">true</value>
+ <value type="bool" key="AutoTest.Framework.GTest">true</value>
+ <value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
+ <value type="bool" key="AutoTest.Framework.QtTest">true</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
+ <value type="int" key="AutoTest.RunAfterBuild">0</value>
+ <value type="bool" key="AutoTest.UseGlobal">true</value>
+ <valuemap type="QVariantMap" key="ClangTools">
+ <value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
+ <value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
+ <value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
+ <value type="int" key="ClangTools.ParallelJobs">4</value>
+ <valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
+ <valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
+ <valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
+ <value type="bool" key="ClangTools.UseGlobalSettings">true</value>
+ </valuemap>
+ </valuemap>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.Target.0</variable>
+ <valuemap type="QVariantMap">
+ <value type="QString" key="DeviceType">Desktop</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 6.4.0 MinGW 64-bit</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 6.4.0 MinGW 64-bit</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt6.640.win64_mingw_kit</value>
+ <value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
+ <value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+ <value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+ <value type="int" key="EnableQmlDebugging">0</value>
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">D:\Github2\avans-whether-station\build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Debug</value>
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">D:/Github2/avans-whether-station/build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Debug</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+ <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+ <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+ </valuemap>
+ <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+ <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">D:\Github2\avans-whether-station\build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Release</value>
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">D:/Github2/avans-whether-station/build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Release</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+ <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+ <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+ </valuemap>
+ <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+ <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+ <value type="int" key="QtQuickCompiler">0</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
+ <value type="int" key="EnableQmlDebugging">0</value>
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">D:\Github2\avans-whether-station\build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Profile</value>
+ <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">D:/Github2/avans-whether-station/build-client-Desktop_Qt_6_4_0_MinGW_64_bit-Profile</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+ <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+ <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+ </valuemap>
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+ <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+ </valuemap>
+ <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+ <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+ <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+ <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+ <value type="int" key="QtQuickCompiler">0</value>
+ <value type="int" key="SeparateDebugInfo">0</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+ <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+ <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+ </valuemap>
+ <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
+ <value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+ <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+ <value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
+ <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+ <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+ <valuelist type="QVariantList" key="CustomOutputParsers"/>
+ <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+ <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
+ <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
+ <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+ <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+ <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+ <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+ </valuemap>
+ <value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.TargetCount</variable>
+ <value type="qlonglong">1</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+ <value type="int">22</value>
+ </data>
+ <data>
+ <variable>Version</variable>
+ <value type="int">22</value>
+ </data>
+</qtcreator>
diff --git a/client/dialog.cpp b/client/dialog.cpp
new file mode 100644
index 0000000..58c3e72
--- /dev/null
+++ b/client/dialog.cpp
@@ -0,0 +1,14 @@
+#include "dialog.h"
+#include "ui_dialog.h"
+
+Dialog::Dialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::Dialog)
+{
+ ui->setupUi(this);
+}
+
+Dialog::~Dialog()
+{
+ delete ui;
+}
diff --git a/client/dialog.h b/client/dialog.h
new file mode 100644
index 0000000..17537d1
--- /dev/null
+++ b/client/dialog.h
@@ -0,0 +1,22 @@
+#ifndef DIALOG_H
+#define DIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class Dialog;
+}
+
+class Dialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit Dialog(QWidget *parent = nullptr);
+ ~Dialog();
+
+private:
+ Ui::Dialog *ui;
+};
+
+#endif // DIALOG_H
diff --git a/client/dialog.ui b/client/dialog.ui
new file mode 100644
index 0000000..9fbffd2
--- /dev/null
+++ b/client/dialog.ui
@@ -0,0 +1,18 @@
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget name="Dialog" class="QDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/client/main.cpp b/client/main.cpp
index 0a1c4e4..267248c 100644
--- a/client/main.cpp
+++ b/client/main.cpp
@@ -14,19 +14,12 @@
#include "mainwindow.h"
#include "main.h"
#include "ui_mainwindow.h"
-#include "mytcpsocket.h"
-#include "timetest.h"
-#include "csv_import.h"
-#include <QDebug>
QSqlDatabase dbRef = QSqlDatabase();
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
- TimeTest time;
- MyTcpSocket s;
- // s.doConnect();
MainWindow w;
dbRef = QSqlDatabase::addDatabase("QMYSQL");
diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp
index e1736e6..49fcc26 100644
--- a/client/mainwindow.cpp
+++ b/client/mainwindow.cpp
@@ -11,6 +11,7 @@ MainWindow::MainWindow(QWidget *parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
+ client.ClientEcho();
}
MainWindow::~MainWindow()
@@ -19,6 +20,11 @@ MainWindow::~MainWindow()
delete ui;
}
+void MainWindow::timeFunction()
+{
+ client.timeFunction();
+}
+
void MainWindow::on_actionConnection_triggered()
{
_dbConenctor = new dbConnector(this);
diff --git a/client/mainwindow.h b/client/mainwindow.h
index 25e22ec..6bcc329 100644
--- a/client/mainwindow.h
+++ b/client/mainwindow.h
@@ -13,6 +13,7 @@
#include <QWidgetSet>
#include "main.h"
+#include "Client.h"
QT_BEGIN_NAMESPACE
@@ -33,7 +34,7 @@ private slots:
// void on_actionAbout_triggered();
// void on_pushButton_clicked();
-
+ void timeFunction();
void on_actionConnection_triggered();
void on_actionRefresh_triggered();
@@ -42,7 +43,7 @@ private slots:
private:
Ui::MainWindow *ui;
-
+ Client client;
dbConnector *_dbConenctor;
QChart *_pChart;
diff --git a/client/mainwindow.ui b/client/mainwindow.ui
index a31ffdc..bbcdf9c 100644
--- a/client/mainwindow.ui
+++ b/client/mainwindow.ui
@@ -17,8 +17,8 @@
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
- <x>270</x>
- <y>190</y>
+ <x>260</x>
+ <y>240</y>
<width>181</width>
<height>51</height>
</rect>
@@ -34,7 +34,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
- <height>24</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuAbouy">
diff --git a/client/mytcpsocket.cpp b/client/mytcpsocket.cpp
deleted file mode 100644
index 92dd67a..0000000
--- a/client/mytcpsocket.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include <mytcpsocket.h>
-
-MyTcpSocket::MyTcpSocket(QObject *parent) :
- QObject(parent)
-{
-}
-
-void MyTcpSocket::doConnect()
-{
- socket = new QTcpSocket(this);
-
- connect(socket, SIGNAL(connected()),this, SLOT(connected()));
- connect(socket, SIGNAL(disconnected()),this, SLOT(disconnected()));
- // connect(socket, SIGNAL(bytesWritten(qint64)),this, SLOT(bytesWritten(qint64)));
- connect(socket, SIGNAL(readyRead()),this, SLOT(readyRead()));
- qDebug() << "connectig...";
-
- socket->connectToHost("192.168.137.141",80);
-
- if(!socket->waitForConnected(5000)){
- qDebug()<<"Error: "<< socket->errorString();
- }
-}
-
-void MyTcpSocket::connected(){
- qDebug() << "connected...";
-
- socket->write("Weerdata: Temp:?\r\n\r\n\r\n\r\n");
-
-}
-void MyTcpSocket::disconnected(){
- qDebug() << "disconnected...";
-
-}
-
-void MyTcpSocket::bytesWritten(qint64 bytes){
- qDebug() << bytes << "bytes written...";
-
-}
-void MyTcpSocket::readyRead(){
- qDebug() << "reading...";
-
- qDebug() << socket->readAll();
-}
diff --git a/client/mytcpsocket.h b/client/mytcpsocket.h
deleted file mode 100644
index 4a7e543..0000000
--- a/client/mytcpsocket.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef MYTCPSOCKET_H
-#define MYTCPSOCKET_H
-
-#include <QObject>
-#include <QTcpSocket>
-#include <QAbstractSocket>
-#include <QDebug>
-
-class MyTcpSocket : public QObject
-{
- Q_OBJECT
-public:
- explicit MyTcpSocket(QObject *parent = 0);
-
- void doConnect();
-signals:
-public slots:
- void connected();
- void disconnected();
- void bytesWritten(qint64 bytes);
- void readyRead();
-private:
- QTcpSocket *socket;
-
-
-
-
-
-};
-
-
-#endif // MYTCPSOCKET_H
diff --git a/client/settingsmenu.cpp b/client/settingsmenu.cpp
new file mode 100644
index 0000000..139c616
--- /dev/null
+++ b/client/settingsmenu.cpp
@@ -0,0 +1,47 @@
+#include "settingsmenu.h"
+//#include "ui_SettingsMenu.h"
+#include "main.h"
+
+#include "mainwindow.h"
+
+SettingsMenu::SettingsMenu(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::SettingsMenu)
+{
+ _dbip = "localhost";
+ ui->setupUi(this);
+}
+
+SettingsMenu::~SettingsMenu()
+{
+ delete ui;
+}
+
+void SettingsMenu::on_pushButton_cancel_clicked()
+{
+ SettingsMenu::~SettingsMenu();
+}
+
+void SettingsMenu::on_pushButton_login_clicked()
+{
+ _dbip = ui->lineEdit_adress->text();
+ _dbName = ui->lineEdit_database->text();
+ QString username = ui->lineEdit_username->text();
+ QString password = ui->lineEdit_password->text();
+
+ dbRef.setHostName(_dbip);
+ dbRef.setUserName(username);
+ dbRef.setPassword(password);
+ dbRef.setDatabaseName(_dbName);
+
+ if(dbRef.open()){
+ QMessageBox::information(this, "Connection", "GREAT SUCCES!");
+ SettingsMenu::~SettingsMenu();
+ } else {
+ QMessageBox::warning(this, "No connection", "Failed to connect");
+ }
+}
+
+
+
+
diff --git a/client/settingsmenu.h b/client/settingsmenu.h
new file mode 100644
index 0000000..881906d
--- /dev/null
+++ b/client/settingsmenu.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <QDialog>
+
+#include <QMessageBox>
+//#include <QtSql>
+//#include <QSqlDatabase>
+
+
+namespace Ui {
+class SettingsMenu;
+}
+
+class SettingsMenu : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsMenu(QWidget *parent = nullptr);
+ ~SettingsMenu();
+
+private slots:
+
+ void on_pushButton_cancel_clicked();
+
+ void on_pushButton_login_clicked();
+
+private:
+ Ui::SettingsMenu *ui;
+
+ QString _dbName = "";
+ QString _dbip = "";
+ QString _ESPip = "";
+};
diff --git a/client/settingsmenu.ui b/client/settingsmenu.ui
new file mode 100644
index 0000000..0de180f
--- /dev/null
+++ b/client/settingsmenu.ui
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsMenu</class>
+ <widget class="QDialog" name="SettingsMenu">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>362</width>
+ <height>273</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <widget class="QWidget" name="formLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>60</x>
+ <y>60</y>
+ <width>241</width>
+ <height>173</height>
+ </rect>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Adress</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="lineEdit_adress">
+ <property name="text">
+ <string>localhost</string>
+ </property>
+ <property name="placeholderText">
+ <string>Hostname/IP-Adress</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Database</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="lineEdit_database">
+ <property name="text">
+ <string>WSdb</string>
+ </property>
+ <property name="placeholderText">
+ <string>Database name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Username</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="lineEdit_username">
+ <property name="text">
+ <string>root</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ <property name="placeholderText">
+ <string>Username</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="lineEdit_password">
+ <property name="font">
+ <font>
+ <underline>false</underline>
+ <strikeout>false</strikeout>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ab12345!</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ <property name="placeholderText">
+ <string>Password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="connectLabel">
+ <property name="text">
+ <string>Connect</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="pushButton_login">
+ <property name="text">
+ <string>Login</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton_cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/client/timetest.cpp b/client/timetest.cpp
deleted file mode 100644
index 2e575f2..0000000
--- a/client/timetest.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "timetest.h"
-
-TimeTest::TimeTest(QObject *parent) : QObject(parent)
-{
- timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()),this,SLOT(myfunction()));
- timer->start(5000);
-}
-
-qint16 TimeTest::myfunction()
-{
- QTime time = QTime::currentTime();
- qint16 time_text = time.minute();
-
- return time_text;
-
-
-}
diff --git a/client/timetest.h b/client/timetest.h
deleted file mode 100644
index aa1d8a5..0000000
--- a/client/timetest.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef TIMETEST_H
-#define TIMETEST_H
-#include <QTimer>
-#include <QDebug>
-#include <QObject>
-#include <QDateTime>
-
-class TimeTest : public QObject
-{
- Q_OBJECT
-public:
- explicit TimeTest(QObject *parent = 0);
-
-signals:
-public slots:
- qint16 myfunction();
-
-private:
- QTimer *timer;
-
-
-
-
-};
-
-
-#endif // TIMETEST_H
diff --git a/client/ui_dbconnector.h b/client/ui_dbconnector.h
new file mode 100644
index 0000000..dec4d7b
--- /dev/null
+++ b/client/ui_dbconnector.h
@@ -0,0 +1,163 @@
+/********************************************************************************
+** Form generated from reading UI file 'dbconnector.ui'
+**
+** Created by: Qt User Interface Compiler version 5.15.6
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_DBCONNECTOR_H
+#define UI_DBCONNECTOR_H
+
+#include <QtCore/QVariant>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QDialog>
+#include <QtWidgets/QFormLayout>
+#include <QtWidgets/QFrame>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_dbConnector
+{
+public:
+ QWidget *formLayoutWidget;
+ QFormLayout *formLayout;
+ QLabel *label_3;
+ QLineEdit *lineEdit_adress;
+ QLabel *label_4;
+ QLineEdit *lineEdit_database;
+ QFrame *line;
+ QLabel *label;
+ QLineEdit *lineEdit_username;
+ QLabel *label_2;
+ QLineEdit *lineEdit_password;
+ QLabel *connectLabel;
+ QHBoxLayout *horizontalLayout;
+ QPushButton *pushButton_login;
+ QPushButton *pushButton_cancel;
+
+ void setupUi(QDialog *dbConnector)
+ {
+ if (dbConnector->objectName().isEmpty())
+ dbConnector->setObjectName(QString::fromUtf8("dbConnector"));
+ dbConnector->resize(362, 273);
+ formLayoutWidget = new QWidget(dbConnector);
+ formLayoutWidget->setObjectName(QString::fromUtf8("formLayoutWidget"));
+ formLayoutWidget->setGeometry(QRect(60, 60, 241, 173));
+ formLayout = new QFormLayout(formLayoutWidget);
+ formLayout->setObjectName(QString::fromUtf8("formLayout"));
+ formLayout->setContentsMargins(0, 0, 0, 0);
+ label_3 = new QLabel(formLayoutWidget);
+ label_3->setObjectName(QString::fromUtf8("label_3"));
+
+ formLayout->setWidget(0, QFormLayout::LabelRole, label_3);
+
+ lineEdit_adress = new QLineEdit(formLayoutWidget);
+ lineEdit_adress->setObjectName(QString::fromUtf8("lineEdit_adress"));
+
+ formLayout->setWidget(0, QFormLayout::FieldRole, lineEdit_adress);
+
+ label_4 = new QLabel(formLayoutWidget);
+ label_4->setObjectName(QString::fromUtf8("label_4"));
+
+ formLayout->setWidget(1, QFormLayout::LabelRole, label_4);
+
+ lineEdit_database = new QLineEdit(formLayoutWidget);
+ lineEdit_database->setObjectName(QString::fromUtf8("lineEdit_database"));
+
+ formLayout->setWidget(1, QFormLayout::FieldRole, lineEdit_database);
+
+ line = new QFrame(formLayoutWidget);
+ line->setObjectName(QString::fromUtf8("line"));
+ line->setFrameShape(QFrame::HLine);
+ line->setFrameShadow(QFrame::Sunken);
+
+ formLayout->setWidget(2, QFormLayout::SpanningRole, line);
+
+ label = new QLabel(formLayoutWidget);
+ label->setObjectName(QString::fromUtf8("label"));
+
+ formLayout->setWidget(3, QFormLayout::LabelRole, label);
+
+ lineEdit_username = new QLineEdit(formLayoutWidget);
+ lineEdit_username->setObjectName(QString::fromUtf8("lineEdit_username"));
+ lineEdit_username->setEchoMode(QLineEdit::PasswordEchoOnEdit);
+
+ formLayout->setWidget(3, QFormLayout::FieldRole, lineEdit_username);
+
+ label_2 = new QLabel(formLayoutWidget);
+ label_2->setObjectName(QString::fromUtf8("label_2"));
+
+ formLayout->setWidget(4, QFormLayout::LabelRole, label_2);
+
+ lineEdit_password = new QLineEdit(formLayoutWidget);
+ lineEdit_password->setObjectName(QString::fromUtf8("lineEdit_password"));
+ QFont font;
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setKerning(true);
+ lineEdit_password->setFont(font);
+ lineEdit_password->setEchoMode(QLineEdit::Password);
+
+ formLayout->setWidget(4, QFormLayout::FieldRole, lineEdit_password);
+
+ connectLabel = new QLabel(formLayoutWidget);
+ connectLabel->setObjectName(QString::fromUtf8("connectLabel"));
+
+ formLayout->setWidget(5, QFormLayout::LabelRole, connectLabel);
+
+ horizontalLayout = new QHBoxLayout();
+ horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
+ pushButton_login = new QPushButton(formLayoutWidget);
+ pushButton_login->setObjectName(QString::fromUtf8("pushButton_login"));
+
+ horizontalLayout->addWidget(pushButton_login);
+
+ pushButton_cancel = new QPushButton(formLayoutWidget);
+ pushButton_cancel->setObjectName(QString::fromUtf8("pushButton_cancel"));
+
+ horizontalLayout->addWidget(pushButton_cancel);
+
+
+ formLayout->setLayout(5, QFormLayout::FieldRole, horizontalLayout);
+
+
+ retranslateUi(dbConnector);
+
+ QMetaObject::connectSlotsByName(dbConnector);
+ } // setupUi
+
+ void retranslateUi(QDialog *dbConnector)
+ {
+ dbConnector->setWindowTitle(QCoreApplication::translate("dbConnector", "Dialog", nullptr));
+ label_3->setText(QCoreApplication::translate("dbConnector", "Adress", nullptr));
+ lineEdit_adress->setText(QCoreApplication::translate("dbConnector", "localhost", nullptr));
+ lineEdit_adress->setPlaceholderText(QCoreApplication::translate("dbConnector", "Hostname/IP-Adress", nullptr));
+ label_4->setText(QCoreApplication::translate("dbConnector", "Database", nullptr));
+ lineEdit_database->setText(QCoreApplication::translate("dbConnector", "WSdb", nullptr));
+ lineEdit_database->setPlaceholderText(QCoreApplication::translate("dbConnector", "Database name", nullptr));
+ label->setText(QCoreApplication::translate("dbConnector", "Username", nullptr));
+ lineEdit_username->setText(QCoreApplication::translate("dbConnector", "root", nullptr));
+ lineEdit_username->setPlaceholderText(QCoreApplication::translate("dbConnector", "Username", nullptr));
+ label_2->setText(QCoreApplication::translate("dbConnector", "Password", nullptr));
+ lineEdit_password->setText(QCoreApplication::translate("dbConnector", "Ab12345!", nullptr));
+ lineEdit_password->setPlaceholderText(QCoreApplication::translate("dbConnector", "Password", nullptr));
+ connectLabel->setText(QCoreApplication::translate("dbConnector", "Connect", nullptr));
+ pushButton_login->setText(QCoreApplication::translate("dbConnector", "Login", nullptr));
+ pushButton_cancel->setText(QCoreApplication::translate("dbConnector", "Cancel", nullptr));
+ } // retranslateUi
+
+};
+
+namespace Ui {
+ class dbConnector: public Ui_dbConnector {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_DBCONNECTOR_H
diff --git a/client/ui_mainwindow.h b/client/ui_mainwindow.h
new file mode 100644
index 0000000..69c476b
--- /dev/null
+++ b/client/ui_mainwindow.h
@@ -0,0 +1,110 @@
+/********************************************************************************
+** Form generated from reading UI file 'mainwindow.ui'
+**
+** Created by: Qt User Interface Compiler version 5.15.6
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_MAINWINDOW_H
+#define UI_MAINWINDOW_H
+
+#include <QtCore/QVariant>
+// #include <QtWidgets/QAction>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QMenu>
+#include <QtWidgets/QMenuBar>
+#include <QtWidgets/QStatusBar>
+#include <QtWidgets/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_MainWindow
+{
+public:
+ QAction *actionRefresh;
+ QAction *actionLOAD;
+ QAction *actionQuerry;
+ QAction *actionConnection;
+ QAction *actionDisconnenct;
+ QWidget *centralwidget;
+ QLabel *label;
+ QMenuBar *menubar;
+ QMenu *menuAbouy;
+ QMenu *menuDatabase;
+ QStatusBar *statusbar;
+
+ void setupUi(QMainWindow *MainWindow)
+ {
+ if (MainWindow->objectName().isEmpty())
+ MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
+ MainWindow->resize(800, 600);
+ actionRefresh = new QAction(MainWindow);
+ actionRefresh->setObjectName(QString::fromUtf8("actionRefresh"));
+ actionLOAD = new QAction(MainWindow);
+ actionLOAD->setObjectName(QString::fromUtf8("actionLOAD"));
+ actionQuerry = new QAction(MainWindow);
+ actionQuerry->setObjectName(QString::fromUtf8("actionQuerry"));
+ actionConnection = new QAction(MainWindow);
+ actionConnection->setObjectName(QString::fromUtf8("actionConnection"));
+ actionDisconnenct = new QAction(MainWindow);
+ actionDisconnenct->setObjectName(QString::fromUtf8("actionDisconnenct"));
+ centralwidget = new QWidget(MainWindow);
+ centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
+ label = new QLabel(centralwidget);
+ label->setObjectName(QString::fromUtf8("label"));
+ label->setGeometry(QRect(270, 190, 181, 51));
+ MainWindow->setCentralWidget(centralwidget);
+ menubar = new QMenuBar(MainWindow);
+ menubar->setObjectName(QString::fromUtf8("menubar"));
+ menubar->setGeometry(QRect(0, 0, 800, 24));
+ menuAbouy = new QMenu(menubar);
+ menuAbouy->setObjectName(QString::fromUtf8("menuAbouy"));
+ menuDatabase = new QMenu(menubar);
+ menuDatabase->setObjectName(QString::fromUtf8("menuDatabase"));
+ MainWindow->setMenuBar(menubar);
+ statusbar = new QStatusBar(MainWindow);
+ statusbar->setObjectName(QString::fromUtf8("statusbar"));
+ MainWindow->setStatusBar(statusbar);
+
+ menubar->addAction(menuAbouy->menuAction());
+ menubar->addAction(menuDatabase->menuAction());
+ menuAbouy->addAction(actionRefresh);
+ menuDatabase->addAction(actionConnection);
+ menuDatabase->addAction(actionDisconnenct);
+
+ retranslateUi(MainWindow);
+
+ QMetaObject::connectSlotsByName(MainWindow);
+ } // setupUi
+
+ void retranslateUi(QMainWindow *MainWindow)
+ {
+ MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
+ actionRefresh->setText(QCoreApplication::translate("MainWindow", "Refresh", nullptr));
+#if QT_CONFIG(shortcut)
+ actionRefresh->setShortcut(QCoreApplication::translate("MainWindow", "Ctrl+R", nullptr));
+#endif // QT_CONFIG(shortcut)
+ actionLOAD->setText(QCoreApplication::translate("MainWindow", "Load", nullptr));
+ actionQuerry->setText(QCoreApplication::translate("MainWindow", "Query", nullptr));
+ actionConnection->setText(QCoreApplication::translate("MainWindow", "Connect", nullptr));
+#if QT_CONFIG(shortcut)
+ actionConnection->setShortcut(QCoreApplication::translate("MainWindow", "Ctrl+O", nullptr));
+#endif // QT_CONFIG(shortcut)
+ actionDisconnenct->setText(QCoreApplication::translate("MainWindow", "Disconnenct", nullptr));
+ label->setText(QCoreApplication::translate("MainWindow", "Please load data first", nullptr));
+ menuAbouy->setTitle(QCoreApplication::translate("MainWindow", "Home", nullptr));
+ menuDatabase->setTitle(QCoreApplication::translate("MainWindow", "Database", nullptr));
+ } // retranslateUi
+
+};
+
+namespace Ui {
+ class MainWindow: public Ui_MainWindow {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_MAINWINDOW_H
diff --git a/readme.md b/readme.md
index 1b42659..8068030 100644
--- a/readme.md
+++ b/readme.md
@@ -6,8 +6,10 @@
`client/client.pro` should be tracked under version control, not
`client/makefile`
- the stm32 makefile uses git submodules to pull necessary files for
- compilation. make sure to initialize and sync the git submodules, or re-clone
- using the `--recursive` flag.
+ compilation. make sure to initialize and sync the git submodules, or use the
+ `--recursive` flag when cloning
+- the stm32 firmware expects the esp8266 module to have the official espressif
+ firmware, not the ai-thinker firmware that the module comes pre-flashed with
## support
diff --git a/scripts/compiledb-full-path-mingw.sh b/scripts/compiledb-full-path-mingw.sh
index 8f95756..be45ca7 100755
--- a/scripts/compiledb-full-path-mingw.sh
+++ b/scripts/compiledb-full-path-mingw.sh
@@ -10,4 +10,4 @@ fixpath () {
fixpath arm-none-eabi-gcc
fixpath arm-none-eabi-objcopy
-sed "s#\"/c/#\"C:/#g" -i "$COMPILEDB_FILE"
+sed 's#"/\(.\)/#"\U\1:/#g' -i "$COMPILEDB_FILE" \ No newline at end of file
diff --git a/scripts/db-init.sql b/scripts/db-init.sql
index 4c4c4d4..2b0581c 100644
--- a/scripts/db-init.sql
+++ b/scripts/db-init.sql
@@ -1,9 +1,11 @@
-CREATE SCHEMA `WSdb`;
-CREATE TABLE `DAB1Pract1`.`tblMain` (
- `ID` INT GENERATED ALWAYS AS (),
- `temperature` DECIMAL(5,2) NULL,
- `humidity` DECIMAL(5,2) NULL,
- `pressure` DECIMAL(5,2) NULL,
- `time` DATETIME NULL,
- PRIMARY KEY (`ID`),
- UNIQUE INDEX `ID_UNIQUE` (`ID` ASC) VISIBLE);
+drop schema if exists `WSdb`;
+create schema if not exists `WSdb`;
+drop table if exists `WSdb`.`tblMain`;
+create table if not exists `WSdb`.`tblMain` (
+ id int unsigned not null auto_increment,
+ temperature decimal(5,2) null,
+ humidity decimal(5,2) null,
+ pressure decimal(5,2) null,
+ time datetime null,
+ primary key (id)
+);
diff --git a/scripts/db-privileges.sql b/scripts/db-privileges.sql
new file mode 100644
index 0000000..75f8fcc
--- /dev/null
+++ b/scripts/db-privileges.sql
@@ -0,0 +1 @@
+grant all privileges on WSdb.* to 'user'@'localhost';
diff --git a/scripts/dummy-server.py b/scripts/dummy-server.py
new file mode 100755
index 0000000..9350cab
--- /dev/null
+++ b/scripts/dummy-server.py
@@ -0,0 +1,48 @@
+#!/bin/python3
+
+"""
+this is a garbage python script that opens a tcp socket on localhost:33 for
+connecting with the qt client
+"""
+
+import socketserver
+from random import randint
+
+def hexpad(n, pad):
+ return hex(n)[2:].zfill(pad)
+
+def bs(str):
+ return bytes(str, 'utf-8')
+
+def r(max):
+ return randint(0, max)
+
+class DummyServer(socketserver.BaseRequestHandler):
+ def error(self):
+ self.request.sendall(bs("error\n"))
+
+ def ok(self, rows):
+ response = "id,temperature,humidity,atmospheric_pressure\n"
+ line_len = len("xxxx,xx,xx,xx\n")
+ retstr = f"ok,{hex(len(response) + rows * line_len)[2:]}\n"
+ retstr += response
+ for row in range(rows):
+ retstr += f"{hexpad(row, 4)},{hexpad(r(0xff), 2)},{hexpad(r(0xff), 2)},{hexpad(r(0xff), 2)}\n"
+ self.request.sendall(bs(retstr))
+
+ def handle(self):
+ self.data = self.request.recv(1024).strip()
+ if len(self.data) > 40: return self.error()
+ if not self.data.startswith(bs('last-records')): return self.error()
+ self.data = self.data.replace(bs('last-records'), bs(''))
+ try:
+ self.ok(int(self.data))
+ except ValueError:
+ self.error()
+
+if __name__ == "__main__":
+ socketserver.TCPServer.allow_reuse_address = True
+ with socketserver.TCPServer(("localhost", 33), DummyServer) as server:
+ server.serve_forever()
+
+
diff --git a/shared/bin.c b/shared/bin.c
new file mode 100644
index 0000000..def2aa8
--- /dev/null
+++ b/shared/bin.c
@@ -0,0 +1,11 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <memory.h>
+
+#include "bin.h"
+
+ws_s_bin *ws_bin_s_alloc(uint16_t bytes) {
+ ws_s_bin *temp = malloc(sizeof(ws_s_bin) + sizeof(uint8_t) * bytes);
+ temp->bytes = bytes;
+ return temp;
+}
diff --git a/shared/bin.h b/shared/bin.h
new file mode 100644
index 0000000..bfcda0c
--- /dev/null
+++ b/shared/bin.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdint.h>
+
+/** @brief binary data container with length */
+typedef struct {
+ uint16_t bytes;
+ uint8_t data[];
+} ws_s_bin;
+
+/** @brief allocate new ws_s_bin struct */
+ws_s_bin *ws_bin_s_alloc(uint16_t bytes);
+
diff --git a/shared/protocol.c b/shared/protocol.c
new file mode 100644
index 0000000..8887070
--- /dev/null
+++ b/shared/protocol.c
@@ -0,0 +1,147 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "protocol.h"
+#include "util.h"
+
+#define WS_CMD_MAP(parsed_cmd, name, code) \
+ if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code;
+
+static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_cmd* parsed_cmd) {
+ if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command
+ WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS);
+
+ return WS_PROTOCOL_CMD_UNKNOWN;
+}
+
+void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input) {
+ switch(input) {
+ case WS_PROTOCOL_C_EOL: {
+ break;
+ }
+
+ case WS_PROTOCOL_C_SPACE: {
+ if (!state->valid) return;
+ state->arg_len++;
+ return;
+ }
+
+ case WS_PROTOCOL_C_NULL: {
+ state->valid = false;
+ return;
+ }
+
+ default: {
+ if (!state->valid) return;
+ state->cmd[state->cmd_len++] = input;
+ state->args_len[state->arg_len] += 1;
+ if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false;
+ return;
+ }
+ }
+ // arg_len is used as an index while parsing, so add 1 to get length
+ state->arg_len++;
+
+ // parse cmd into argc and argv
+ if (state->valid) ws_protocol_req_cmd_init(state);
+ // create response
+ ws_s_protocol_res* response = ws_protocol_parse_req_finished(state->target);
+
+ // send response
+ char response_first_line[16];
+ sprintf(response_first_line, "%s,%x\n", response->success == WS_PROTOCOL_CMD_RETURN_OK ? "ok" : "error", response->msg->bytes);
+ ws_protocol_send_data(response_first_line, strlen(response_first_line));
+ if (!response->csh) ws_protocol_send_data((char*) response->msg->data, response->msg->bytes);
+ else (*g_ws_protocol_res_handlers[response->cmd_code])(state->target, response, true);
+
+ // free response data containers
+ free(response->msg);
+ free(response);
+
+ // reset parser
+ ws_protocol_req_parser_reset(state);
+
+ return;
+}
+
+ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd) {
+ ws_s_protocol_res* response = malloc(sizeof(ws_s_protocol_res));
+ response->success = WS_PROTOCOL_CMD_RETURN_ERROR;
+ response->csh = false;
+ response->msg = NULL;
+ response->cmd_code = ws_protocol_get_req_cmd_code(parsed_cmd);
+
+ if (response->cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit;
+ if (response->cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit;
+
+ ws_protocol_res_handler_t* ws_protocol_res_handler = g_ws_protocol_res_handlers[response->cmd_code];
+ if (ws_protocol_res_handler == NULL) goto ws_protocol_parse_exit;
+ (*ws_protocol_res_handler)(parsed_cmd, response, false);
+
+ws_protocol_parse_exit:
+
+ if (response->msg == NULL) response->msg = ws_bin_s_alloc(0);
+ return response;
+}
+
+void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length) {
+ for (unsigned int i = 0; i < length; i++) ws_protocol_parse_req_byte(state, input[i]);
+}
+
+ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() {
+ ws_s_protocol_req_parser_state* parser_state = malloc(sizeof(ws_s_protocol_req_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS);
+ parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN);
+ parser_state->target = NULL;
+ ws_protocol_req_parser_reset(parser_state);
+ return parser_state;
+}
+
+void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) {
+ state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len);
+ unsigned int args = WS_MIN(state->arg_len, WS_PROTOCOL_CMD_MAX_ARGUMENTS);
+ for (unsigned int i = 0; i < args; i++)
+ state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1));
+
+ state->target->argc = args;
+
+ unsigned int head = 0;
+ for (unsigned int i = 0; i < state->arg_len; i++) {
+ strncpy(state->target->argv[i], &state->cmd[head], state->args_len[i]);
+ state->target->argv[i][state->args_len[i]] = 0x00; // terminate argument with null byte
+ head += state->args_len[i];
+ }
+}
+
+void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state) {
+ if (state == NULL) return;
+ if (state->target != NULL) ws_protocol_req_cmd_free(state->target);
+ state->target = NULL;
+ free(state->cmd);
+ free(state);
+ return;
+}
+
+void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state) {
+ if (state->target != NULL) ws_protocol_req_cmd_free(state->target);
+ state->target = NULL;
+ state->valid = true;
+ state->cmd_len = 0;
+ state->arg_len = 0;
+ memset(state->args_len, 0, sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS);
+}
+
+void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd) {
+ for (int i = 0; i < cmd->argc; i++)
+ free(cmd->argv[i]);
+ free(cmd);
+ return;
+}
+
+unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response) {
+ unsigned short size = 2; // comma and trailing newline
+ if (response->success == WS_PROTOCOL_CMD_RETURN_OK) size += 2; // ok
+ if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) size += 5; // error
+ size += ws_log16(response->msg->bytes) + 1; // amount of characters for message size (hex)
+ return size;
+} \ No newline at end of file
diff --git a/shared/protocol.h b/shared/protocol.h
new file mode 100644
index 0000000..96c039a
--- /dev/null
+++ b/shared/protocol.h
@@ -0,0 +1,151 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "bin.h"
+
+#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (3)
+#define WS_PROTOCOL_CMD_BUFFER_LEN (40)
+
+#define WS_PROTOCOL_CMD_AMOUNT (1)
+
+#define WS_PROTOCOL_C_EOL (0x0a)
+#define WS_PROTOCOL_C_SPACE (0x20)
+#define WS_PROTOCOL_C_NULL (0x00)
+
+/**
+ * @brief parsed request cmd struct, holds arguments similar to argc and argv
+ * provided to `int main()`
+ */
+typedef struct {
+ int argc; /** argument count */
+ char* argv[]; /** argument array, null terminated strings */
+} ws_s_protocol_parsed_req_cmd;
+
+/**
+ * @brief holds parser state variables for `ws_protocol_parse_req_byte` function.
+ * each incoming tcp request should get it's own parser 'instance'
+ */
+typedef struct {
+ ws_s_protocol_parsed_req_cmd* target; /** parsed cmd reference */
+ bool valid; /** command still valid flag */
+ char* cmd; /** raw cmd */
+ uint16_t cmd_len; /** raw cmd string length */
+ uint16_t arg_len; /** amount of arguments */
+ uint16_t args_len[]; /** array of argument lengths */
+} ws_s_protocol_req_parser_state;
+
+/** @brief return values for command handlers */
+typedef enum {
+ WS_PROTOCOL_CMD_RETURN_OK = 0,
+ WS_PROTOCOL_CMD_RETURN_ERROR = 1,
+} ws_e_protocol_cmd_return_value;
+
+/** @brief cmd codes (used to call handlers) */
+typedef enum {
+ WS_PROTOCOL_CMD_UNKNOWN = -1,
+
+ WS_PROTOCOL_CMD_LAST_RECORDS = 0,
+} ws_e_protocol_cmd;
+
+/** @brief request response data struct */
+typedef struct {
+ ws_e_protocol_cmd_return_value success; /** status code for response
+ validity, defaults to
+ WS_PROTOCOL_CMD_RETURN_ERROR */
+ bool csh; /** whether the response handler has logic for a custom send
+ handler, false by default */
+ ws_s_bin* msg; /** pointer to response data, uninitialized by default */
+ ws_e_protocol_cmd cmd_code; /** cmd code */
+} ws_s_protocol_res;
+
+/**
+ * @brief allocate parser struct
+ *
+ * @return pointer to newly allocated struct
+ */
+ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc();
+/** @brief deallocate parser struct, automatically frees all child pointers */
+void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state);
+/** @brief reset parser state to parse a new request */
+void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state);
+/**
+ * @brief initialize ws_s_protocol_parsed_req_cmd struct pointer of
+ * ws_s_protocol_req_parser_state (internal only)
+ */
+void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state);
+/** @brief deallocate ws_s_protocol_parsed_req_cmd struct pointer (internal only) */
+void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd);
+
+/**
+ * @brief parse incoming data byte by byte until a finished command is detected
+ *
+ * @param state parser state object, each incoming request should have it's own parser state
+ * @param input input byte
+ */
+void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input);
+/**
+ * @brief parse incoming data chunk
+ *
+ * @param state parser state object, each incoming request should have it's own parser state
+ * @param input input byte array
+ * @param length input byte array length
+ */
+void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length);
+/**
+ * @brief handle complete command
+ *
+ * this function gets called when ws_protocol_parse_req_byte(s) has detected a
+ * finished command. this function decides which command handler gets called,
+ * given that argv[0] contains a valid command. command argument parsing is
+ * handled by the command handler function.
+ *
+ * @return response
+ *
+ * @param parsed_cmd cmd parsed into ws_s_protocol_parsed_req_cmd struct
+ */
+ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd);
+
+/**
+ * @brief create a `last-records` request command
+ * @return ws_s_bin containing the command string
+ */
+ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount);
+
+/**
+ * @brief response handler
+ *
+ * gets fired when the weather station receives a complete command, and returns
+ * a response struct with a success code and an optional message. if
+ * response->csh is set to `true` within the handler, it gets fired a second
+ * time after the response header is sent, but with the `send` parameter set to
+ * `true`. this is so response handlers can send large amounts of data without
+ * allocating large areas of memory.
+ *
+ * @param parsed_cmd complete parsed command from ws_protocol_parse_req_*
+ * @param response response struct with uninitialized pointer to msg
+ * @param send `false` on first run, `true` on second run if `response->csh` was set to true
+ */
+typedef void ws_protocol_res_handler_t(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*, bool);
+
+ws_protocol_res_handler_t ws_protocol_res_last_records;
+
+/**
+ * @brief data sender wrapper
+ *
+ * this function should be implemented in the source files of each target
+ * platform, as the send interface will be different on desktop and on the
+ * stm32.
+ *
+ * @param data pointer to data char array
+ * @param length length of data array
+ */
+void ws_protocol_send_data(const char* data, unsigned int length);
+
+/** @brief response handlers, called when a command is parsed */
+static ws_protocol_res_handler_t* g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT] = {
+ [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records,
+};
+
+unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response);
diff --git a/shared/protocol.md b/shared/protocol.md
new file mode 100644
index 0000000..1e52e42
--- /dev/null
+++ b/shared/protocol.md
@@ -0,0 +1,57 @@
+# Protocol spec
+
+This is a brief overview of the protocol specifications that the weather
+station uses to send and receive data between the weather station and qt
+client. This protocol is text-based, and used over a TCP connection. This
+document will only go into detail about the data sent over this connection, not
+requirements about the connection itself.
+
+The protocol is only used in a request-response fashion, so all commands are
+assumed to be sent by the qt client, and responded to by the weather station.
+
+~Functions for generating commands and parsing incoming data are provided by
+the protocol.c and protocol.h files in this folder.~ A server using these files
+should implement every protocol handler function in a seperate c file, along
+with a data sending function that is also used internally.
+
+- LF for newline instead of CRLF
+- Commands are single-line
+- Spaces used for separating command arguments
+- Commands with malformed data are discarded and return error
+- Response consist of `ok` or `error`, a comma, and the byte length of the
+ remaining response (if any)
+- Numbers are sent as hexadecimal
+
+## Commands
+
+### `last-records <n> <o>`
+
+Returns the last `n` records with offset `<o>` in csv format. The first line
+has the csv table header, with the fields `id`, `temperature`, `humidity`, and
+`atmospheric_pressure`. The rest of the response consists of 1 record per line.
+The amount of records is limited to the amount of valid records in the backlog
+buffer. When the amount of returned records is 0, the response consists of the
+csv header, but without any following records.
+
+Offset `<o>` is a positive integer, representing the starting point for the
+most recent record that is returned, this will get subtracted from the id of
+the most recent record. E.g. if the last record has id `00f0`, and a request is
+sent with parameters `n=3` and `o=5`, the records with id's `00eb`, `00ea`, and
+`00e9` will be returned.
+
+## Example transaction
+
+In the following example, newlines are indicated by `<0a>`, request by lines
+starting with `<`, and response by lines starting with `>`.
+
+```
+< last-records 5 0<0a>
+> ok,73<0a>
+> id,temperature,humidity,atmospheric_pressure<0a>
+> 10dc,2f,c5,7f<0a>
+> 10dd,30,c6,7f<0a>
+> 10de,31,c7,7f<0a>
+> 10df,35,ca,7e<0a>
+> 10e0,34,c9,7e<0a>
+```
+
diff --git a/shared/shared.mk b/shared/shared.mk
new file mode 100644
index 0000000..f9586ff
--- /dev/null
+++ b/shared/shared.mk
@@ -0,0 +1 @@
+OBJS += $(patsubst %.c,%-stm.o, $(wildcard ../shared/*.c))
diff --git a/shared/util.c b/shared/util.c
new file mode 100644
index 0000000..ea972b0
--- /dev/null
+++ b/shared/util.c
@@ -0,0 +1,7 @@
+#include "util.h"
+
+unsigned int ws_log16(unsigned int x) {
+ unsigned int l = 0;
+ while (x >>= 4) ++l; // bitshift right by 4 until x == 0
+ return l;
+} \ No newline at end of file
diff --git a/shared/util.h b/shared/util.h
new file mode 100644
index 0000000..94a3dfe
--- /dev/null
+++ b/shared/util.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#define WS_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define WS_MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+unsigned int ws_log16(unsigned int x);
diff --git a/stm32f091/backlog.c b/stm32f091/backlog.c
index 3f21924..662fc75 100644
--- a/stm32f091/backlog.c
+++ b/stm32f091/backlog.c
@@ -2,33 +2,41 @@
#include "backlog.h"
-ws_s_backlog_database* WS_G_BACKLOG_DATABASE = NULL;
+ws_s_backlog_database* g_ws_backlog_database = NULL;
void ws_backlog_alloc(uint16_t record_amt) {
- WS_G_BACKLOG_DATABASE = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt);
- WS_G_BACKLOG_DATABASE->buffer_size = record_amt;
- WS_G_BACKLOG_DATABASE->buffer_start = 0;
- WS_G_BACKLOG_DATABASE->buffer_end = 0;
+ g_ws_backlog_database = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt);
+ g_ws_backlog_database->buffer_size = record_amt;
+ g_ws_backlog_database->buffer_start = 0;
+ g_ws_backlog_database->buffer_end = 0;
}
void ws_backlog_add_record(ws_s_backlog_record record) {
static uint16_t id = 0;
- WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].id = id++;
- WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_atm_pressure = record.sens_atm_pressure;
- WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_humidity = record.sens_humidity;
- WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_temperature = record.sens_temperature;
+ g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].id = id++;
+ g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_atm_pressure = record.sens_atm_pressure;
+ g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_humidity = record.sens_humidity;
+ g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_temperature = record.sens_temperature;
// shift buffer start/end
- WS_G_BACKLOG_DATABASE->buffer_end = (WS_G_BACKLOG_DATABASE->buffer_end + 1) % WS_G_BACKLOG_DATABASE->buffer_size;
- if (WS_G_BACKLOG_DATABASE->buffer_end == WS_G_BACKLOG_DATABASE->buffer_start)
- WS_G_BACKLOG_DATABASE->buffer_start = (WS_G_BACKLOG_DATABASE->buffer_start + 1) % WS_G_BACKLOG_DATABASE->buffer_size;
+ g_ws_backlog_database->buffer_end = (g_ws_backlog_database->buffer_end + 1) % g_ws_backlog_database->buffer_size;
+ if (g_ws_backlog_database->buffer_end == g_ws_backlog_database->buffer_start)
+ g_ws_backlog_database->buffer_start = (g_ws_backlog_database->buffer_start + 1) % g_ws_backlog_database->buffer_size;
}
ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index) {
- return &WS_G_BACKLOG_DATABASE->records[record_index];
+ return &g_ws_backlog_database->records[record_index];
}
ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset) {
- return ws_backlog_get_record((WS_G_BACKLOG_DATABASE->buffer_end - record_offset - 1) % WS_G_BACKLOG_DATABASE->buffer_size);
+ return ws_backlog_get_record((g_ws_backlog_database->buffer_end - record_offset - 1) % g_ws_backlog_database->buffer_size);
+}
+
+uint16_t ws_backlog_get_record_count() {
+ // add buffer_size to the result of the modulo operation if it's result is negative
+ // (only works when buffer_size is less than 2^15)
+ // this is a consequence of the way in which c handles negative numbers in modulo operations
+ int16_t mod = (g_ws_backlog_database->buffer_end - g_ws_backlog_database->buffer_start) % g_ws_backlog_database->buffer_size;
+ return mod < 0 ? mod + g_ws_backlog_database->buffer_size : mod;
}
diff --git a/stm32f091/backlog.h b/stm32f091/backlog.h
index 465b3c0..c8ea019 100644
--- a/stm32f091/backlog.h
+++ b/stm32f091/backlog.h
@@ -30,7 +30,7 @@ typedef struct {
#pragma pack(pop)
/** @brief global record backlog database pointer */
-extern ws_s_backlog_database* WS_G_BACKLOG_DATABASE;
+extern ws_s_backlog_database* g_ws_backlog_database;
/**
* @brief add record to database
@@ -55,3 +55,5 @@ ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index);
/** @brief get pointer to last record with offset `record_offset` from the database */
ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset);
+/** @brief return amount of valid records in database */
+uint16_t ws_backlog_get_record_count();
diff --git a/stm32f091/consts.h b/stm32f091/consts.h
new file mode 100644
index 0000000..3d2ef5a
--- /dev/null
+++ b/stm32f091/consts.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "wifi.h"
+
+#define WS_SERVER_PORT "80"
+#define WS_SERVER_MAX_CHANNELS 4
+
+#define WS_DMA_RX_BUFFER_SIZE 100
+#define WS_DMA_TX_BUFFER_SIZE 1024
+
+#define WS_PINOUT_I2C_SDA_PIN GPIO_PIN_9
+#define WS_PINOUT_I2C_SDA_PORT GPIOB
+#define WS_PINOUT_I2C_SCL_PIN GPIO_PIN_8
+#define WS_PINOUT_I2C_SCL_PORT GPIOB
+
+#define WS_PINOUT_USART1_RX_PIN GPIO_PIN_10
+#define WS_PINOUT_USART1_RX_PORT GPIOA
+#define WS_PINOUT_USART1_TX_PIN GPIO_PIN_9
+#define WS_PINOUT_USART1_TX_PORT GPIOA
+
+#define WS_PINOUT_USART2_RX_PIN GPIO_PIN_3
+#define WS_PINOUT_USART2_RX_PORT GPIOA
+#define WS_PINOUT_USART2_TX_PIN GPIO_PIN_2
+#define WS_PINOUT_USART2_TX_PORT GPIOA
+
+// print esp communication over usb serial (green for tx, red for rx)
+#define WS_DBG_PRINT_ESP_OVER_USART2
diff --git a/stm32f091/esp8266.c b/stm32f091/esp8266.c
index 6f12191..74ec347 100644
--- a/stm32f091/esp8266.c
+++ b/stm32f091/esp8266.c
@@ -2,208 +2,88 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stm32f0xx.h>
#include "esp8266.h"
#include "setup.h"
-
-void ws_esp8266_ATsendCommand(uint8_t* data){
- char dataChar[20];
- uint8_t Tx_send[]="AT+CIPSEND=0,";
-
- itoa(strlen((char*)data),dataChar,10);
- strcat((char*)Tx_send,dataChar);
- strcat((char*)Tx_send,"\r\n");
- HAL_UART_Transmit(&huart1, Tx_send, strlen((char*)Tx_send),1000);
- HAL_Delay(2000);
- HAL_UART_Transmit(&huart1, data, strlen((char*)data),1000);
- HAL_Delay(1000);
- HAL_UART_Transmit(&huart2, data, strlen((char*)data),1000);
- HAL_Delay(5000);
-}
-int ws_esp8266_checkOK(uint8_t *receiveData,int length){
- char *ret="";
- char *ret1="";
- HAL_UART_Transmit(&huart2, receiveData,length,1000);
- ret = strstr((char*)receiveData,"OK");
- // ret = strstr((char*)receiveData,"change");
- // memset(receiveData,0,30);
- if((ret[0]='O') && (ret[1]=='K')){
- //HAL_UART_Transmit(&huart2, (uint8_t*)ret, sizeof(ret), 100);
- return 1;
-
- }
-// else if((ret1[0]='c') && (ret1[1]=='h')){
-// //HAL_UART_Transmit(&huart2, (uint8_t*)ret, sizeof(ret), 100);
-// return 1;
-//
-// }
- else{
- return 0;
- }
-
+#include "consts.h"
+#include "server.h"
+#include "util.h"
+
+// macro for concise sending of multiple commands
+#define ws_esp8266_send_seq(cmd) { \
+ uint8_t _cmd[] = cmd; \
+ ws_server_send(_cmd, sizeof(_cmd)); \
}
-int ws_esp8266_receivingMsg(uint8_t *receiveData,int length){
- char *ret="";
- HAL_UART_Transmit(&huart2, receiveData,length,1000);
- ret = strstr((char*)receiveData,"+IPD");
- // memset(receiveData,0,30);
- if((ret[0]='+') && (ret[1]=='I')){
- //HAL_UART_Transmit(&huart2, (uint8_t*)ret, sizeof(ret), 100);
- return 1;
- }
- else{
- return 0;
- }
+uint8_t g_ws_esp8266_dma_rx_buffer[WS_DMA_RX_BUFFER_SIZE];
+unsigned int g_ws_esp8266_dma_rx_head = 0;
+unsigned int g_ws_esp8266_dma_rx_tail = 0;
+uint8_t g_ws_esp8266_dma_tx_buffer[WS_DMA_TX_BUFFER_SIZE];
-}
-int ws_esp8266_unlink(uint8_t *receiveData,int length){
- char *ret="";
- HAL_UART_Transmit(&huart2, receiveData,length,1000);
- ret = strstr((char*)receiveData,"UNLINK");
- // memset(receiveData,0,30);
- if((ret[0]='U') && (ret[1]=='N')){
- //HAL_UART_Transmit(&huart2, (uint8_t*)ret, sizeof(ret), 100);
- return 1;
-
- }
- else{
- return 0;
- }
+void DMA1_Ch1_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); }
+void DMA1_Ch2_3_DMA2_Ch1_2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_tx); }
+void USART1_IRQHandler(void) {
+ if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {
+ __HAL_UART_CLEAR_IDLEFLAG(&huart1);
+ // https://stackoverflow.com/questions/71039052/hal-uartex-rxeventcallback-circular-dma-what-address-is-the-data
+ g_ws_esp8266_dma_rx_head = huart1.RxXferSize - huart1.hdmarx->Instance->CNDTR;
+ ws_esp8266_incoming_data_chunk();
+ }
+ HAL_UART_IRQHandler(&huart1);
}
-void ws_esp8266_StartEsp(){
-
- uint8_t Tx_AT[]="AT\r\n";
- uint8_t Rx_buffer[10]={0};
- for(int i=0;i<3;i++){
- // HAL_UART_Transmit(&huart2, hier,sizeof(hier),100);
- HAL_UART_Transmit_IT(&huart1, Tx_AT,strlen((char*)Tx_AT));
- HAL_UART_Receive_IT(&huart1, Rx_buffer, 10);
-
-
- HAL_UART_Transmit(&huart2, Rx_buffer,10,100);
- HAL_Delay(5000);
- //memset(Rx_buffer,0,sizeof(Rx_buffer));
+void ws_esp8266_incoming_data_chunk() {
+ if (g_ws_esp8266_dma_rx_head == g_ws_esp8266_dma_rx_tail) return; // no new data
+ if (g_ws_esp8266_dma_rx_head > g_ws_esp8266_dma_rx_tail) {
+ // read from tail until head
+ ws_server_req_incoming(&g_ws_esp8266_dma_rx_buffer[g_ws_esp8266_dma_rx_tail],
+ g_ws_esp8266_dma_rx_head - g_ws_esp8266_dma_rx_tail);
+ } else /* if (g_ws_esp8266_dma_rx_head < g_ws_esp8266_dma_rx_tail) */ {
+ // read from tail until end of buffer
+ ws_server_req_incoming(&g_ws_esp8266_dma_rx_buffer[g_ws_esp8266_dma_rx_tail],
+ WS_DMA_RX_BUFFER_SIZE - g_ws_esp8266_dma_rx_tail);
+ // read from buffer begin until head
+ ws_server_req_incoming(&g_ws_esp8266_dma_rx_buffer[0], // yes i know this looks dumb
+ g_ws_esp8266_dma_rx_head);
}
-
+ // finish read by shifting tail forward
+ g_ws_esp8266_dma_rx_tail = g_ws_esp8266_dma_rx_head;
}
-void ws_esp8266_disconnect(){
- int ret;
- uint8_t Tx_disconnect[]="AT+CWQAP\r\n";uint8_t buffer[17]={0};
- while(ret!=1){
-
- HAL_UART_Transmit_IT(&huart1, Tx_disconnect,strlen((char*)Tx_disconnect));
- HAL_UART_Receive_IT(&huart1, buffer, 17);
- HAL_Delay(2000);
- if(ws_esp8266_checkOK(buffer,17)==1){
- ret=1;
- }
+void ws_esp8266_send(uint8_t* data, size_t size) {
+ size_t limited_size = WS_MIN(size, WS_DMA_TX_BUFFER_SIZE - 1);
+ memcpy(g_ws_esp8266_dma_tx_buffer, data, limited_size);
+ g_ws_esp8266_dma_tx_buffer[limited_size] = 0x00;
- }
+#ifdef WS_DBG_PRINT_ESP_OVER_USART2
+ ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_TX);
+ HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer), 100);
+#endif
- HAL_Delay(5000);
+ HAL_UART_Transmit_DMA(&huart1, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer));
+ __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
}
-void ws_esp8266_mode(){
- int ret;
- uint8_t buffer1[20]={0}; uint8_t Tx_mode[]="AT+CWMODE=1\r\n";
-
- while(ret!=1){
-
- HAL_UART_Transmit_IT(&huart1, Tx_mode,strlen((char*)Tx_mode));
- HAL_UART_Receive_IT(&huart1, buffer1, 20);
- HAL_Delay(1000);
-
- if(ws_esp8266_checkOK(buffer1,20)==1){
- ret=1;
-
- }
- }
-
- HAL_Delay(1000);
+void ws_esp8266_connect() {
+ ws_esp8266_send_seq("AT+CWJAP=\"" WS_ESP8266_WLAN_SSID "\",\"" WS_ESP8266_WLAN_PASSWD "\"\r\n");
}
-void ws_esp8266_connect(){
- uint8_t Tx_network[]="AT+CWJAP=\"Test\",\"12345678\"\r\n";
-
-
- HAL_UART_Transmit(&huart1, Tx_network,strlen((char*)Tx_network),1000);
- HAL_Delay(10000);
-// HAL_UART_Transmit(&huart1, Tx_network,sizeof(Tx_network),1000);
-// HAL_Delay(10000);
-
-
-
-
+void ws_esp8266_ap_client_mode() {
+ ws_esp8266_send_seq("AT+CWMODE=1\r\n");
}
-void ws_esp8266_serveraan(){
- int ret;
- uint8_t buffer1[30]={0}; uint8_t Tx_server[]="AT+CIPSERVER=1,80\r\n";
-
- while(ret!=1){
-
- HAL_UART_Transmit_IT(&huart1, Tx_server,strlen((char*)Tx_server));
- HAL_UART_Receive_IT(&huart1, buffer1, 30);
- HAL_Delay(2000);
-
- if(ws_esp8266_checkOK(buffer1,30)==1){
- ret=1;
-
- }
- }
-
- HAL_Delay(1000);
+void ws_esp8266_start_tcp_server() {
+ ws_esp8266_send_seq("AT+CIPSERVER=0\r\n"); // stop tcp server (if running)
+ ws_esp8266_send_seq("AT+CIPMUX=1\r\n"); // enable multiplexing (allow multiple connections)
+ ws_esp8266_send_seq("AT+CIPSERVER=1," WS_SERVER_PORT "\r\n"); // start tcp server
}
-void ws_esp8266_serveruit(){
- int ret;
- uint8_t buffer1[27]={0}; uint8_t Tx_server[]="AT+CIPSERVER=0\r\n";
-//
-// while(ret!=1){
-
- HAL_UART_Transmit_IT(&huart1, Tx_server,strlen((char*)Tx_server));
-// HAL_UART_Receive_IT(&huart1, buffer1, 27);
- HAL_Delay(3000);
-
-// if(unlink(buffer1,27)==1){
-// ret=1;
-//
-// }
-//
-// }
- HAL_Delay(1000);
+void ws_esp8266_set_mac() {
+ ws_esp8266_send_seq("AT+CIPSTAMAC=\"" WS_ESP8266_WLAN_MAC "\"\r\n");
}
-void ws_esp8266_mux(){
- int ret;
- uint8_t buffer2[20]={0}; uint8_t Tx_mux[]="AT+CIPMUX=1\r\n";
-
- while(ret!=1){
-
- HAL_UART_Transmit_IT(&huart1, Tx_mux,strlen((char*)Tx_mux));
- HAL_UART_Receive_IT(&huart1, buffer2, 20);
- HAL_Delay(2000);
-
- if(ws_esp8266_checkOK(buffer2,20)==1){
- ret=1;
-
- }
-
- }
-
- HAL_Delay(5000);
-}
-void ws_esp8266_close(){
-
- uint8_t Tx_close[]="AT+CIPCLOSE=0\r\n";
-
-
- HAL_UART_Transmit_IT(&huart1, Tx_close,strlen((char*)Tx_close));
-
- HAL_Delay(3000);
+void ws_esp8266_set_ip() {
+ ws_esp8266_send_seq("AT+CIPSTA=\"" WS_ESP8266_WLAN_IP "\"\r\n");
}
-
diff --git a/stm32f091/esp8266.h b/stm32f091/esp8266.h
index c09a557..94a7356 100644
--- a/stm32f091/esp8266.h
+++ b/stm32f091/esp8266.h
@@ -1,16 +1,38 @@
#pragma once
+#include <stm32f0xx_hal.h>
+#include <stdlib.h>
#include <stdint.h>
+#include <stdbool.h>
-void ws_esp8266_ATsendCommand(uint8_t* data);
-int ws_esp8266_checkOK(uint8_t *receiveData,int length);
-int ws_esp8266_receivingMsg(uint8_t *receiveData,int length);
-int ws_esp8266_unlink(uint8_t *receiveData,int length);
-void ws_esp8266_StartEsp();
-void ws_esp8266_disconnect();
-void ws_esp8266_mode();
+#include "consts.h"
+
+/** @brief DMA rx buffer */
+extern uint8_t g_ws_esp8266_dma_rx_buffer[WS_DMA_RX_BUFFER_SIZE];
+/** @brief null-terminated tx buffer string */
+extern uint8_t g_ws_esp8266_dma_tx_buffer[WS_DMA_TX_BUFFER_SIZE];
+
+/** @brief DMA1 channel 2-3 & DMA2 channel 1-2 interrupt handler */
+void DMA1_Ch2_3_DMA2_Ch1_2_IRQHandler(void);
+/** @brief DMA1 channel 1 interrupt handler */
+void DMA1_Ch1_IRQHandler(void);
+/** @brief USART1 interrupt handler */
+void USART1_IRQHandler(void);
+
+/** @brief send data to esp over uart with dma */
+void ws_esp8266_send(uint8_t* data, size_t size);
+
+/** @brief line idle, handle new data on dma buffer */
+void ws_esp8266_incoming_data_chunk();
+
+/** @brief connect to access point using wifi.h credentials */
void ws_esp8266_connect();
-void ws_esp8266_serveraan();
-void ws_esp8266_serveruit();
-void ws_esp8266_mux();
-void ws_esp8266_close();
+/** @brief set esp to access point client mode (connect to AP, not become one) */
+void ws_esp8266_ap_client_mode();
+/** @brief initialize and configure the tcp server */
+void ws_esp8266_start_tcp_server();
+
+/** @brief set mac address of the esp client */
+void ws_esp8266_set_mac();
+/** @brief set static ip address of the esp client */
+void ws_esp8266_set_ip();
diff --git a/stm32f091/main.c b/stm32f091/main.c
index 9235f1b..67c0a8d 100644
--- a/stm32f091/main.c
+++ b/stm32f091/main.c
@@ -5,19 +5,9 @@
#include "main.h"
#include "setup.h"
#include "sensor.h"
-#include "backlog.h"
int main() {
ws_io_setup();
- HAL_GPIO_Init(GPIOA, &(GPIO_InitTypeDef) {
- .Pin = GPIO_PIN_5,
- .Mode = GPIO_MODE_OUTPUT_PP,
- .Pull = GPIO_NOPULL
- });
-
- ws_backlog_alloc(24 * 60);
- ws_sensor_read();
-
- xTaskCreate(ws_sensor_read_task, "sensor", 128, NULL, 1, NULL);
+ xTaskCreate(ws_sensor_read_task, "sensor", 64, NULL, 1, NULL);
vTaskStartScheduler();
}
diff --git a/stm32f091/makefile b/stm32f091/makefile
index 5a185de..dd03761 100644
--- a/stm32f091/makefile
+++ b/stm32f091/makefile
@@ -4,6 +4,9 @@ OC = arm-none-eabi-objcopy
RM = rm -f
TARGET = main
+HOST=$(strip $(shell uname -o))
+
+include ../shared/shared.mk
SHARED_FLAGS += -g
SHARED_FLAGS += -DSTM32F091xC
@@ -11,7 +14,6 @@ SHARED_FLAGS += -Wall
SHARED_FLAGS += -Wextra
# SHARED_FLAGS += -Wno-register
SHARED_FLAGS += -Wa,--defsym,CALL_ARM_SYSTEM_INIT=1
-# SHARED_FLAGS += -I/usr/arm-none-eabi/include/
SHARED_FLAGS += -I./lib/STM32-base-STM32Cube/HAL/STM32F0xx/inc
SHARED_FLAGS += -I./lib/STM32-base-STM32Cube/HAL/STM32F0xx/inc/Legacy
SHARED_FLAGS += -I./lib/STM32-base-STM32Cube/CMSIS/ARM/inc
@@ -20,7 +22,10 @@ SHARED_FLAGS += -I./lib/STM32-base/startup
SHARED_FLAGS += -I./lib/FreeRTOS-Kernel/include
SHARED_FLAGS += -I./lib/FreeRTOS-Kernel/portable/GCC/ARM_CM0/
SHARED_FLAGS += -I.
-SHARED_FLAGS += -O1
+ifeq ($(HOST),GNU/Linux)
+SHARED_FLAGS += -I/usr/arm-none-eabi/include/
+endif
+# SHARED_FLAGS += -O1
SHARED_FLAGS += -ffunction-sections
SHARED_FLAGS += -fdata-sections
SHARED_FLAGS += -Wl,--gc-sections
@@ -74,14 +79,20 @@ $(TARGET).bin: $(TARGET).elf
%.o: %.s
$(CC) -c $(AFLAGS) $< -o $@
+lib/%.o: lib/%.c
+ $(CC) -c $(CFLAGS) -w $< -o $@
+
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
+%-stm.o: %.c
+ $(CC) -c $(CFLAGS) $< -o $@
+
$(TARGET).elf: $(OBJS)
$(LD) $(LFLAGS) $^ -o $@
flash: $(TARGET).bin
- st-flash write $(TARGET).bin 0x08000000
+ st-flash --reset write $(TARGET).bin 0x08000000
compile_commands: clean
compiledb make -n
diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c
new file mode 100644
index 0000000..f4be08f
--- /dev/null
+++ b/stm32f091/protocol.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+
+#include "../shared/protocol.h"
+#include "backlog.h"
+#include "util.h"
+#include "server.h"
+#include "esp8266.h"
+
+void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) {
+ static int record_amount = 0;
+ static unsigned int record_offset = 0;
+ const char* response_header = "id,temperature,humidity,atmospheric_pressure\n";
+ const unsigned int response_line_len = strlen("xxxx,xx,xx,xx\n");
+
+ if (!send) {
+ response->success = WS_PROTOCOL_CMD_RETURN_OK;
+ response->csh = true;
+ response->msg = ws_bin_s_alloc(0);
+ response->msg->bytes = 0;
+ if (sscanf(parsed_cmd->argv[1], "%x", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR;
+ if (sscanf(parsed_cmd->argv[2], "%x", &record_offset) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR;
+ else {
+ record_amount = WS_MIN(record_amount + record_offset, ws_backlog_get_record_count());
+ record_amount = WS_MAX(0, record_amount - record_offset);
+ response->msg->bytes = strlen(response_header) + response_line_len * record_amount;
+ }
+ } else {
+ if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) return;
+ ws_protocol_send_data(response_header, strlen(response_header));
+ char line[response_line_len + 1]; // + 1 for string terminator
+ for (unsigned int i = 0; i < record_amount; i++) {
+ ws_s_backlog_record* record = ws_backlog_get_last_record(i + record_offset);
+ sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure);
+ ws_protocol_send_data(line, response_line_len);
+ }
+ }
+}
+
+void ws_protocol_send_data(const char* data, unsigned int length) {
+ ws_server_buffer_send_append((uint8_t*) data, length);
+}
diff --git a/stm32f091/readme.md b/stm32f091/readme.md
new file mode 100644
index 0000000..d2758b5
--- /dev/null
+++ b/stm32f091/readme.md
@@ -0,0 +1,11 @@
+# stm32 firmware subdirectory
+
+- uses make
+- make sure to initialize the git submodules
+- all warnings from source files in the lib/ subfolder are hidden
+- copy wifi.def.h to wifi.h and edit the network credentials
+- the initialization code is broken in some way which means that a soft reset
+ is required for the uart dma to work, either (a) press the reset button on
+ the development board after plugging in, or (b) run `st-flash reset` after
+ plugging in.
+
diff --git a/stm32f091/sensor.c b/stm32f091/sensor.c
index bc3cfd3..1c94e2a 100644
--- a/stm32f091/sensor.c
+++ b/stm32f091/sensor.c
@@ -58,6 +58,12 @@ void ws_sensor_read() {
.sens_humidity = ws_sensor_humidity()
};
ws_backlog_add_record(record);
+
+ // < DEBUG PROTOCOL PARSING CODE >
+ // ws_s_protocol_req_parser_state* parser = ws_protocol_req_parser_alloc();
+ // const char* request = "last-records 5\n";
+ // ws_protocol_parse_req_bytes(parser, (char*) request, strlen(request));
+ // ws_protocol_req_parser_free(parser);
}
void ws_sensor_read_task() {
diff --git a/stm32f091/server.c b/stm32f091/server.c
index e289245..d69ef1e 100644
--- a/stm32f091/server.c
+++ b/stm32f091/server.c
@@ -3,55 +3,222 @@
#include <stdlib.h>
#include <string.h>
-#include "setup.h"
-#include "server.h"
+#include "../shared/protocol.h"
#include "esp8266.h"
+#include "server.h"
+#include "setup.h"
+#include "consts.h"
+#include "util.h"
+
+ws_s_server_parser g_ws_server_parser = {
+ .last_response = WS_SERVER_RC_NONE,
+ .mode = WS_SERVER_LM_IDLE,
+
+ .current_channel = 0,
+ .channel_data_length = 0,
+ .channel_data_counter = 0,
+ .channel_listen_mode = WS_SERVER_CL_CHANNEL_ID,
+
+ .rc = { 0 },
+};
+
+static ws_s_protocol_req_parser_state* g_ws_protocol_parsers[WS_SERVER_MAX_CHANNELS] = {0};
+static unsigned int g_ws_esp8266_dma_tx_buffer_head = 0;
+static unsigned int g_ws_esp8266_dma_tx_buffer_tail = 0;
+static unsigned int g_ws_esp8266_dma_tx_buffer_cs = 0; // chunk size
+
+void ws_server_req_parse_byte(unsigned int channel, uint8_t byte, bool ignore) {
+ if (ignore) return;
+ if (channel >= WS_SERVER_MAX_CHANNELS) return;
+
+ if (g_ws_protocol_parsers[channel] == NULL) {
+ g_ws_protocol_parsers[channel] = ws_protocol_req_parser_alloc();
+ }
-void ws_server_demo() {
- uint8_t ok[]= "ok\r\n";
- uint8_t hier[]= "hier\r\n";
- //disconnect();//connect();//
-
- ws_esp8266_StartEsp();
- HAL_UART_Transmit(&huart2, hier,sizeof(hier),1000);
- ws_esp8266_close();
- HAL_UART_Transmit(&huart2, hier,sizeof(hier),1000);
- ws_esp8266_serveruit();
- HAL_UART_Transmit(&huart2, hier,sizeof(hier),1000);
- ws_esp8266_mux();
- HAL_UART_Transmit(&huart2, hier,sizeof(hier),1000);
- ws_esp8266_mode();
- HAL_UART_Transmit(&huart2, hier,sizeof(hier),1000);
-
- ws_esp8266_serveraan();
- HAL_UART_Transmit(&huart2, ok,sizeof(ok),1000);
- uint8_t receive[24]={0};
- uint8_t sendToQTData[]="gelukt";
- uint8_t test[]="test";
- int ret;
- //ATsendCommand(sendToQTData);
- while (1)
- {
-
- if(receive[0]=='\0')
- {
- HAL_UART_Receive_IT(&huart1, receive, 24);
+ ws_protocol_parse_req_byte(g_ws_protocol_parsers[channel], byte);
+}
+
+void ws_server_req_finish(unsigned int channel, bool ignore) {
+ if (ignore) return;
+ if (channel >= WS_SERVER_MAX_CHANNELS) return;
+
+ if (g_ws_protocol_parsers[channel] != NULL) {
+ ws_protocol_req_parser_free(g_ws_protocol_parsers[channel]);
+ g_ws_protocol_parsers[channel] = NULL;
+ }
+}
+
+static bool ws_server_is_response(char data, uint8_t* counter, const char* cmd, unsigned short cmd_len) {
+ if (data == cmd[*counter]) *counter += 1;
+ else *counter = 0;
+ if (*counter == cmd_len) return true;
+ return false;
+}
+
+void ws_server_req_incoming(uint8_t* data, size_t size) {
+#ifdef WS_DBG_PRINT_ESP_OVER_USART2
+ ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_RX);
+ HAL_UART_Transmit(&huart2, data, size, 100);
+#endif
+
+ for (unsigned int i = 0; i < size; i++) {
+ uint8_t byte = data[i];
+
+ switch (g_ws_server_parser.mode) {
+ case WS_SERVER_LM_CMD_ECHO: {
+ if (byte == '\n') g_ws_server_parser.mode = WS_SERVER_LM_STATUS_CODE;
+ break;
+ }
+ case WS_SERVER_LM_STATUS_CODE: {
+ bool code_got = false;
+ if (ws_server_is_response(byte, &g_ws_server_parser.rc.s_ok, "OK", 2)) {
+ code_got = true;
+ g_ws_server_parser.last_response = WS_SERVER_RC_OK;
+ } else if (ws_server_is_response(byte, &g_ws_server_parser.rc.s_error, "ERROR", 5)) {
+ code_got = true;
+ g_ws_server_parser.last_response = WS_SERVER_RC_ERR;
+ } else if (ws_server_is_response(byte, &g_ws_server_parser.rc.s_fail, "FAIL", 4)) {
+ code_got = true;
+ g_ws_server_parser.last_response = WS_SERVER_RC_ERR;
+ } else if (ws_server_is_response(byte, &g_ws_server_parser.rc.s_busy, "busy p...", 9)) {
+ code_got = true;
+ g_ws_server_parser.last_response = WS_SERVER_RC_BUSY;
+ }
+ if (code_got) g_ws_server_parser.mode = WS_SERVER_LM_IDLE;
+ break;
}
- else
- {
- HAL_UART_Transmit(&huart2, test,strlen((char*)test),1000);
- HAL_Delay(5000);
-
- // if(receivingMsg(receive, 24)==1)
- // {
- // ret=1;
- ws_esp8266_ATsendCommand(sendToQTData);
- ws_esp8266_close();
- memset(receive,0,24);
- //}
+ case WS_SERVER_LM_IDLE: {
+ if (ws_server_is_response(byte, &g_ws_server_parser.rc.i_ipd, "+IPD,", 5)) {
+ g_ws_server_parser.mode = WS_SERVER_LM_IPD_LISTENING;
+ } else if (ws_server_is_response(byte, &g_ws_server_parser.rc.i_prompt, ">", 1)) {
+ // ^^^ this is ">" on official espressif firmware, "> " on ai-thinker firmware
+ g_ws_server_parser.mode = WS_SERVER_LM_CIPSEND_LISTENING;
+ ws_server_buffer_send_chunk();
+ }
+ break;
}
+ case WS_SERVER_LM_IPD_LISTENING: {
+ switch (g_ws_server_parser.channel_listen_mode) {
+ case WS_SERVER_CL_CHANNEL_ID: {
+ if (byte == ',') {
+ g_ws_server_parser.channel_listen_mode = WS_SERVER_CL_DATA_LENGTH;
+ break;
+ }
+ g_ws_server_parser.current_channel *= 10;
+ g_ws_server_parser.current_channel += byte - '0'; // ascii to int
+ break;
+ }
+ case WS_SERVER_CL_DATA_LENGTH: {
+ if (byte == ':') {
+ g_ws_server_parser.channel_listen_mode = WS_SERVER_CL_DATA_READ;
+ if (g_ws_server_parser.channel_data_length > WS_PROTOCOL_CMD_BUFFER_LEN)
+ g_ws_server_parser.channel_data_ignore = true;
+ break;
+ }
+ g_ws_server_parser.channel_data_length *= 10;
+ g_ws_server_parser.channel_data_length += byte - '0'; // ascii to int
+ break;
+ }
+ case WS_SERVER_CL_DATA_READ: {
+ ws_server_req_parse_byte(g_ws_server_parser.current_channel, byte, g_ws_server_parser.channel_data_ignore);
+ g_ws_server_parser.channel_data_counter++;
+ if (g_ws_server_parser.channel_data_counter == g_ws_server_parser.channel_data_length) {
+ g_ws_server_parser.current_channel = 0;
+ g_ws_server_parser.channel_data_counter = 0;
+ g_ws_server_parser.channel_data_length = 0;
+ g_ws_server_parser.channel_listen_mode = WS_SERVER_CL_CHANNEL_ID;
+ ws_server_req_finish(g_ws_server_parser.current_channel, g_ws_server_parser.channel_data_ignore);
+ g_ws_server_parser.mode = WS_SERVER_LM_IDLE;
+ ws_server_buffer_request_chunk_send();
+ }
+ break;
+ }
+ default: {}
+ }
+ break;
+ }
+ case WS_SERVER_LM_CIPSEND_LISTENING: {
+ if (ws_server_is_response(byte, &g_ws_server_parser.rc.l_send_ok, "SEND OK", 7) || ws_server_is_response(byte, &g_ws_server_parser.rc.l_error, "ERROR", 5)) {
+ ws_server_buffer_request_chunk_send();
+ }
+ break;
+ }
+ default: {}
+ }
+ }
+}
+
+/** @brief get amount of bytes in g_ws_esp8266_dma_tx_buffer until \n */
+static unsigned int ws_server_next_line_length() {
+ for (unsigned int i = g_ws_esp8266_dma_tx_buffer_tail; i <= g_ws_esp8266_dma_tx_buffer_head; i++)
+ if (g_ws_esp8266_dma_tx_buffer[i] == '\n') return i - g_ws_esp8266_dma_tx_buffer_tail + 1;
+ return g_ws_esp8266_dma_tx_buffer_head - g_ws_esp8266_dma_tx_buffer_tail;
+}
+void ws_server_send(uint8_t* data, size_t size) {
+ g_ws_server_parser.mode = WS_SERVER_LM_CMD_ECHO;
+ ws_esp8266_send(data, size);
+ while (g_ws_server_parser.mode != WS_SERVER_LM_IDLE) {};
+}
+void ws_server_buffer_send_append(uint8_t* data, size_t size) {
+ // TODO: buffer overrun protection
+ // while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); // make sure buffer isn't used
+ strncpy((char*) &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_head], (char*) data, size); // append string
+ g_ws_esp8266_dma_tx_buffer_head += size; // shift head
+}
- }
+void ws_server_buffer_request_chunk_send() {
+ g_ws_esp8266_dma_tx_buffer_cs = ws_server_next_line_length();
+
+ char* cmd = NULL;
+ size_t len;
+
+ if (g_ws_esp8266_dma_tx_buffer_cs > 0) {
+ len = asiprintf(&cmd, "AT+CIPSEND=%d,%d\r\n", g_ws_server_parser.current_channel, g_ws_esp8266_dma_tx_buffer_cs);
+ } else {
+ len = asiprintf(&cmd, "AT+CIPCLOSE=%d\r\n", g_ws_server_parser.current_channel);
+ }
+ free(cmd);
+
+ g_ws_server_parser.mode = WS_SERVER_LM_CMD_ECHO;
+
+#ifdef WS_DBG_PRINT_ESP_OVER_USART2
+ ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_TX);
+ HAL_UART_Transmit(&huart2, (uint8_t*) cmd, len, 100);
+#endif
+ HAL_UART_Transmit(&huart1, (uint8_t*) cmd, len, 100);
+}
+
+void ws_server_buffer_send_chunk() {
+#ifdef WS_DBG_PRINT_ESP_OVER_USART2
+ ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_TX);
+ HAL_UART_Transmit(&huart2, &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_tail], g_ws_esp8266_dma_tx_buffer_cs, 100);
+#endif
+ HAL_UART_Transmit(&huart1, &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_tail], g_ws_esp8266_dma_tx_buffer_cs, 100);
+ g_ws_esp8266_dma_tx_buffer_tail += g_ws_esp8266_dma_tx_buffer_cs;
+
+ if (g_ws_esp8266_dma_tx_buffer_head == g_ws_esp8266_dma_tx_buffer_tail) {
+ g_ws_esp8266_dma_tx_buffer_head = g_ws_esp8266_dma_tx_buffer_tail = 0;
+ }
+
+// #ifdef WS_DBG_PRINT_ESP_OVER_USART2
+// ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_TX);
+// HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, g_ws_esp8266_dma_tx_buffer_head, 100);
+// #endif
+//
+// HAL_UART_Transmit(&huart1, g_ws_esp8266_dma_tx_buffer, g_ws_esp8266_dma_tx_buffer_head, 100);
+// g_ws_esp8266_dma_tx_buffer_head = 0;
+//
+// HAL_UART_Transmit(&huart1, (uint8_t*) "+++", 3, 100);
+}
+
+// TODO: refactor this
+void ws_server_req_respond_end(unsigned int channel) {
+ char* cmd = NULL;
+ size_t len = asiprintf(&cmd, "AT+CIPCLOSE=%d\r\n", channel);
+ g_ws_server_parser.mode = WS_SERVER_LM_CMD_ECHO;
+ ws_esp8266_send((uint8_t*) cmd, len);
+ while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2));
+ free(cmd);
}
diff --git a/stm32f091/server.h b/stm32f091/server.h
index 6a3501d..07c49d9 100644
--- a/stm32f091/server.h
+++ b/stm32f091/server.h
@@ -1,6 +1,99 @@
#pragma once
-void ws_server_demo();
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
-/** FreeRTOS task that listens for incoming requests from the esp */
-void ws_server_listen_task();
+typedef enum {
+ WS_SERVER_LM_CMD_ECHO, /** @brief listen for echo of sent command */
+ WS_SERVER_LM_STATUS_CODE, /** @brief listen for busy, ERROR or OK */
+ WS_SERVER_LM_IDLE, /** @brief listen for incoming +IPD commands */
+ WS_SERVER_LM_IPD_LISTENING, /** @brief +IPD received, now reading data */
+ WS_SERVER_LM_CIPSEND_LISTENING, /** @brief AT+CIPSEND sent, now reading data */
+} ws_e_server_listen_mode;
+
+typedef enum {
+ WS_SERVER_CL_CHANNEL_ID, /** @brief listen channel id */
+ WS_SERVER_CL_DATA_LENGTH, /** @brief listen for data byte length */
+ WS_SERVER_CL_DATA_READ, /** @brief listen for data and pipe to ws_protocol_parse_req_byte */
+} ws_e_channel_listen_mode;
+
+typedef enum {
+ WS_SERVER_RC_NONE = -1,
+ WS_SERVER_RC_BUSY,
+ WS_SERVER_RC_ERR,
+ WS_SERVER_RC_OK,
+} ws_e_server_response_code;
+
+typedef struct {
+ uint8_t s_ok; /** @brief status code OK */
+ uint8_t s_error; /** @brief status code OK */
+ uint8_t s_fail; /** @brief status code OK */
+ uint8_t s_busy; /** @brief status code OK */
+ uint8_t i_ipd; /** @brief idle +IPD, */
+ uint8_t i_prompt; /** @brief idle > */
+ uint8_t l_send_ok; /** @brief ipd listen SEND OK */
+ uint8_t l_error; /** @brief ipd listen ERROR */
+} ws_s_server_parser_response_counter;
+
+typedef struct {
+ ws_e_server_listen_mode mode;
+ ws_e_server_response_code last_response;
+ unsigned int current_channel;
+ unsigned int channel_data_length;
+ unsigned int channel_data_counter;
+ ws_e_channel_listen_mode channel_listen_mode;
+ bool channel_data_ignore;
+ ws_s_server_parser_response_counter rc;
+} ws_s_server_parser;
+
+/** @brief global server parser struct */
+extern ws_s_server_parser g_ws_server_parser;
+
+/**
+ * @brief +IPD incoming request handler
+ *
+ * this function takes chunks of data from the esp8266 and parses +IPD
+ * commands. when a valid +IPD command is detected, it gets forwarded to
+ * ws_protocol_parse_req_byte.
+ *
+ * @param data pointer to data array
+ * @param size amount of bytes allowed to be read from `data`
+ */
+void ws_server_req_incoming(uint8_t* data, size_t size);
+
+// /** @brief send AT response header for incoming request on specific channel */
+// void ws_server_req_respond_start(unsigned int channel);
+// /** @brief send AT tcp close on specific channel */
+// void ws_server_req_respond_end(unsigned int channel);
+
+/** @brief send data to esp, waiting until server returns to idle mode */
+void ws_server_send(uint8_t* data, size_t size);
+
+/**
+ * @brief parse byte from channel
+ *
+ * automatically creates parser struct and passes data onto protocol parser
+ * functions
+ *
+ * @param channel request channel
+ * @param byte data byte
+ * @param ignore ignore mode
+ */
+void ws_server_req_parse_byte(unsigned int channel, uint8_t byte, bool ignore);
+
+/**
+ * @brief close connection
+ *
+ * deallocates any parser struct that were automatically created in
+ * ws_server_req_parse_byte
+ *
+ * @param channel request channel
+ * @param ignore ignore mode
+ */
+void ws_server_req_finish(unsigned int channel, bool ignore);
+
+void ws_server_buffer_send_append(uint8_t* data, size_t size);
+
+void ws_server_buffer_request_chunk_send();
+void ws_server_buffer_send_chunk();
diff --git a/stm32f091/setup.c b/stm32f091/setup.c
index fec2b7c..ef00a29 100644
--- a/stm32f091/setup.c
+++ b/stm32f091/setup.c
@@ -5,7 +5,11 @@
#include <FreeRTOS.h>
#include <task.h>
+#include "esp8266.h"
#include "setup.h"
+#include "backlog.h"
+#include "server.h"
+#include "util.h"
I2C_HandleTypeDef hi2c1 = {
.Instance = I2C1,
@@ -20,16 +24,14 @@ I2C_HandleTypeDef hi2c1 = {
};
UART_HandleTypeDef huart1 = {
- .Instance = USART1,
- .Init.BaudRate = 115200,
- .Init.WordLength = UART_WORDLENGTH_8B,
- .Init.StopBits = UART_STOPBITS_1,
- .Init.Parity = UART_PARITY_NONE,
- .Init.Mode = UART_MODE_TX_RX,
- .Init.HwFlowCtl = UART_HWCONTROL_NONE,
- .Init.OverSampling = UART_OVERSAMPLING_16,
- .Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE,
- .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
+ .Instance = USART1,
+ .Init.BaudRate = 115200,
+ .Init.WordLength = UART_WORDLENGTH_8B,
+ .Init.StopBits = UART_STOPBITS_1,
+ .Init.Parity = UART_PARITY_NONE,
+ .Init.Mode = UART_MODE_TX_RX,
+ .Init.HwFlowCtl = UART_HWCONTROL_NONE,
+ .Init.OverSampling = UART_OVERSAMPLING_16,
};
UART_HandleTypeDef huart2 = {
@@ -45,10 +47,33 @@ UART_HandleTypeDef huart2 = {
.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
};
+DMA_HandleTypeDef hdma_usart1_rx = {
+ .Instance = DMA1_Channel1,
+ .Init.Direction = DMA_PERIPH_TO_MEMORY,
+ .Init.PeriphInc = DMA_PINC_DISABLE,
+ .Init.MemInc = DMA_MINC_ENABLE,
+ .Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE,
+ .Init.MemDataAlignment = DMA_MDATAALIGN_BYTE,
+ .Init.Mode = DMA_CIRCULAR,
+ .Init.Priority = DMA_PRIORITY_LOW,
+};
+
+DMA_HandleTypeDef hdma_usart1_tx = {
+ .Instance = DMA1_Channel2,
+ .Init.Direction = DMA_MEMORY_TO_PERIPH,
+ .Init.PeriphInc = DMA_PINC_DISABLE,
+ .Init.MemInc = DMA_MINC_ENABLE,
+ .Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE,
+ .Init.MemDataAlignment = DMA_MDATAALIGN_BYTE,
+ .Init.Mode = DMA_NORMAL,
+ .Init.Priority = DMA_PRIORITY_LOW,
+};
+
static void ws_io_clock_setup();
static void ws_io_i2c_setup();
static void ws_io_usart1_setup();
static void ws_io_usart2_setup();
+static void ws_io_dma_setup();
static void ws_setup_error_handler();
void ws_io_setup() {
@@ -56,8 +81,36 @@ void ws_io_setup() {
ws_io_clock_setup();
ws_io_i2c_setup();
+ ws_io_dma_setup();
ws_io_usart1_setup();
ws_io_usart2_setup();
+ ws_io_dma_setup();
+
+ HAL_GPIO_Init(GPIOA, &(GPIO_InitTypeDef) {
+ .Pin = GPIO_PIN_5,
+ .Mode = GPIO_MODE_OUTPUT_PP,
+ .Pull = GPIO_NOPULL
+ });
+
+ // TODO: remove debug size
+ ws_backlog_alloc(24 * 60);
+ // ws_backlog_alloc(10);
+
+#ifdef WS_DBG_PRINT_ESP_OVER_USART2
+ ws_dbg_set_usart2_tty_color(WS_DBG_TTY_COLOR_DBGMSG);
+ const char restart_str[] = "\r\n--- stm restart ---\r\n";
+ HAL_UART_Transmit(&huart2, (uint8_t*) restart_str, strlen(restart_str), 100);
+#endif
+
+ ws_esp8266_ap_client_mode();
+#ifdef WS_ESP8266_WLAN_MAC
+ ws_esp8266_set_mac();
+#endif
+#ifdef WS_ESP8266_WLAN_IP
+ ws_esp8266_set_ip();
+#endif
+ do ws_esp8266_connect(); while (g_ws_server_parser.last_response == WS_SERVER_RC_ERR);
+ ws_esp8266_start_tcp_server();
}
static void ws_io_clock_setup() {
@@ -97,11 +150,27 @@ static void ws_io_i2c_setup() {
static void ws_io_usart1_setup() {
if (HAL_UART_Init(&huart1) != HAL_OK)
return ws_setup_error_handler();
+
+ HAL_UART_Receive_DMA(&huart1, g_ws_esp8266_dma_rx_buffer, WS_DMA_RX_BUFFER_SIZE);
+ __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);
+
+ __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // enable receive intterupts
+ __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // enable idle line detection
}
static void ws_io_usart2_setup() {
if (HAL_UART_Init(&huart2) != HAL_OK)
return ws_setup_error_handler();
+}
+
+static void ws_io_dma_setup() {
+ __HAL_RCC_DMA1_CLK_ENABLE();
+
+ // interrupt priorities
+ HAL_NVIC_SetPriority(DMA1_Ch1_IRQn, 3, 0);
+ HAL_NVIC_EnableIRQ(DMA1_Ch1_IRQn);
+ HAL_NVIC_SetPriority(DMA1_Ch2_3_DMA2_Ch1_2_IRQn, 3, 0);
+ HAL_NVIC_EnableIRQ(DMA1_Ch2_3_DMA2_Ch1_2_IRQn);
}
void HAL_MspInit() {
@@ -136,6 +205,14 @@ void HAL_UART_MspInit(UART_HandleTypeDef *huart) {
.Alternate = GPIO_AF1_USART1,
});
+ // DMA setup
+ if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) return ws_setup_error_handler();
+ __HAL_DMA1_REMAP(HAL_DMA1_CH1_USART1_RX);
+ __HAL_LINKDMA(huart, hdmarx, hdma_usart1_rx);
+ if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK) return ws_setup_error_handler();
+ __HAL_DMA1_REMAP(HAL_DMA1_CH2_USART1_TX);
+ __HAL_LINKDMA(huart, hdmatx, hdma_usart1_tx);
+
// USART1 interrupt Init
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
diff --git a/stm32f091/setup.h b/stm32f091/setup.h
index 8ca3720..d459635 100644
--- a/stm32f091/setup.h
+++ b/stm32f091/setup.h
@@ -4,24 +4,13 @@
#include <stm32f0xx_hal_i2c.h>
#include <stm32f0xx_hal_uart.h>
-#define WS_PINOUT_I2C_SDA_PIN GPIO_PIN_9
-#define WS_PINOUT_I2C_SDA_PORT GPIOB
-#define WS_PINOUT_I2C_SCL_PIN GPIO_PIN_8
-#define WS_PINOUT_I2C_SCL_PORT GPIOB
-
-#define WS_PINOUT_USART1_RX_PIN GPIO_PIN_10
-#define WS_PINOUT_USART1_RX_PORT GPIOA
-#define WS_PINOUT_USART1_TX_PIN GPIO_PIN_9
-#define WS_PINOUT_USART1_TX_PORT GPIOA
-
-#define WS_PINOUT_USART2_RX_PIN GPIO_PIN_3
-#define WS_PINOUT_USART2_RX_PORT GPIOA
-#define WS_PINOUT_USART2_TX_PIN GPIO_PIN_2
-#define WS_PINOUT_USART2_TX_PORT GPIOA
+#include "consts.h"
extern I2C_HandleTypeDef hi2c1;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
+extern DMA_HandleTypeDef hdma_usart1_rx;
+extern DMA_HandleTypeDef hdma_usart1_tx;
void ws_io_setup();
diff --git a/stm32f091/util.h b/stm32f091/util.h
new file mode 100644
index 0000000..11bee6f
--- /dev/null
+++ b/stm32f091/util.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stm32f0xx_hal.h>
+#include <string.h>
+
+#include "setup.h"
+#include "../shared/util.h"
+
+#define WS_DBG_TTY_COLOR_BLK 0x0
+#define WS_DBG_TTY_COLOR_RED 0x1
+#define WS_DBG_TTY_COLOR_GRN 0x2
+#define WS_DBG_TTY_COLOR_YLW 0x3
+#define WS_DBG_TTY_COLOR_BLU 0x4
+#define WS_DBG_TTY_COLOR_VLT 0x5
+#define WS_DBG_TTY_COLOR_CYN 0x6
+#define WS_DBG_TTY_COLOR_WHI 0x7
+#define WS_DBG_TTY_COLOR_BR_BLK 0x8
+#define WS_DBG_TTY_COLOR_BR_RED 0x9
+#define WS_DBG_TTY_COLOR_BR_GRN 0xa
+#define WS_DBG_TTY_COLOR_BR_YLW 0xb
+#define WS_DBG_TTY_COLOR_BR_BLU 0xc
+#define WS_DBG_TTY_COLOR_BR_VLT 0xd
+#define WS_DBG_TTY_COLOR_BR_CYN 0xe
+#define WS_DBG_TTY_COLOR_BR_WHI 0xf
+
+// serial debug color scheme
+#define WS_DBG_TTY_COLOR_DBGMSG WS_DBG_TTY_COLOR_BR_BLK
+#define WS_DBG_TTY_COLOR_TX WS_DBG_TTY_COLOR_GRN
+#define WS_DBG_TTY_COLOR_RX WS_DBG_TTY_COLOR_RED
+
+#define debugger asm("nop")
+
+// unused
+// #define WS_DBG_TTY_COLOR_CMD_ECHO WS_DBG_TTY_COLOR_BLK
+// #define WS_DBG_TTY_COLOR_STATUS_CODE WS_DBG_TTY_COLOR_YLW
+// #define WS_DBG_TTY_COLOR_IDLE WS_DBG_TTY_COLOR_VLT
+// #define WS_DBG_TTY_COLOR_IPD_LISTENING WS_DBG_TTY_COLOR_GRN
+// #define WS_DBG_TTY_COLOR_CIPSEND_LISTENING WS_DBG_TTY_COLOR_RED
+
+#define ws_usb_printf(fmt, ...) { \
+ char temp[255]; \
+ snprintf(temp, 255, fmt, ##__VA_ARGS__); \
+ HAL_UART_Transmit(&huart2, (uint8_t*) temp, sizeof(char) * strlen(temp), HAL_MAX_DELAY); \
+}
+
+#define ws_dbg_set_usart2_tty_color(color) { \
+ uint8_t sgr[] = { 0x1b, 0x5b, 0x33 + (color > 7 ? 6 : 0), 0x30 + (color & 0b111), 0x6d }; \
+ HAL_UART_Transmit(&huart2, sgr, sizeof(sgr), 100); \
+}
diff --git a/stm32f091/wifi.def.h b/stm32f091/wifi.def.h
new file mode 100644
index 0000000..61e1706
--- /dev/null
+++ b/stm32f091/wifi.def.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#define WS_ESP8266_WLAN_SSID "Test"
+#define WS_ESP8266_WLAN_PASSWD "12345678"
+// #define WS_ESP8266_WLAN_MAC "f2:9b:89:47:c4:f3"
+// #define WS_ESP8266_WLAN_IP "192.168.2.69"
+
diff --git a/todo.md b/todo.md
new file mode 100644
index 0000000..5924b20
--- /dev/null
+++ b/todo.md
@@ -0,0 +1,28 @@
+# things that have to get fixed before monday
+
+- [ ] more documentation in header files (for both client and stm code)
+- [ ] design/architecture document
+- [x] more tests in the test document
+- [x] handle errors from `AT+CIPSEND`, these look like this:
+ ```
+ > AT0,CONNECT
+
+ > +IPD,0,15:last-records 5<0a>
+ < AT+CIPSEND=0,125
+ > AT+CIPSEND=0,125
+
+ > ERROR
+ ```
+
+## `// TODO:`'s
+
+- [ ] `sensor.c:24: return (uint8_t) temp_c; //TODO: convert with range -> util.h`
+- [ ] `sensor.c:36: return (uint8_t) humidity; //TODO: convert with range -> util.h`
+- [ ] `sensor.c:51: return (uint8_t) val; // TODO: convert with range`
+- [x] `server.c:47:// TODO: next_few_bytes_are assumes that the complete search string is in the`
+- [ ] `server.c:146: // TODO: buffer overrun protection`
+- [x] `server.c:152:// TODO: refactor this`
+- [x] `server.c:165:// TODO: refactor this`
+- [x] `server.c:174:// TODO: refactor this`
+- [ ] `setup.c:95: // TODO: remove debug size`
+- [ ] `setup.c:187: .Pin = GPIO_PIN_8|GPIO_PIN_9, //TODO: use #defines in setup.h`