Protocol running.
This commit is contained in:
@@ -114,5 +114,8 @@ String buildConfigJson();
|
|||||||
bool applyConfigJson(const String &payload, String &errorMessage);
|
bool applyConfigJson(const String &payload, String &errorMessage);
|
||||||
void startWebInterface();
|
void startWebInterface();
|
||||||
void handleWebInterface();
|
void handleWebInterface();
|
||||||
|
void Log(const String &message);
|
||||||
|
void Log(const char *message);
|
||||||
|
String buildLogsJson(uint32_t sinceSeq);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
platform = platformio/espressif32
|
platform = platformio/espressif32
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
monitor_speed = 115200
|
monitor_speed = 9600
|
||||||
upload_speed = 576000
|
upload_speed = 576000
|
||||||
upload_port = /dev/ttyUSB*
|
upload_port = /dev/ttyUSB*
|
||||||
lib_deps =
|
lib_deps =
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#include "EggDuino.h"
|
#include "EggDuino.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <DNSServer.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char *kConfigPath = "/config.json";
|
const char *kConfigPath = "/config.json";
|
||||||
const char *kApSsid = "EggDuino";
|
const char *kWifiSsid = "Sternenlabor";
|
||||||
const uint16_t kDnsPort = 53;
|
const char *kWifiPassword = "!Sternenlabor99!";
|
||||||
const IPAddress kApIp(192, 168, 4, 1);
|
|
||||||
const IPAddress kApSubnet(255, 255, 255, 0);
|
|
||||||
|
|
||||||
WebServer server(80);
|
WebServer server(80);
|
||||||
DNSServer dnsServer;
|
|
||||||
bool configStoreReady = false;
|
bool configStoreReady = false;
|
||||||
|
|
||||||
ConfigParameter *findParameter(const String &key) {
|
ConfigParameter *findParameter(const String &key) {
|
||||||
@@ -38,12 +34,14 @@ void handleRoot() {
|
|||||||
<title>EggDuino Konfiguration</title>
|
<title>EggDuino Konfiguration</title>
|
||||||
<style>
|
<style>
|
||||||
body { font-family: "Segoe UI", sans-serif; margin: 20px; background: #f3f6fb; color: #1a1a1a; }
|
body { font-family: "Segoe UI", sans-serif; margin: 20px; background: #f3f6fb; color: #1a1a1a; }
|
||||||
main { max-width: 560px; margin: 0 auto; background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
main { max-width: 760px; margin: 0 auto; background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||||
h1 { margin-top: 0; font-size: 1.35rem; }
|
h1 { margin-top: 0; font-size: 1.35rem; }
|
||||||
label { display: block; margin: 14px 0 6px; font-weight: 600; }
|
label { display: block; margin: 14px 0 6px; font-weight: 600; }
|
||||||
input[type='number'] { width: 100%; padding: 10px; border: 1px solid #c7d2e5; border-radius: 8px; box-sizing: border-box; }
|
input[type='number'] { width: 100%; padding: 10px; border: 1px solid #c7d2e5; border-radius: 8px; box-sizing: border-box; }
|
||||||
button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding: 10px 14px; border-radius: 8px; cursor: pointer; }
|
button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding: 10px 14px; border-radius: 8px; cursor: pointer; }
|
||||||
#status { margin-top: 12px; min-height: 1.2em; }
|
#status { margin-top: 12px; min-height: 1.2em; }
|
||||||
|
#log { margin-top: 20px; border: 1px solid #d6dfef; border-radius: 8px; background: #0f172a; color: #d2e3ff; padding: 10px; height: 220px; overflow-y: auto; white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.9rem; }
|
||||||
|
#logTitle { margin-top: 24px; margin-bottom: 8px; font-weight: 700; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -52,8 +50,12 @@ button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding
|
|||||||
<form id="cfgForm"></form>
|
<form id="cfgForm"></form>
|
||||||
<button id="saveBtn" type="button">Speichern</button>
|
<button id="saveBtn" type="button">Speichern</button>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
|
<div id="logTitle">Logs</div>
|
||||||
|
<div id="log"></div>
|
||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
|
let lastSeq = 0;
|
||||||
|
|
||||||
async function loadConfig() {
|
async function loadConfig() {
|
||||||
const resp = await fetch('/api/config');
|
const resp = await fetch('/api/config');
|
||||||
if (!resp.ok) throw new Error('Konfiguration konnte nicht geladen werden');
|
if (!resp.ok) throw new Error('Konfiguration konnte nicht geladen werden');
|
||||||
@@ -93,6 +95,22 @@ async function saveConfig() {
|
|||||||
status.textContent = 'Gespeichert';
|
status.textContent = 'Gespeichert';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function pollLogs() {
|
||||||
|
const box = document.getElementById('log');
|
||||||
|
const resp = await fetch('/api/logs?since=' + lastSeq);
|
||||||
|
if (!resp.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const payload = await resp.json();
|
||||||
|
(payload.logs || []).forEach(entry => {
|
||||||
|
box.textContent += entry.text + '\n';
|
||||||
|
});
|
||||||
|
if (typeof payload.lastSeq === 'number') {
|
||||||
|
lastSeq = payload.lastSeq;
|
||||||
|
}
|
||||||
|
box.scrollTop = box.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
(async function init() {
|
(async function init() {
|
||||||
const status = document.getElementById('status');
|
const status = document.getElementById('status');
|
||||||
try {
|
try {
|
||||||
@@ -109,6 +127,8 @@ async function saveConfig() {
|
|||||||
status.textContent = e.message;
|
status.textContent = e.message;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
pollLogs();
|
||||||
|
setInterval(pollLogs, 800);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
@@ -118,14 +138,6 @@ async function saveConfig() {
|
|||||||
server.send(200, "text/html", kPage);
|
server.send(200, "text/html", kPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void redirectToPortal() {
|
|
||||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
||||||
server.sendHeader("Pragma", "no-cache");
|
|
||||||
server.sendHeader("Expires", "-1");
|
|
||||||
server.sendHeader("Location", String("http://") + WiFi.softAPIP().toString() + "/", true);
|
|
||||||
server.send(302, "text/plain", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleGetConfig() {
|
void handleGetConfig() {
|
||||||
if (!configStoreReady && !initConfigStore()) {
|
if (!configStoreReady && !initConfigStore()) {
|
||||||
server.send(500, "text/plain", "Config storage not available");
|
server.send(500, "text/plain", "Config storage not available");
|
||||||
@@ -160,6 +172,14 @@ void handlePostConfig() {
|
|||||||
// penServo.write(penState);
|
// penServo.write(penState);
|
||||||
server.send(200, "application/json", buildConfigJson());
|
server.send(200, "application/json", buildConfigJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleGetLogs() {
|
||||||
|
uint32_t since = 0;
|
||||||
|
if (server.hasArg("since")) {
|
||||||
|
since = static_cast<uint32_t>(server.arg("since").toInt());
|
||||||
|
}
|
||||||
|
server.send(200, "application/json", buildLogsJson(since));
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ConfigParameter configParameters[] = {
|
ConfigParameter configParameters[] = {
|
||||||
@@ -284,28 +304,30 @@ bool applyConfigJson(const String &payload, String &errorMessage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startWebInterface() {
|
void startWebInterface() {
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.softAPConfig(kApIp, kApIp, kApSubnet);
|
WiFi.begin(kWifiSsid, kWifiPassword);
|
||||||
WiFi.softAP(kApSsid);
|
|
||||||
dnsServer.start(kDnsPort, "*", WiFi.softAPIP());
|
const unsigned long connectStart = millis();
|
||||||
|
const unsigned long connectTimeoutMs = 20000;
|
||||||
|
while (WiFi.status() != WL_CONNECTED && millis() - connectStart < connectTimeoutMs) {
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
|
||||||
initConfigStore();
|
initConfigStore();
|
||||||
Serial.print("Config AP IP: ");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
Serial.println(WiFi.softAPIP());
|
Serial.println(String("WLAN verbunden, IP: ") + WiFi.localIP().toString());
|
||||||
|
} else {
|
||||||
|
Serial.println("WLAN Verbindung fehlgeschlagen");
|
||||||
|
}
|
||||||
|
|
||||||
server.on("/", HTTP_GET, handleRoot);
|
server.on("/", HTTP_GET, handleRoot);
|
||||||
server.on("/api/config", HTTP_GET, handleGetConfig);
|
server.on("/api/config", HTTP_GET, handleGetConfig);
|
||||||
server.on("/api/config", HTTP_POST, handlePostConfig);
|
server.on("/api/config", HTTP_POST, handlePostConfig);
|
||||||
server.on("/generate_204", HTTP_GET, redirectToPortal);
|
server.on("/api/logs", HTTP_GET, handleGetLogs);
|
||||||
server.on("/gen_204", HTTP_GET, redirectToPortal);
|
server.onNotFound(handleRoot);
|
||||||
server.on("/hotspot-detect.html", HTTP_GET, redirectToPortal);
|
|
||||||
server.on("/library/test/success.html", HTTP_GET, redirectToPortal);
|
|
||||||
server.on("/ncsi.txt", HTTP_GET, redirectToPortal);
|
|
||||||
server.on("/connecttest.txt", HTTP_GET, redirectToPortal);
|
|
||||||
server.onNotFound(redirectToPortal);
|
|
||||||
server.begin();
|
server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleWebInterface() {
|
void handleWebInterface() {
|
||||||
dnsServer.processNextRequest();
|
|
||||||
server.handleClient();
|
server.handleClient();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,22 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
|
|||||||
{
|
{
|
||||||
motorsOn();
|
motorsOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (duration == 0)
|
||||||
|
{
|
||||||
|
duration = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (penStepCorrection == 0)
|
||||||
|
{
|
||||||
|
penStepCorrection = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotStepCorrection == 0)
|
||||||
|
{
|
||||||
|
rotStepCorrection = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ((1 == rotStepCorrection) && (1 == penStepCorrection))
|
if ((1 == rotStepCorrection) && (1 == penStepCorrection))
|
||||||
{ // if coordinatessystems are identical
|
{ // if coordinatessystems are identical
|
||||||
// set Coordinates and Speed
|
// set Coordinates and Speed
|
||||||
@@ -132,7 +148,7 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
|
|||||||
// penMotor.move(penStepsEBB);
|
// penMotor.move(penStepsEBB);
|
||||||
// penMotor.setSpeed(abs((float)penStepsEBB * (float)1000 / (float)duration));
|
// penMotor.setSpeed(abs((float)penStepsEBB * (float)1000 / (float)duration));
|
||||||
g_pStepperPen->move(penStepsEBB);
|
g_pStepperPen->move(penStepsEBB);
|
||||||
g_pStepperRotate->setSpeedInTicks(abs((float)penStepsEBB * (float)1000 / (float)duration));
|
g_pStepperPen->setSpeedInTicks(abs((float)penStepsEBB * (float)1000 / (float)duration));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -162,12 +178,14 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
|
|||||||
// penMotor.move(penStepsToGo);
|
// penMotor.move(penStepsToGo);
|
||||||
// penMotor.setSpeed(penSpeed);
|
// penMotor.setSpeed(penSpeed);
|
||||||
g_pStepperPen->move(penStepsToGo);
|
g_pStepperPen->move(penStepsToGo);
|
||||||
g_pStepperRotate->setSpeedInTicks(penSpeed);
|
g_pStepperPen->setSpeedInTicks(penSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void moveOneStep()
|
void moveOneStep()
|
||||||
{
|
{
|
||||||
|
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning())
|
||||||
|
;
|
||||||
// if (penMotor.distanceToGo() || rotMotor.distanceToGo())
|
// if (penMotor.distanceToGo() || rotMotor.distanceToGo())
|
||||||
// {
|
// {
|
||||||
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
||||||
@@ -177,6 +195,8 @@ void moveOneStep()
|
|||||||
|
|
||||||
void moveToDestination()
|
void moveToDestination()
|
||||||
{
|
{
|
||||||
|
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning())
|
||||||
|
;
|
||||||
// while (penMotor.distanceToGo() || rotMotor.distanceToGo())
|
// while (penMotor.distanceToGo() || rotMotor.distanceToGo())
|
||||||
// {
|
// {
|
||||||
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
||||||
|
|||||||
86
src/Logging.cpp
Normal file
86
src/Logging.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "EggDuino.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr size_t kLogCapacity = 80;
|
||||||
|
constexpr size_t kLogLineLength = 160;
|
||||||
|
|
||||||
|
char g_logLines[kLogCapacity][kLogLineLength];
|
||||||
|
uint32_t g_logSeq[kLogCapacity];
|
||||||
|
size_t g_logWritePos = 0;
|
||||||
|
uint32_t g_nextLogSeq = 1;
|
||||||
|
|
||||||
|
void appendJsonEscaped(String &out, const char *text) {
|
||||||
|
out += "\"";
|
||||||
|
for (size_t i = 0; text[i] != '\0'; ++i) {
|
||||||
|
const char c = text[i];
|
||||||
|
switch (c) {
|
||||||
|
case '\\':
|
||||||
|
out += "\\\\";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
out += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
out += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
out += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
out += "\\t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (static_cast<unsigned char>(c) < 0x20) {
|
||||||
|
out += '?';
|
||||||
|
} else {
|
||||||
|
out += c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += "\"";
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Log(const String &message) {
|
||||||
|
const String trimmed = message.substring(0, kLogLineLength - 1);
|
||||||
|
trimmed.toCharArray(g_logLines[g_logWritePos], kLogLineLength);
|
||||||
|
g_logSeq[g_logWritePos] = g_nextLogSeq++;
|
||||||
|
g_logWritePos = (g_logWritePos + 1) % kLogCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log(const char *message) {
|
||||||
|
Log(String(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildLogsJson(uint32_t sinceSeq) {
|
||||||
|
String output;
|
||||||
|
output.reserve(2048);
|
||||||
|
output += "{\"logs\":[";
|
||||||
|
uint32_t lastSeq = sinceSeq;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < kLogCapacity; ++i) {
|
||||||
|
const size_t idx = (g_logWritePos + i) % kLogCapacity;
|
||||||
|
const uint32_t seq = g_logSeq[idx];
|
||||||
|
if (seq == 0 || seq <= sinceSeq) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!first) {
|
||||||
|
output += ",";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
output += "{\"seq\":";
|
||||||
|
output += String(seq);
|
||||||
|
output += ",\"text\":";
|
||||||
|
appendJsonEscaped(output, g_logLines[idx]);
|
||||||
|
output += "}";
|
||||||
|
lastSeq = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += "],\"lastSeq\":";
|
||||||
|
output += String(lastSeq);
|
||||||
|
output += "}";
|
||||||
|
return output;
|
||||||
|
}
|
||||||
17
src/main.cpp
17
src/main.cpp
@@ -77,10 +77,10 @@ void setup()
|
|||||||
{
|
{
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
Serial.println("Starting...");
|
Serial.println("Starting...");
|
||||||
|
Log("Starting...");
|
||||||
makeComInterface();
|
makeComInterface();
|
||||||
initHardware();
|
initHardware();
|
||||||
startWebInterface();
|
startWebInterface();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t g_uiState = 0;
|
uint8_t g_uiState = 0;
|
||||||
@@ -88,18 +88,18 @@ unsigned long g_uiLastTim = millis();
|
|||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
|
|
||||||
unsigned long uiNow = millis();
|
unsigned long uiNow = millis();
|
||||||
motorsOn();
|
motorsOn();
|
||||||
|
|
||||||
if (uiNow - g_uiLastTim > 500)
|
if (uiNow - g_uiLastTim > 5000)
|
||||||
{
|
{
|
||||||
g_uiLastTim = uiNow;
|
g_uiLastTim = uiNow;
|
||||||
switch (g_uiState)
|
switch (g_uiState)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
Serial.println(g_uiState);
|
Log(String(g_uiState));
|
||||||
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
g_pStepperRotate->setAcceleration(10000);
|
g_pStepperRotate->setAcceleration(10000);
|
||||||
g_pStepperRotate->move(1000);
|
g_pStepperRotate->move(1000);
|
||||||
@@ -107,7 +107,7 @@ void loop()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
Serial.println(g_uiState);
|
Log(String(g_uiState));
|
||||||
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
g_pStepperRotate->setAcceleration(10000);
|
g_pStepperRotate->setAcceleration(10000);
|
||||||
g_pStepperRotate->move(-1000);
|
g_pStepperRotate->move(-1000);
|
||||||
@@ -115,7 +115,7 @@ void loop()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
Serial.println(g_uiState);
|
Log(String(g_uiState));
|
||||||
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
g_pStepperPen->setAcceleration(10000);
|
g_pStepperPen->setAcceleration(10000);
|
||||||
g_pStepperPen->move(1000);
|
g_pStepperPen->move(1000);
|
||||||
@@ -123,7 +123,7 @@ void loop()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
Serial.println(g_uiState);
|
Log(String(g_uiState));
|
||||||
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
g_pStepperPen->setAcceleration(10000);
|
g_pStepperPen->setAcceleration(10000);
|
||||||
g_pStepperPen->move(-1000);
|
g_pStepperPen->move(-1000);
|
||||||
@@ -133,9 +133,10 @@ void loop()
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Log("Alive");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
//moveOneStep();
|
// moveOneStep();
|
||||||
SCmd.readSerial();
|
SCmd.readSerial();
|
||||||
handleWebInterface();
|
handleWebInterface();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user