Dodanie Captive Portal z logarytmami

This commit is contained in:
Victus
2026-05-10 20:46:23 +02:00
parent 0985792a06
commit 66475edad4
17 changed files with 776 additions and 316 deletions

View File

@@ -21,7 +21,7 @@ public:
ADXL345FreshSPI() = default;
// --- Init (SPI only) ---
// --- Init (SPI) ---
// Uwaga: ADXL345 wymaga SPI MODE3, zegar ≤ ~5 MHz.
bool begin(SPIClass *spi, uint8_t csPin, uint32_t clockHz = 5000000);

19
include/APIClient.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef APICLIENT_H
#define APICLIENT_H
#include <Arduino.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
#include <FS.h>
#include <SD.h>
#include "Config.h"
#include "Watchdog.h"
class APIClient {
public:
APIClient();
bool uploadMeasurement(const String& filePath);
};
#endif

View File

@@ -60,10 +60,6 @@ public:
void setCursor(int16_t x, int16_t y);
void showAccel(float a, float b, float c);
void displayOnOffM(bool measure, String myDir, String myFile);
// Metoda sterująca ikoną SSL (dodana do klasy)
void setSSLStatus(bool active);
void initMeasure(
bool measure, // czy pomiar ciągły?
bool run, // czy uruchomiony?
@@ -78,9 +74,20 @@ public:
private:
LiquidCrystal_I2C *_lcd;
RTC_DS3231 &rtc_;
uint8_t _address;
uint8_t _columns;
uint8_t _rows;
uint8_t _address;
// Poprzednie
uint16_t oyear;
uint8_t omonth;
uint8_t oday;
uint8_t ohour;
uint8_t omin;
uint8_t osec;
float ospace;
uint8_t oadxlcnt;
bool omode;
};
#endif
#endif

View File

@@ -17,8 +17,6 @@ static constexpr uint32_t SPI_HZ = 5000000; // 5 MHz (MODE3)
static constexpr float ODR_HZ = 3200.0f; // maks. ODR
// Zakres ustawiany w main.cpp przez ADXL345FastSPI::begin(..., RANGE_2G, ...)
//extern Display display;
struct FileInfo {
String path; // np. "/3/00000057.wmt"
uint64_t size; // bajty
@@ -31,7 +29,7 @@ struct SpaceInfo {
};
class DataCapture {
// --- Nagłówek pliku WMT (jak w oryginale) ---
// --- Nagłówek pliku WMT ---
struct FileHeader {
char magic[3]; // "WMT"
uint16_t version; // 1
@@ -42,7 +40,7 @@ class DataCapture {
} __attribute__((packed));
public:
// --- Rekord próbki (jak w oryginale) ---
// --- Rekord próbki ---
struct Sample {
uint32_t offset; // µs od startu akwizycji (wspólny dla ramki)
uint8_t sensor_id; // 0..3
@@ -105,8 +103,6 @@ private:
return ispress;
} // szybkidigitalRead : czy BTN stop???
// Helpers (zachowane z Twojej wersji)
public:
bool isAllDigits(const char *s);
uint32_t toUint(const char *s);

View File

@@ -9,13 +9,12 @@
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <DNSServer.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#endif
#include <DNSServer.h> // Dodano dla Captive Portal
//#include <Pinout.h>
//#include <IPAddress.h>
//#include <WiFiUdp.h>
@@ -28,11 +27,6 @@
#include <functional>
// OTA
#include "Display.h" // Dodano dla obsługi komunikatów na LCD
extern Config config; // Deklaracja zewnętrznej struktury config
extern ConfigManager configManager; // Deklaracja zewnętrznego managera (to naprawi błąd)
class WiFiManager {
public:
WiFiManager();
@@ -50,11 +44,11 @@ class WiFiManager {
int rssiToPercent(int rssi); // rssi na procenty
int8_t getRSSI();
/**
* NOWA METODA: Portal konfiguracyjny (Captive Portal)
* Uruchamiany automatycznie przy braku połączenia.
*/
void startConfigPortal(Display &display);
void handleClient();
void startCaptivePortal();
void handleRoot();
void handleSave();
void handleNotFound();
/**
* Aktualizacja systemu przez internet z adresu config.updateUrl.
@@ -75,18 +69,10 @@ class WiFiManager {
private:
bool isAccessPoint = false;
// Obiekty serwerów dla Portalu (ESP32/ESP8266)
#ifdef ESP32
bool captivePortalActive = false;
WebServer server{80};
#elif defined(ESP8266)
ESP8266WebServer server{80};
#endif
DNSServer dnsServer;
void handleRoot();
void handleSave();
int expectedCaptchaAnswer = 0;
};
extern WiFiManager wifi;
#endif // WIFIMANAGER_H
#endif // WIFIMANAGER_H

View File

@@ -24,7 +24,7 @@ class Settings {
// bool is1and3();
bool isBtnReset();
bool isSetClock();
bool readBtnUp() { return digitalRead(BTN_UP) == LOW; }
bool readBtnOk() { return digitalRead(BTN_OK) == LOW; }
bool readBtnDown() { return digitalRead(BTN_DOWN) == LOW; }

31
include/UploadManager.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef UPLOADMANAGER_H
#define UPLOADMANAGER_H
#include <Arduino.h>
#include <FS.h>
#include <SD.h>
#include "APIClient.h"
#include "RTClib.h"
#include "Measure.h"
class UploadManager {
public:
UploadManager(APIClient& client, RTC_DS3231& rtc, DataCapture& capture);
// Call this to upload a specific file immediately
void uploadFile(const String& filePath);
// Call this in the background when WiFi is connected
void processPendingUploads();
private:
APIClient& apiClient;
RTC_DS3231& rtc_;
DataCapture& capture_;
bool isAlreadyUploaded(const String& filePath);
void appendLog(const String& filePath, const String& status);
String getCurrentTimestamp();
};
#endif

View File

@@ -1,7 +1,7 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION "1.3.2"
#define VERSION "1.3.4.1"
// 1: graphical 128x64, 2: LCD I2C Text 4x20
#define LCD_TYPE 2

View File

@@ -1,4 +1,5 @@
#include "ADXL345FastSPI.h"
#include "Watchdog.h"
static const char *TAG_FRESH = "ADXLFAST";
@@ -43,8 +44,14 @@ bool ADXL345FastSPI::availableAll() {
uint8_t ADXL345FastSPI::readAlignedOnce(int16_t* x, int16_t* y, int16_t* z, uint32_t* ts_us){
// 1) Czekaj aż każdy ma ≥1 próbkę (DATA_READY => FIFO>0)
uint32_t start_wait = millis();
while (!availableAll()) {
// micro-spin; w razie potrzeby można dodać yield() / feed watchdog poza hot-path
if (millis() - start_wait > 100) {
ESP_LOGE(TAG_FRESH, "Timeout waiting for sensors data!");
return 0;
}
Watchdog::feed();
yield();
}
// 2) Zdejmij najstarszą z każdego sensora

124
src/APIClient.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include "APIClient.h"
#include <WiFiClient.h>
#include <base64.h>
static const char *TAG_API = "API";
APIClient::APIClient() {}
bool APIClient::uploadMeasurement(const String &filePath) {
if (WiFi.status() != WL_CONNECTED) {
ESP_LOGE(TAG_API, "No WiFi connection.");
return false;
}
File file = SD.open(filePath, FILE_READ);
if (!file) {
ESP_LOGE(TAG_API, "Failed to open file %s", filePath.c_str());
return false;
}
// Wyciągnij samą nazwę pliku (bez ścieżki)
String filename = filePath;
int slashIndex = filename.lastIndexOf('/');
if (slashIndex >= 0) {
filename = filename.substring(slashIndex + 1);
}
// multipart/form-data boundary
String boundary = "----ESP32WMTBoundary";
String head = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"" +
filename + "\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n";
String tail = "\r\n--" + boundary + "--\r\n";
size_t fileSize = file.size();
size_t totalLength = head.length() + fileSize + tail.length();
// Połączenie z hostem
WiFiClient client;
String host = String(config.restURL);
int port = config.restPort;
// Usuń prefiks protokołu z hosta
host.replace("http://", "");
host.replace("https://", "");
ESP_LOGI(TAG_API, "Connecting to %s:%d for upload", host.c_str(), port);
if (!client.connect(host.c_str(), port)) {
ESP_LOGE(TAG_API, "Connection failed to host %s:%d", host.c_str(), port);
file.close();
return false;
}
ESP_LOGI(TAG_API, "Uploading %s (%d bytes)", filePath.c_str(), fileSize);
// --- HTTP Request ---
// Endpoint wg dokumentacji API IoT
client.println("POST /api/v1/measurements/measurements/upload HTTP/1.1");
client.println("Host: " + host + ":" + String(port));
// Basic Auth: base64(serial:password)
String auth = String(config.restUser) + ":" + String(config.restPass);
String authBase64 = base64::encode(auth);
client.println("Authorization: Basic " + authBase64);
client.println("Content-Length: " + String(totalLength));
client.println("Content-Type: multipart/form-data; boundary=" + boundary);
client.println("Connection: close");
client.println();
// Multipart body: head + file data + tail
client.print(head);
uint8_t buffer[2048];
while (file.available()) {
size_t len = file.read(buffer, sizeof(buffer));
client.write(buffer, len);
Watchdog::feed();
}
client.print(tail);
file.close();
// --- Parsowanie odpowiedzi ---
int httpCode = 0;
String responseLine;
unsigned long timeout = millis();
while (client.connected() && millis() - timeout < 15000) {
if (client.available()) {
responseLine = client.readStringUntil('\n');
responseLine.trim();
if (responseLine.startsWith("HTTP/1.1 ")) {
httpCode = responseLine.substring(9, 12).toInt();
}
if (responseLine.length() == 0)
break;
}
Watchdog::feed();
}
String responseBody = "";
while (client.available()) {
responseBody += client.readString();
}
client.stop();
// --- Obsługa kodów odpowiedzi wg dokumentacji API ---
if (httpCode == 201) {
ESP_LOGI(TAG_API, "Upload successful: 201 Created");
return true;
} else if (httpCode == 401) {
ESP_LOGE(TAG_API, "Unauthorized 401: Wrong serial or password!");
} else if (httpCode == 403) {
ESP_LOGE(TAG_API, "Forbidden 403: Device not authorized or user tried upload.");
} else if (httpCode == 400) {
ESP_LOGE(TAG_API, "Bad Request 400: %s", responseBody.c_str());
} else {
ESP_LOGE(TAG_API, "Upload failed with code %d. Response: %s", httpCode,
responseBody.c_str());
}
return false;
}

View File

@@ -22,7 +22,7 @@ void ConfigManager::begin() {
// Check is EEPROM is empty
bool ConfigManager::isEEPROMEmpty() {
if (EEPROM.read(0) != 251){
if (EEPROM.read(0) != 253){
ESP_LOGI(TAG_CONF, "EEPROM is new!");
return true;
} else {
@@ -40,7 +40,7 @@ void ConfigManager::readConfig() {
void ConfigManager::saveConfig() {
ESP_LOGI(TAG_CONF, "SAVE CONFIG");
EEPROM.put(1, config);
EEPROM.write(0, 251);
EEPROM.write(0, 253);
if (EEPROM.commit()) {
ESP_LOGI(TAG_CONF, "Config saved");
} else {
@@ -77,15 +77,15 @@ void ConfigManager::resetToDefaults() {
strcpy(config.user, "admin");
strcpy(config.pass, "admin");
strcpy(config.ntp, "pl.pool.ntp.org");
config.connect = 0; // urządzenie połączone z siecią lub 0 offline
config.connect = 1; // urządzenie połączone z siecią lub 0 offline
config.measure = 1; // włącz automatyczny pomiar co x sekunt (pause)
config.duration = 5; // czas trwania pomiaru 5 sekund
config.pause = 10000; // odstęp pomiędzy pomiarami w ms
//strcpy(config.ntp, "0.pl.pool.ntp.org");
strcpy(config.restURL, "http://62.93.60.19/accels/api1");
config.restPort = 8000;
strcpy(config.restUser, "wmt");
strcpy(config.restPass, "Zaq12wsx");
strcpy(config.restURL, "http://62.93.60.19");
config.restPort = 5004;
strcpy(config.restUser, "SN001234ABCD56789012");
strcpy(config.restPass, "device001");
strcpy(config.S0, "ACCEL1");
strcpy(config.S1, "ACCEL2");
strcpy(config.S2, "ACCEL3");
@@ -95,7 +95,7 @@ void ConfigManager::resetToDefaults() {
strcpy(config.S6, "ACCEL7");
strcpy(config.S7, "ACCEL8");
EEPROM.put(1, config);
EEPROM.write(0, 251);
EEPROM.write(0, 253);
saveConfig();
readConfig();
isRebootRequired = true;

View File

@@ -3,9 +3,6 @@
static const char *DISP = "display";
// Flaga informująca o aktywnym trybie SSL
static bool _sslActive = false;
Display::Display(RTC_DS3231 &rtc, uint8_t address, uint8_t columns, uint8_t rows):rtc_(rtc) {
_lcd = new LiquidCrystal_I2C(address, columns, rows);
_address = address;
@@ -28,9 +25,6 @@ bool Display::begin(TwoWire *wire) {
return true;
}
void Display::setSSLStatus(bool active) {
_sslActive = active;
}
void Display::initMeasure(bool measure, bool run, bool runmes, uint16_t pause, uint8_t duration,
bool connect, long counter, String gdate, String gtime) {
@@ -43,54 +37,97 @@ void Display::initMeasure(bool measure, bool run, bool runmes, uint16_t pause, u
String type = "Sample:" + String(duration) + "s Pause:" + String(pause/1000)+"s";
textCenter(2, type.c_str());
textCenter(3, String("Time:" + gtime).c_str());
}
void Display::welcomeScreen() {
_lcd->clear();
_lcd->setCursor(0, 0);
_lcd->print(" VICTUS SYSTEM ");
_lcd->setCursor(0, 1);
_lcd->print(" VIBRATION LOGGER ");
_lcd->setCursor(0, 2);
_lcd->print(" V " + String(VERSION) + " ");
_lcd->setCursor(0, 3);
_lcd->print("WMT Stalowa Wola '25");
}
void Display::clearRow(uint16_t line) {
_lcd->setCursor(0, line);
for (int i = 0; i < _columns; i++) {
_lcd->print(" ");
}
}
void Display::textCenter(uint8_t line, const char *txt) {
int len = strlen(txt);
int pos = (_columns - len) / 2;
if (pos < 0) pos = 0;
_lcd->setCursor(pos, line);
_lcd->print(txt);
}
void Display::text(uint8_t line, const char *text) {
_lcd->setCursor(0, line);
_lcd->print(text);
}
void Display::textStatus(const char *text) {
clearRow(3);
_lcd->setCursor(0, 3);
_lcd->print(text);
textStatus("initialisation");
}
void Display::clear() {
_lcd->clear();
_lcd->clear();
}
void Display::mainScreen() {
// Twoja logika mainScreen
_lcd->clear();
textCenter(1, "MAIN SCREEN");
void Display::textCenter(uint8_t line, const char *txt){
if (line >= _rows) {
ESP_LOGE(DISP, "Line >= max rows!");
return;
}
_lcd->setCursor(0, line);
int len = strlen(txt);
if (len < _columns) {
int pad = (_columns - len) / 2;
for (int i = 0; i < pad; i++) {
_lcd->print(" ");
}
}
_lcd->print(txt);
}
void Display::text(uint8_t line, const char *text) {
if (line >= _rows) {
ESP_LOGE(DISP, "Line >= max rows!");
return;
}
_lcd->setCursor(0, line);
_lcd->print(text);
}
void Display::welcomeScreen() {
ESP_LOGI(DISP, "Info screen");
clear();
textCenter(0, "** Vibra Dude **");
textCenter(1, "WMT Stalowa Wola");
String firm = String(VERSION);
firm.trim();
textCenter(2, firm.c_str());
//_lcd->print(VERSION);
}
void Display::mainScreen(){
ESP_LOGI(DISP, "Main screen");
clear();
textCenter(0, "** Vibra Dude **");
textCenter(1, "WMT Stalowa Wola");
//String firm = String(VERSION).trim();
//textCenter(2, firm.c_str());
//_lcd->print(VERSION);
ESP_LOGI(DISP, "Finish main screen");
}
void Display::clearRow(uint16_t line){
_lcd->setCursor(0, line);
_lcd->print(" ");
_lcd->setCursor(0, line);
}
void Display::textStatus(const char *text){
clearRow(3);
_lcd->setCursor(0, 3);
_lcd->print(text);
}
void Display::updateNetwork(String ip, bool connected) {
// przykładowa prosta implementacja:
// clear();
// text(0, connected ? "NET: OK" : "NET: OFF");
// _lcd->setCursor(0, 1);
// _lcd->print(ip);
}
void Display::updateBarWiFi(long signal, bool connected) {
// tu możesz wykorzystać np. init_bargraph / draw_horizontal_graph
// (void)signal;
// (void)connected;
}
void Display::textStyle(const uint8_t st, String text) {
// miejsce na różne style tekstu
//(void)st;
// clear();
//text(0, text.c_str());
}
void Display::message(String text) {
// clear();
//text(0, text.c_str());
}
void Display::print(String text) {
@@ -99,77 +136,96 @@ void Display::print(String text) {
void Display::println(String text) {
_lcd->print(text);
_lcd->print("\n");
}
void Display::setCursor(int16_t x, int16_t y) {
_lcd->setCursor(x, y);
}
void Display::updateNetwork(String ip, bool connected) {
_lcd->setCursor(0, 0);
if (connected) {
_lcd->print("IP:" + ip);
} else {
_lcd->print("IP: DISCONNECTED ");
}
}
void Display::updateBarWiFi(long signal, bool connected) {
_lcd->setCursor(17, 0);
if (connected) {
if (signal > -60) _lcd->print("(((");
else if (signal > -80) _lcd->print(" ((");
else _lcd->print(" (");
} else {
_lcd->print(" ");
}
}
void Display::textStyle(const uint8_t st, String text) {
// Twoja implementacja stylów (np. bold/inwersja jeśli używasz)
_lcd->print(text);
}
void Display::message(String text) {
clear();
textCenter(1, text.c_str());
delay(2000);
_lcd->setCursor((uint8_t)x, (uint8_t)y);
}
void Display::showAccel(float a, float b, float c) {
_lcd->setCursor(0, 1);
_lcd->print("X:"); _lcd->print(a, 2);
_lcd->setCursor(0, 2);
_lcd->print("Y:"); _lcd->print(b, 2);
_lcd->setCursor(0, 3);
_lcd->print("Z:"); _lcd->print(c, 2);
clear();
// _lcd->setCursor(0, 0);
// _lcd->print("Ax: ");
// _lcd->print(a, 2);
// _lcd->setCursor(0, 1);
// _lcd->print("Ay: ");
// _lcd->print(b, 2);
// _lcd->setCursor(0, 2);
// _lcd->print("Az: ");
// _lcd->print(c, 2);
}
void Display::displayOffline(bool measure, uint8_t count, float freeSpace, long licznik, bool refresh) {
static uint8_t oadxlcnt = 255;
static bool omode = false;
static long last_tick = 0;
if (refresh) clear();
DateTime now = rtc_.now();
char buf[21];
snprintf(buf, sizeof(buf), "%02d:%02d:%02d %02d/%02d", now.hour(), now.minute(), now.second(), now.day(), now.month());
_lcd->setCursor(0, 0);
_lcd->print(buf);
// Znacznik SSL "S"
if (_sslActive) {
_lcd->setCursor(19, 0);
_lcd->print("S");
void Display::displayOffline(bool measure, uint8_t count, float freeSpace, long licznik, bool refresh){
if(refresh){
oyear, omonth, oday, ohour, omin, osec, ospace, oadxlcnt = 100;
_lcd->clear();
_lcd->setCursor(0, 0);
}
_lcd->setCursor(0, 1);
_lcd->print("SD FREE: ");
_lcd->print(freeSpace, 2);
_lcd->print(" GB ");
DateTime now = rtc_.now();
uint16_t year = now.year();
uint8_t month = now.month();
uint8_t day = now.day();
uint8_t hour = now.hour();
uint8_t min = now.minute();
uint8_t sec = now.second();
if ((oadxlcnt != count) || (omode != config.measure) || (refresh)){
// DEBUG
//ESP_LOGI(DISP, "RTC: %d:%d:%d %d:%d:%d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
//ESP_LOGI(DISP, "RTC: %d:%d:%d %d:%d:%d", oday, omonth, oyear, ohour, omin, osec);
if ((hour != ohour) || (refresh)) {
ohour = hour;
_lcd->setCursor(0, 0);
if (hour < 10) _lcd->print('0');
_lcd->print(hour);
_lcd->setCursor(2, 0);
_lcd->print(':');
}
if ((min != omin) || (refresh)) {
omin = min;
_lcd->setCursor(3, 0);
if (min < 10) _lcd->print('0');
_lcd->print(min);
_lcd->print(':');
}
if ((sec != osec) || (refresh)) {
_lcd->setCursor(6, 0);
osec = sec;
if (sec < 10) _lcd->print('0');
_lcd->print(sec);
}
if ((day != oday || month != omonth || year != oyear) || (refresh)){
_lcd->setCursor(10, 0);
if (day < 10) _lcd->print('0');
_lcd->print(day);
_lcd->print('.');
if (month < 10) _lcd->print('0');
_lcd->print(month);
_lcd->print('.');
_lcd->print(year);
oday = day;
omonth = month;
oyear = year;
if(!config.measure)
textStatus("OK: Start MEASURE");
else
textStatus("Wait for NEXT");
}
if((freeSpace != ospace) || (refresh)){
ospace = freeSpace;
_lcd->setCursor(0, 1);
_lcd->print("FREE:");
_lcd->print(freeSpace);
_lcd->print("GB");
}
if((count != oadxlcnt) || (omode != config.measure) || (refresh)){
oadxlcnt = count;
omode = config.measure;
_lcd->setCursor(0, 2);
@@ -178,25 +234,24 @@ void Display::displayOffline(bool measure, uint8_t count, float freeSpace, long
_lcd->print(" MODE:");
_lcd->print(config.measure ? "AUTO ":"MANUAL ");
}
if (millis() - last_tick > 1000) {
last_tick = millis();
_lcd->setCursor(0, 3);
_lcd->print("SYSTEM READY ");
_lcd->print(licznik % 2 == 0 ? "*" : " ");
}
}
// Podczas pomiaru
void Display::displayOnOffM(bool measure, String myDir, String myFile) {
clear();
_lcd->setCursor(0, 0);
_lcd->print(measure ? "MEASURE STARTED" : "MEASURE STOPPED");
_lcd->setCursor(0, 1);
_lcd->print("DIR: " + myDir);
//_lcd->print(myDir);
//_lcd->setCursor(0, 2);
//_lcd->print("FILE:");
//_lcd->setCursor(0, 3);
_lcd->setCursor(0, 2);
_lcd->print(myFile);
}
/* Podsumowanie po pomiarze: ramki, sampling, etc. */
void Display::displaySampleRateSummary(uint32_t reccount, uint32_t captureSeconds, float khz, String filename){
clear();
_lcd->setCursor(0, 0);
@@ -205,12 +260,20 @@ void Display::displaySampleRateSummary(uint32_t reccount, uint32_t captureSecond
delay(1000);
return;
}
float fs = (float)reccount / (float)captureSeconds;
_lcd->print("DONE: " + filename);
_lcd->setCursor(0, 1);
_lcd->print("Recs: " + String(reccount));
_lcd->setCursor(0, 2);
_lcd->print("Freq: " + String(fs, 1) + " Hz");
_lcd->setCursor(0, 3);
_lcd->print("Target: " + String(khz, 1) + " kHz");
}
float fs = (float)reccount / (float)captureSeconds; // Hz (ramki/s)
float fs_kHz = fs / 1000.0f;
_lcd->setCursor(0,0);
_lcd->print("Frames:");
_lcd->print((unsigned)reccount);
_lcd->setCursor(0,1);
_lcd->print("Time:");
_lcd->print((unsigned)captureSeconds);
_lcd->print("s.");
_lcd->setCursor(0,2);
_lcd->print("Rate:");
_lcd->print(fs_kHz);
_lcd->print("kHz");
_lcd->setCursor(0,3);
_lcd->print(filename);
}

View File

@@ -264,12 +264,22 @@ bool DataCapture::capture(uint32_t captureSeconds, const char *filename) {
}
// Czekamy na dane z sensora
uint32_t start_wait = millis();
while (!adxl_.availableAll()) {
if(isEscape()) break;
if (isEscape()) break;
if (millis() - start_wait > 500) {
ESP_LOGE(TAG_CAPTURE, "Sensor data timeout - aborting capture.");
measurementActive_ = false;
break;
}
Watchdog::feed();
yield();
}
if (!measurementActive_) break;
const uint8_t got = adxl_.readAlignedOnce(X, Y, Z, TS);
if (got != presentCnt) continue;
if (got == 0 || got != presentCnt) continue;
// Wspólny timestamp ramki
uint32_t tmin = UINT32_MAX;

View File

@@ -1,7 +1,8 @@
#include <Network.h>
#include "Watchdog.h" // Dodano dla feed() w portalu
#include <SD.h>
static const char *WIFI = "wifi";
extern ConfigManager configManager;
WiFiManager::WiFiManager() {}
@@ -19,62 +20,6 @@ void WiFiManager::begin() {
setupMDNS();
}
// NOWA FUNKCJA: Portal konfiguracyjny (Captive Portal)
void WiFiManager::startConfigPortal(Display &display) {
WiFi.mode(WIFI_AP);
WiFi.softAP(ssidAP.c_str(), passwordAP.c_str());
dnsServer.start(53, "*", WiFi.softAPIP());
server.on("/", [this]() { this->handleRoot(); });
server.on("/save", [this]() { this->handleSave(); });
server.onNotFound([this]() { this->handleRoot(); });
server.begin();
display.clear();
display.textCenter(0, "WIFI SETUP MODE");
display.textCenter(1, ssidAP.c_str());
display.textCenter(2, "IP: 192.168.4.1");
ESP_LOGI(WIFI, "Portal started: %s", ssidAP.c_str());
while (true) {
dnsServer.processNextRequest();
server.handleClient();
Watchdog::feed(); // Użycie Twojego Watchdoga z Watchdog.h
delay(10);
// Pętla trwa do momentu restartu w handleSave()
}
}
void WiFiManager::handleRoot() {
String html = "<html><head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<style>body{font-family:sans-serif;background:#f0f0f0;padding:20px;}input{width:100%;padding:10px;margin:10px 0;}</style></head>";
html += "<body><h1>VICTUS WMT</h1><p>Konfiguracja WiFi:</p>";
html += "<form action='/save' method='POST'>";
html += "SSID:<br><input type='text' name='s' value='" + String(config.ssid) + "'><br>";
html += "Hasło:<br><input type='password' name='p'><br>";
html += "<input type='submit' value='Zapisz i Restartuj'></form></body></html>";
server.send(200, "text/html", html);
}
void WiFiManager::handleSave() {
String newSsid = server.arg("s");
String newPass = server.arg("p");
if (newSsid.length() > 0) {
strncpy(config.ssid, newSsid.c_str(), sizeof(config.ssid));
strncpy(config.password, newPass.c_str(), sizeof(config.password));
configManager.saveConfig();
server.send(200, "text/html", "Zapisano dane. Restartuje...");
ESP_LOGI(WIFI, "WiFi saved. Rebooting...");
delay(2000);
ESP.restart();
}
}
// --- TWOJE ORYGINALNE FUNKCJE (ZACHOWANE W 100%) ---
// Konwersja char na IPAddress
bool WiFiManager::convertCharToIPAddress(const char *str, IPAddress& ip) {
@@ -108,7 +53,7 @@ void WiFiManager::connectToWiFi() {
}
}
ESP_LOGI(WIFI, "WiFi connecting");
ESP_LOGI(WIFI, "WiFi connecting to last SSID: %s", config.ssid);
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
@@ -116,6 +61,44 @@ void WiFiManager::connectToWiFi() {
updateLED();
}
if (WiFi.status() != WL_CONNECTED) {
ESP_LOGI(WIFI, "Failed. Reading wifi.txt from SD card...");
File f = SD.open("/wifi.txt");
if (f) {
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.isEmpty()) continue;
int sep = line.indexOf(';');
if (sep > 0) {
String s = line.substring(0, sep);
String p = line.substring(sep + 1);
s.trim();
p.trim();
ESP_LOGI(WIFI, "Trying SSID from list: '%s'", s.c_str());
WiFi.disconnect();
WiFi.begin(s.c_str(), p.c_str());
int tr = 0;
while (WiFi.status() != WL_CONNECTED && tr < 20) {
delay(500);
tr++;
updateLED();
}
if (WiFi.status() == WL_CONNECTED) {
ESP_LOGI(WIFI, "Connected to %s. Saving to config.", s.c_str());
strncpy(config.ssid, s.c_str(), sizeof(config.ssid)-1);
strncpy(config.password, p.c_str(), sizeof(config.password)-1);
configManager.saveConfig();
break;
}
}
}
f.close();
} else {
ESP_LOGW(WIFI, "wifi.txt not found on SD card.");
}
}
if (WiFi.status() == WL_CONNECTED) {
ESP_LOGI(WIFI, "SSID: %s", config.ssid);
String ipString = "IP: " + WiFi.localIP().toString();
@@ -125,7 +108,7 @@ void WiFiManager::connectToWiFi() {
String dnsInfo = "DNS: " + WiFi.dnsIP().toString();
ESP_LOGI(WIFI, "%s", dnsInfo.c_str());
} else {
String infoWiFi = "WIFI CONNECTION ERROR: " + String(config.ssid);
String infoWiFi = "WIFI CONNECTION ERROR: ALL FAILED";
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
}
updateLED();
@@ -165,31 +148,31 @@ bool WiFiManager::isWiFiOK(){
void WiFiManager::checkWiFiConnection() {
if (!isAccessPoint) {
getRSSI();
if (WiFi.status() != WL_CONNECTED) {
String infoWiFi = "WiFi reconnecting: " + String(config.ssid);
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
WiFi.reconnect();
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
retries++;
updateLED();
}
if (WiFi.status() == WL_CONNECTED) {
//String infoWiFi = "WiFi connected: " + String(config.ssid);
//ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
//String ipString = "IP: " + WiFi.localIP().toString();
//ESP_LOGI(WIFI, "%s", ipString.c_str());
getRSSI();
setupMDNS();
} else {
String infoWiFi = "WiFi reconnect error: " + String(config.ssid);
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
getRSSI();
if (WiFi.status() != WL_CONNECTED) {
String infoWiFi = "WiFi reconnecting: " + String(config.ssid);
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
WiFi.reconnect();
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
retries++;
updateLED();
}
if (WiFi.status() == WL_CONNECTED) {
//String infoWiFi = "WiFi connected: " + String(config.ssid);
//ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
//String ipString = "IP: " + WiFi.localIP().toString();
//ESP_LOGI(WIFI, "%s", ipString.c_str());
getRSSI();
}
}
updateLED();
setupMDNS();
} else {
String infoWiFi = "WiFi reconnect error: " + String(config.ssid);
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
getRSSI();
}
}
updateLED();
}
}
@@ -260,7 +243,7 @@ bool WiFiManager::performOTAUpdate(bool allowInsecureTLS, std::function<void(int
}
// 4) Konfiguracja klienta i wywołanie aktualizacji
httpUpdate.rebootOnUpdate(true); // po sukcesie reboot
httpUpdate.rebootOnUpdate(true); // po sukcesie reboot
t_httpUpdate_return ret;
if (url.startsWith("https://")) {
@@ -279,7 +262,7 @@ bool WiFiManager::performOTAUpdate(bool allowInsecureTLS, std::function<void(int
// 5) Obsługa rezultatów
switch (ret) {
case HTTP_UPDATE_OK:
ESP_LOGI(WIFI, "[OTA] Sukces - restart nastąpi za chwilę.");
ESP_LOGI(WIFI, "[OTA] Sukces restart nastąpi za chwilę.");
return true; // reboot i tak zaraz nastąpi
case HTTP_UPDATE_NO_UPDATES:
ESP_LOGW(WIFI, "[OTA] Brak nowej wersji (304/Not Modified).");
@@ -289,4 +272,90 @@ bool WiFiManager::performOTAUpdate(bool allowInsecureTLS, std::function<void(int
ESP_LOGE(WIFI, "[OTA] Błąd (%d): %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
return false;
}
}
void WiFiManager::startCaptivePortal() {
ESP_LOGI(WIFI, "Starting Captive Portal");
setupAccessPoint(ssidAP.c_str(), passwordAP.c_str());
// Start DNS Server
const byte DNS_PORT = 53;
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
// Start Web Server
server.on("/", std::bind(&WiFiManager::handleRoot, this));
server.on("/save", HTTP_POST, std::bind(&WiFiManager::handleSave, this));
server.onNotFound(std::bind(&WiFiManager::handleNotFound, this));
server.begin();
captivePortalActive = true;
ESP_LOGI(WIFI, "Captive Portal Started");
}
void WiFiManager::handleClient() {
if (captivePortalActive) {
dnsServer.processNextRequest();
server.handleClient();
}
}
void WiFiManager::handleRoot() {
int bases[] = {2, 2, 2, 2, 3, 3, 10, 10};
int vals[] = {4, 8, 16, 32, 9, 27, 100, 1000};
int answers[] = {2, 3, 4, 5, 2, 3, 2, 3};
int idx = random(0, 8);
int base = bases[idx];
int val = vals[idx];
expectedCaptchaAnswer = answers[idx];
String html = "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
html += "<style>body{font-family: Arial, sans-serif; text-align: center; margin:0; padding: 20px;}";
html += "input[type=text], input[type=password], input[type=number] {width: 100%; padding: 12px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; box-sizing: border-box;}";
html += "input[type=submit] {background-color: #4CAF50; color: white; padding: 14px 20px; margin: 8px 0; border: none; cursor: pointer; width: 100%;}";
html += "input[type=submit]:hover {opacity: 0.8;}";
html += ".container {max-width: 400px; margin: auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);}</style></head><body>";
html += "<h2>WiFi Configuration</h2>";
html += "<div class=\"container\">";
html += "<form action=\"/save\" method=\"POST\">";
html += "<label for=\"ssid\"><b>SSID</b></label>";
html += "<input type=\"text\" placeholder=\"Enter SSID\" name=\"ssid\" required>";
html += "<label for=\"password\"><b>Password</b></label>";
html += "<input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>";
html += "<label for=\"captcha\"><b>Captcha: What is log<sub>" + String(base) + "</sub>(" + String(val) + ")?</b></label>";
html += "<input type=\"number\" placeholder=\"Enter Answer\" name=\"captcha\" required>";
html += "<input type=\"submit\" value=\"Save and Restart\">";
html += "</form></div></body></html>";
server.send(200, "text/html", html);
}
void WiFiManager::handleSave() {
String ssid = server.arg("ssid");
String pass = server.arg("password");
String captchaStr = server.arg("captcha");
if (captchaStr.toInt() != expectedCaptchaAnswer) {
server.send(400, "text/plain", "Incorrect Captcha. Please go back and try again.");
return;
}
if (ssid.length() > 0) {
File f = SD.open("/wifi.txt", FILE_APPEND);
if (f) {
f.println(ssid + ";" + pass);
f.close();
server.send(200, "text/plain", "Credentials saved successfully! The device will now restart and try to connect.");
delay(2000);
ESP.restart();
} else {
server.send(500, "text/plain", "Failed to open wifi.txt on SD card for appending.");
}
} else {
server.send(400, "text/plain", "SSID cannot be empty.");
}
}
void WiFiManager::handleNotFound() {
server.sendHeader("Location", String("http://") + WiFi.softAPIP().toString(), true);
server.send(302, "text/plain", "");
}

View File

@@ -30,6 +30,10 @@ bool Settings::isBtnReset() {
return (digitalRead(BTN_UP) == LOW && digitalRead(BTN_DOWN) == LOW);
}
bool Settings::isSetClock() {
ESP_LOGI(TAG_SETTINGS, "BTN set clock");
return (digitalRead(BTN_OK) == LOW);
}
/*
Ustawienie opcji
@@ -141,7 +145,7 @@ int Settings::editField(int value, int minVal, int maxVal, const char *label) {
lastDown = down;
lastOk = ok;
delay(80); // prosty debounce + ograniczenie odpytywania
delay(80); //debouncing
}
}

117
src/UploadManager.cpp Normal file
View File

@@ -0,0 +1,117 @@
#include "UploadManager.h"
#include "Watchdog.h"
static const char* TAG_UPLOAD = "UPLOAD";
static const char* LOG_FILE = "/uploaded.csv";
UploadManager::UploadManager(APIClient& client, RTC_DS3231& rtc, DataCapture& capture)
: apiClient(client), rtc_(rtc), capture_(capture) {}
String UploadManager::getCurrentTimestamp() {
DateTime now = rtc_.now();
char buf[25];
snprintf(buf, sizeof(buf), "%04u-%02u-%02u %02u:%02u:%02u",
now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
return String(buf);
}
void UploadManager::appendLog(const String& filePath, const String& status) {
File f = SD.open(LOG_FILE, FILE_APPEND);
if (f) {
f.printf("%s,%s,%s\n", getCurrentTimestamp().c_str(), filePath.c_str(), status.c_str());
f.close();
}
}
bool UploadManager::isAlreadyUploaded(const String& filePath) {
File f = SD.open(LOG_FILE, FILE_READ);
if (!f) return false;
bool found = false;
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0) continue;
int firstComma = line.indexOf(',');
if (firstComma < 0) continue;
int secondComma = line.indexOf(',', firstComma + 1);
if (secondComma < 0) continue;
String logPath = line.substring(firstComma + 1, secondComma);
String logStatus = line.substring(secondComma + 1);
if (logPath == filePath) {
if (logStatus == "OK") found = true;
else found = false; // A retry might be needed if last status wasn't OK
}
Watchdog::feed();
}
f.close();
return found;
}
void UploadManager::uploadFile(const String& filePath) {
if (isAlreadyUploaded(filePath)) {
ESP_LOGI(TAG_UPLOAD, "File %s is already uploaded.", filePath.c_str());
return;
}
if (WiFi.status() != WL_CONNECTED) {
ESP_LOGE(TAG_UPLOAD, "No WiFi. Cannot upload %s", filePath.c_str());
appendLog(filePath, "ERROR: No WiFi");
return;
}
bool success = apiClient.uploadMeasurement(filePath);
if (success) {
appendLog(filePath, "OK");
} else {
if (WiFi.status() != WL_CONNECTED) {
appendLog(filePath, "ERROR: WiFi lost during upload");
} else {
appendLog(filePath, "ERROR: Upload Failed");
}
}
}
void UploadManager::processPendingUploads() {
if (WiFi.status() != WL_CONNECTED) return;
ESP_LOGI(TAG_UPLOAD, "Checking for pending uploads...");
uint32_t highestDir = capture_.findHighestNumericDir();
if (highestDir == 0) return;
for (uint32_t d = 1; d <= highestDir; d++) {
String path = capture_.dirPath(d);
File dir = SD.open(path);
if (!dir || !dir.isDirectory()) {
if(dir) dir.close();
continue;
}
for (File f = dir.openNextFile(); f; f = dir.openNextFile()) {
if (f.isDirectory()) { f.close(); continue; }
String childPath = String(f.name());
if (!childPath.startsWith("/")) {
childPath = path + "/" + childPath;
}
if (childPath.endsWith(".wmt")) {
if (!isAlreadyUploaded(childPath)) {
ESP_LOGI(TAG_UPLOAD, "Found pending file: %s", childPath.c_str());
uploadFile(childPath);
delay(1000);
}
}
f.close();
Watchdog::feed();
if (WiFi.status() != WL_CONNECTED) {
dir.close();
return;
}
}
dir.close();
}
ESP_LOGI(TAG_UPLOAD, "Pending uploads check complete.");
}

View File

@@ -15,7 +15,8 @@
#include <Measure.h>
#include <Tool.h>
#include <Settings.h>
#include <Uploader.h> // Dodano moduł wysyłki
#include "APIClient.h"
#include "UploadManager.h"
#define WDT_TIMEOUT 60 // Czas watchdoga do restartu
#define MAX_ADXL345_SENSORS 4 // Maksymalna ilość podłączanych sensorów
@@ -23,7 +24,7 @@
SPIClass SPI_ADXL(FSPI); // SPI2 (VSPI)
SPIClass SPI_SD(HSPI); // SPI3 (HSPI)
float x, y, z = 0; // Dane odczytane z akcelerometru
float x, y, z = 0; // Dane odczytane z akcelerometru
String name;
bool isRebootRequired = false;
@@ -46,14 +47,13 @@ Settings settings(display, rtc); // obsługa przycisków
WiFiManager wifi;
DataCapture capture(adxl, display, rtc, SD, 8192); // NEW !!! MEASURE!!!
// Inicjalizacja Uploadera
Uploader uploader(display);
APIClient apiClient;
UploadManager uploadManager(apiClient, rtc, capture);
Thread wifiTestThread = Thread(); // Cykliczny test WiFi
Thread offlineThread = Thread(); // Jeśli offline
Thread measureThread = Thread(); // Pomiar i zapis na SD
Thread uploadThread = Thread(); // Wątek wysyłki SSL
Thread uploadThread = Thread(); // Background upload
//////// PROTOTYPY /////////////////
void setup();
@@ -63,10 +63,7 @@ void resetBtnClick();
void checkWiFi();
void showOfflineScreen();
void measure();
void runUploader();
void toogleMode();
void settingsDevice();
void showError(String err);
void setClockRTCBtn();
/* ******************* SETUP() ************************* */
void setup() {
@@ -84,8 +81,8 @@ void setup() {
ESP_LOGI(TAG_MAIN, "Rejestrator parametrow");
ESP_LOGI(TAG_MAIN, "Firmware: %s", VERSION);
ESP_LOGI(TAG_MAIN, "----------------------");
ESP_LOGI(TAG_MAIN, "ESP32 model: %s Rev %d", ESP.getChipModel(), ESP.getChipRevision());
ESP_LOGI(TAG_MAIN, "Chip cores: %d", ESP.getChipCores());
ESP_LOGI(TAG_MAIN, "ESP32 model: %s Rev %d", ESP.getChipModel(), ESP.getChipRevision());
ESP_LOGI(TAG_MAIN, "Chip cores: %d", ESP.getChipCores());
// Inicjalizacja Watchdoga na 5 sek, panic_on_trigger = true
if (Watchdog::init(25, true)) {
@@ -99,6 +96,8 @@ void setup() {
configManager.begin(); // konfiguracja EEPROM urządzenia
//configManager.showConfig();
// Test LCD I2C
if (!isI2CDevPresent(0x27)) {
ESP_LOGE(TAG_MAIN, "LCD 0x27 wire error!");
@@ -158,6 +157,10 @@ void setup() {
settings.setTimeRTC();
}
}
// Przycisk ustawianai czasu: przytrzymaj OK przy starcie systemu
if(settings.isSetClock()) setClockRTCBtn();
delay(500);
// Karta SD
@@ -165,7 +168,7 @@ void setup() {
ESP_LOGI(TAG_MAIN, "SPI2 SD SCK: %d, MISO: %d, MOSI: %d, CS: %d", SD_SCK, SD_MISO, SD_MOSI, SD_CS);
SPI_SD.begin(SD_SCK, SD_MISO, SD_MOSI);
display.textStatus("SD CARD:");
while(!SD.begin(SD_CS, SPI_SD, 4000000)) {
while(!SD.begin(SD_CS, SPI_SD, 4000000)) { // 10 MHz na start; w razie czego 4 MHz (bez tego często SD Failed)
ESP_LOGE(TAG_MAIN, "SD mount failed");
display.print("FAILED");
delay(4000);
@@ -181,7 +184,8 @@ void setup() {
// Inicjalizacja ADXL345
ESP_LOGI(TAG_MAIN, "ADXL345 init");
display.textStatus("ADXL345: ");
if(!adxl.begin(&SPI_ADXL, 5000000, ADXL345FastSPI::RATE_3200HZ, ADXL345FastSPI::RANGE_16G, 1)){
//if(!adxl.begin(&SPI_ADXL, 5000000, ADXL345FastSPI::RATE_3200HZ, ADXL345FastSPI::RANGE_16G, 1)){
if(!adxl.begin(&SPI_ADXL, 2000000, ADXL345FastSPI::RATE_3200HZ, ADXL345FastSPI::RANGE_16G, 1)){
ESP_LOGE(TAG_MAIN, "ADXL345 Error");
display.print("FAILED");
isAccelExists = false;
@@ -208,36 +212,23 @@ void setup() {
delay(1000);
if(config.connect){
display.textStatus("WiFi Connect...");
wifi.begin();
int retry_count = 0;
while (WiFi.status() != WL_CONNECTED && retry_count < 60) {
delay(500);
retry_count++;
Watchdog::feed();
if(retry_count % 10 == 0) display.print(".");
}
if (WiFi.status() != WL_CONNECTED) {
ESP_LOGW(TAG_MAIN, "WiFi Failed. Starting Configuration Portal...");
wifi.startConfigPortal(display);
} else {
ESP_LOGI(TAG_MAIN, "WiFi OK");
wifiTestThread.onRun(checkWiFi);
wifiTestThread.setInterval(5000);
uploadThread.onRun(runUploader);
uploadThread.setInterval(30000);
}
wifiTestThread.onRun(checkWiFi);
wifiTestThread.setInterval(5000); // Test WiFi co 3 sekundy
} else {
offlineThread.onRun(showOfflineScreen);
offlineThread.setInterval(1000);
offlineThread.setInterval(1000); // Jeśli offline, to wyświetl ekran co 1 sek
}
measureThread.onRun(measure);
measureThread.setInterval(config.pause);
measureThread.setInterval(config.pause); // Test co X sekund
if(config.connect){
uploadThread.onRun([]() { uploadManager.processPendingUploads(); });
uploadThread.setInterval(60000); // Co 1 minutę sprawdzaj zaległe
}
// Dodanie taska loop do WDT
if (Watchdog::addThisTask()) {
ESP_LOGI(TAG_MAIN, "Added main task to Watchdog");
}
@@ -246,14 +237,23 @@ void setup() {
display.clear();
}
void runUploader() {
if (!testingNow) {
uploader.processQueue(3);
}
}
// Factory reset
void setClockRTCBtn(){
ESP_LOGI(TAG_MAIN, "SET DATE TIME");
while(settings.isSetClock()){
display.clear();
display.textCenter(0, " PLEASE ");
display.textCenter(1, "release key");
display.textCenter(3, "TO SET DATE TIME");
delay(500);
}
settings.setTimeRTC();
}
// Factory reset
void resetBtnClick(){
uint8_t counter = 10;
uint8_t counter = 10; // ile sekund trzymać przycisk na factory reset
ESP_LOGI(TAG_MAIN, "RESET BTN to 10 sec.");
while(settings.isBtnReset()){
Serial.print(counter); Serial.print(",");
@@ -280,6 +280,8 @@ void resetBtnClick(){
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
void showError(String err){
Watchdog::feed();
display.clear();
@@ -288,6 +290,7 @@ void showError(String err){
delay(3000);
}
void checkWiFi(){
Watchdog::feed();
bool isConnected = WiFi.isConnected();
@@ -297,6 +300,7 @@ void checkWiFi(){
display.updateNetwork(ip, isConnected);
}
////// Ekran główny tylko gdy brak pomiaru /////////////
void showOfflineScreen(){
if((!config.connect) && (!testingNow)) {
bool isGB;
@@ -306,23 +310,26 @@ void showOfflineScreen(){
}
}
void reboot() {
display.clear();
display.textCenter(1, "SYSTEM");
display.textCenter(2, "RESTARTING");
#if defined(ARDUINO_RASPBERRY_PI_PICO)
watchdog_enable(1, 1);
while (true);
watchdog_enable(1, 1); // 1 ms timeout, restart po upływie
while (true); // czekaj na watchdog reset
#endif
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
ESP_LOGI(TAG_MAIN, "RESTART");
ESP.restart();
ESP.restart(); // restart ESP
#endif
#if defined(ARDUINO_ARCH_STM32)
NVIC_SystemReset();
NVIC_SystemReset(); // restartuj STM32
#endif
}
// pomiar z Thread START POMIARU TUTAJ
void measure(){
Watchdog::feed();
testingNow = true;
@@ -334,7 +341,7 @@ void measure(){
snprintf(btime, sizeof(btime), "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
display.initMeasure(config.measure, testingNow, runMeasure, config.pause, config.duration, config.connect, licznik, bdate, btime);
ESP_LOGI(TAG_MAIN, "MEASURE RUNNING");
//delay(1000);
capture.captureAuto(config.duration, "/");
testingNow = false;
Watchdog::feed();
@@ -344,14 +351,21 @@ void measure(){
ESP_LOGI(TAG_MAIN, "MEASURE FINISH");
} else {
ESP_LOGI(TAG_MAIN, "MEASURE INTERRUPT");
//runMeasure = false;
}
if (config.connect && WiFi.status() == WL_CONNECTED) {
ESP_LOGI(TAG_MAIN, "TRIGGER UPLOAD PENDING...");
uploadManager.processPendingUploads();
}
runMeasure = false;
ESP_LOGI(TAG_MAIN, "DISPLAY Offline");
display.displayOffline(testingNow, adxl.connectedSensorsCount(), capture.freeSpaceFloat(), licznik, true);
offlineThread.enabled = true;
}
void toogleMode(){
void toogleMode(){ // tryb ciągłego, pojedynczego pomiaru
config.measure = !config.measure;
configManager.saveConfig();
display.textStatus("Mode changed");
@@ -385,18 +399,30 @@ void loop() {
if(settings.isPressed(3)) settingsDevice(); // DOWN
if(settings.isPressed(1)) toogleMode(); // UP
if (settings.readBtnUp() && settings.readBtnDown()) {
ESP_LOGI(TAG_MAIN, "Manual AP Mode trigger");
wifi.startCaptivePortal();
display.clear();
display.textCenter(1, "AP MODE");
display.textCenter(2, "192.168.4.1");
while(settings.readBtnUp() && settings.readBtnDown()) {
Watchdog::feed();
delay(50);
}
}
if(testingNow) {
if((runMeasure) && (settings.isPressed(2))){
runMeasure = false;
display.textStatus("STOPPING. WAIT");
}
return;
return; // jeśli trwa akurat test, nic nie rób
}
if(wifiTestThread.shouldRun() && (config.connect))
wifiTestThread.run();
if(uploadThread.shouldRun() && (config.connect))
if(uploadThread.shouldRun() && config.connect)
uploadThread.run();
if(offlineThread.shouldRun() && (!config.connect)){
@@ -423,11 +449,12 @@ void loop() {
if (isRebootRequired) {
ESP_LOGI(TAG_MAIN, "Reboot required");
Watchdog::feed();
delay(1000);
reboot();
delay(1000);
reboot();
}
wifi.handleClient();
Watchdog::feed();
delay(20);
delay(20); // 100
licznik ++;
}
}