forked from Akcelerometry_drgania_WMT/PI_mikrokontroler
Dodanie Captive Portal z logarytmami
This commit is contained in:
@@ -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
19
include/APIClient.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
31
include/UploadManager.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
124
src/APIClient.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
307
src/Display.cpp
307
src/Display.cpp
@@ -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();
|
||||
}
|
||||
|
||||
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());
|
||||
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->print(buf);
|
||||
|
||||
// Znacznik SSL "S"
|
||||
if (_sslActive) {
|
||||
_lcd->setCursor(19, 0);
|
||||
_lcd->print("S");
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
189
src/Network.cpp
189
src/Network.cpp
@@ -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();
|
||||
@@ -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).");
|
||||
@@ -290,3 +273,89 @@ bool WiFiManager::performOTAUpdate(bool allowInsecureTLS, std::function<void(int
|
||||
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", "");
|
||||
}
|
||||
@@ -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
117
src/UploadManager.cpp
Normal 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.");
|
||||
}
|
||||
119
src/main.cpp
119
src/main.cpp
@@ -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
|
||||
@@ -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() {
|
||||
@@ -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.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)){
|
||||
@@ -427,7 +453,8 @@ void loop() {
|
||||
reboot();
|
||||
}
|
||||
|
||||
wifi.handleClient();
|
||||
Watchdog::feed();
|
||||
delay(20);
|
||||
delay(20); // 100
|
||||
licznik ++;
|
||||
}
|
||||
Reference in New Issue
Block a user