Protocol running.

This commit is contained in:
Holger Weber
2026-02-13 00:30:34 +01:00
parent 098c577142
commit 42e9c4d97b
6 changed files with 173 additions and 41 deletions

View File

@@ -114,5 +114,8 @@ String buildConfigJson();
bool applyConfigJson(const String &payload, String &errorMessage);
void startWebInterface();
void handleWebInterface();
void Log(const String &message);
void Log(const char *message);
String buildLogsJson(uint32_t sinceSeq);
#endif

View File

@@ -12,7 +12,7 @@
platform = platformio/espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
monitor_speed = 9600
upload_speed = 576000
upload_port = /dev/ttyUSB*
lib_deps =

View File

@@ -1,16 +1,12 @@
#include "EggDuino.h"
#include <ArduinoJson.h>
#include <DNSServer.h>
namespace {
const char *kConfigPath = "/config.json";
const char *kApSsid = "EggDuino";
const uint16_t kDnsPort = 53;
const IPAddress kApIp(192, 168, 4, 1);
const IPAddress kApSubnet(255, 255, 255, 0);
const char *kWifiSsid = "Sternenlabor";
const char *kWifiPassword = "!Sternenlabor99!";
WebServer server(80);
DNSServer dnsServer;
bool configStoreReady = false;
ConfigParameter *findParameter(const String &key) {
@@ -38,12 +34,14 @@ void handleRoot() {
<title>EggDuino Konfiguration</title>
<style>
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; }
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; }
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; }
#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>
</head>
<body>
@@ -52,8 +50,12 @@ button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding
<form id="cfgForm"></form>
<button id="saveBtn" type="button">Speichern</button>
<div id="status"></div>
<div id="logTitle">Logs</div>
<div id="log"></div>
</main>
<script>
let lastSeq = 0;
async function loadConfig() {
const resp = await fetch('/api/config');
if (!resp.ok) throw new Error('Konfiguration konnte nicht geladen werden');
@@ -93,6 +95,22 @@ async function saveConfig() {
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() {
const status = document.getElementById('status');
try {
@@ -109,6 +127,8 @@ async function saveConfig() {
status.textContent = e.message;
}
});
pollLogs();
setInterval(pollLogs, 800);
})();
</script>
</body>
@@ -118,14 +138,6 @@ async function saveConfig() {
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() {
if (!configStoreReady && !initConfigStore()) {
server.send(500, "text/plain", "Config storage not available");
@@ -160,6 +172,14 @@ void handlePostConfig() {
// penServo.write(penState);
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
ConfigParameter configParameters[] = {
@@ -284,28 +304,30 @@ bool applyConfigJson(const String &payload, String &errorMessage) {
}
void startWebInterface() {
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(kApIp, kApIp, kApSubnet);
WiFi.softAP(kApSsid);
dnsServer.start(kDnsPort, "*", WiFi.softAPIP());
WiFi.mode(WIFI_STA);
WiFi.begin(kWifiSsid, kWifiPassword);
const unsigned long connectStart = millis();
const unsigned long connectTimeoutMs = 20000;
while (WiFi.status() != WL_CONNECTED && millis() - connectStart < connectTimeoutMs) {
delay(250);
}
initConfigStore();
Serial.print("Config AP IP: ");
Serial.println(WiFi.softAPIP());
if (WiFi.status() == WL_CONNECTED) {
Serial.println(String("WLAN verbunden, IP: ") + WiFi.localIP().toString());
} else {
Serial.println("WLAN Verbindung fehlgeschlagen");
}
server.on("/", HTTP_GET, handleRoot);
server.on("/api/config", HTTP_GET, handleGetConfig);
server.on("/api/config", HTTP_POST, handlePostConfig);
server.on("/generate_204", HTTP_GET, redirectToPortal);
server.on("/gen_204", HTTP_GET, redirectToPortal);
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.on("/api/logs", HTTP_GET, handleGetLogs);
server.onNotFound(handleRoot);
server.begin();
}
void handleWebInterface() {
dnsServer.processNextRequest();
server.handleClient();
}

View File

@@ -121,6 +121,22 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
{
motorsOn();
}
if (duration == 0)
{
duration = 1;
}
if (penStepCorrection == 0)
{
penStepCorrection = 1;
}
if (rotStepCorrection == 0)
{
rotStepCorrection = 1;
}
if ((1 == rotStepCorrection) && (1 == penStepCorrection))
{ // if coordinatessystems are identical
// set Coordinates and Speed
@@ -132,7 +148,7 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
// penMotor.move(penStepsEBB);
// penMotor.setSpeed(abs((float)penStepsEBB * (float)1000 / (float)duration));
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
{
@@ -162,12 +178,14 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
// penMotor.move(penStepsToGo);
// penMotor.setSpeed(penSpeed);
g_pStepperPen->move(penStepsToGo);
g_pStepperRotate->setSpeedInTicks(penSpeed);
g_pStepperPen->setSpeedInTicks(penSpeed);
}
}
void moveOneStep()
{
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning())
;
// if (penMotor.distanceToGo() || rotMotor.distanceToGo())
// {
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....
@@ -177,6 +195,8 @@ void moveOneStep()
void moveToDestination()
{
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning())
;
// while (penMotor.distanceToGo() || rotMotor.distanceToGo())
// {
// penMotor.runSpeedToPosition(); // Moving.... moving... moving....

86
src/Logging.cpp Normal file
View 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;
}

View File

@@ -77,10 +77,10 @@ void setup()
{
Serial.begin(9600);
Serial.println("Starting...");
Log("Starting...");
makeComInterface();
initHardware();
startWebInterface();
}
uint8_t g_uiState = 0;
@@ -88,18 +88,18 @@ unsigned long g_uiLastTim = millis();
void loop()
{
#ifdef TEST
unsigned long uiNow = millis();
motorsOn();
if (uiNow - g_uiLastTim > 500)
if (uiNow - g_uiLastTim > 5000)
{
g_uiLastTim = uiNow;
switch (g_uiState)
{
case 0:
Serial.println(g_uiState);
Log(String(g_uiState));
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
g_pStepperRotate->setAcceleration(10000);
g_pStepperRotate->move(1000);
@@ -107,7 +107,7 @@ void loop()
break;
case 1:
Serial.println(g_uiState);
Log(String(g_uiState));
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
g_pStepperRotate->setAcceleration(10000);
g_pStepperRotate->move(-1000);
@@ -115,7 +115,7 @@ void loop()
break;
case 2:
Serial.println(g_uiState);
Log(String(g_uiState));
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
g_pStepperPen->setAcceleration(10000);
g_pStepperPen->move(1000);
@@ -123,7 +123,7 @@ void loop()
break;
case 3:
Serial.println(g_uiState);
Log(String(g_uiState));
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
g_pStepperPen->setAcceleration(10000);
g_pStepperPen->move(-1000);
@@ -133,6 +133,7 @@ void loop()
default:
break;
}
Log("Alive");
}
#endif
// moveOneStep();