forked from Akcelerometry_drgania_WMT/PI_mikrokontroler
Dodanie folderów include i src
This commit is contained in:
79
include/ADXL345ArraySPI.h
Normal file
79
include/ADXL345ArraySPI.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "ADXL345FreshSPI.h"
|
||||
|
||||
class ADXL345ArraySPI {
|
||||
public:
|
||||
static constexpr uint8_t MAX_SENSORS = 4;
|
||||
|
||||
explicit ADXL345ArraySPI(const uint8_t cs_pins[MAX_SENSORS]) {
|
||||
for (uint8_t i=0;i<MAX_SENSORS;i++){ cs_[i]=cs_pins[i]; }
|
||||
}
|
||||
|
||||
// Inicjalizacja wszystkich (SPI MODE3, STREAM FIFO, full-res, wybrany ODR/Range).
|
||||
// Zwraca liczbę poprawnie wykrytych czujników.
|
||||
uint8_t beginAll(SPIClass *spi, uint32_t spiHz, float odr_hz, ADXL345FreshSPI::Range range = ADXL345FreshSPI::Range::G16) {
|
||||
presentMask_ = 0;
|
||||
|
||||
// Zbezpieczenie: inicjalizacja CS na HIGH przed startem.
|
||||
for (uint8_t i=0;i<MAX_SENSORS;i++){
|
||||
pinMode(cs_[i], OUTPUT);
|
||||
digitalWrite(cs_[i], HIGH);
|
||||
}
|
||||
delayMicroseconds(5);
|
||||
|
||||
for (uint8_t i=0;i<MAX_SENSORS;i++){
|
||||
if (!acc_[i].begin(spi, cs_[i], spiHz)) continue;
|
||||
acc_[i].write8(0x2D, 0x00); // POWER_CTL = standby (MEASURE=0)
|
||||
if (!acc_[i].setRange(range, true)) continue;
|
||||
acc_[i].write8(0x2D, 0x08); // POWER_CTL = measure
|
||||
if (!acc_[i].setODR_Hz(odr_hz)) continue;
|
||||
// Tryb STREAM – najbezpieczniejszy dla jitteru CPU
|
||||
acc_[i].enableFIFO(ADXL345FreshSPI::FIFOmode::STREAM, 32);
|
||||
presentMask_ |= (1u << i);
|
||||
}
|
||||
return countPresent();
|
||||
}
|
||||
|
||||
inline uint8_t size() const { return MAX_SENSORS; }
|
||||
inline bool isPresent(uint8_t i) const { return (presentMask_ & (1u << i)) != 0; }
|
||||
inline uint8_t countPresent() const { return __builtin_popcount(presentMask_); }
|
||||
|
||||
// Sprawdza, czy KAŻDY obecny sensor ma >=1 nową próbkę (DATA_READY).
|
||||
bool availableAll() {
|
||||
for (uint8_t i=0;i<MAX_SENSORS;i++){
|
||||
if (!isPresent(i)) continue;
|
||||
if (!acc_[i].available()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Zdejmuje po 1 NAJSTARSZEJ próbce z FIFO każdego obecnego sensora.
|
||||
// Zwraca liczbę faktycznie zebranych próbek (== liczbie obecnych sensorów, jeśli sukces).
|
||||
uint8_t readAlignedOnce(int16_t x[], int16_t y[], int16_t z[], uint32_t ts_us[]) {
|
||||
uint8_t got = 0;
|
||||
for (uint8_t i=0;i<MAX_SENSORS;i++){
|
||||
if (!isPresent(i)) continue;
|
||||
|
||||
ADXL345FreshSPI::SampleI16 one[1];
|
||||
size_t n = acc_[i].readFIFOBurst(one, 1); // NAJSTARSZA próbka z FIFO
|
||||
if (n == 0) {
|
||||
// sporadycznie: awaryjnie dociągnij świeżą (rzadkie)
|
||||
ADXL345FreshSPI::SampleI16 s;
|
||||
if (!acc_[i].readFresh(s, 1)) return got; // przerwij zbieranie ramki
|
||||
one[0] = s;
|
||||
}
|
||||
x[i] = one[0].x; y[i] = one[0].y; z[i] = one[0].z; ts_us[i] = one[0].ts_us;
|
||||
got++;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
ADXL345FreshSPI& at(uint8_t i) { return acc_[i]; }
|
||||
|
||||
private:
|
||||
uint8_t cs_[MAX_SENSORS]{};
|
||||
ADXL345FreshSPI acc_[MAX_SENSORS];
|
||||
uint32_t presentMask_ = 0;
|
||||
};
|
||||
91
include/ADXL345FastSPI.h
Normal file
91
include/ADXL345FastSPI.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "ADXL345FreshSPI.h"
|
||||
#include <Logger.h>
|
||||
|
||||
class ADXL345FastSPI {
|
||||
public:
|
||||
enum Rate { RATE_100HZ, RATE_200HZ, RATE_400HZ, RATE_800HZ, RATE_1600HZ, RATE_3200HZ };
|
||||
enum Range { RANGE_2G, RANGE_4G, RANGE_8G, RANGE_16G };
|
||||
|
||||
ADXL345FastSPI(const uint8_t* csPins, uint8_t count) : count_(count > MAX_NUM ? MAX_NUM : count){
|
||||
for (uint8_t i = 0; i < count_; ++i) cs_[i] = csPins[i];
|
||||
}
|
||||
|
||||
/* Pobierz konfigurację zakres akcelerometru */
|
||||
uint8_t getRange(uint8_t accel);
|
||||
|
||||
/* Zwraca czy jest FUL_RES*/
|
||||
bool getFullRes(uint8_t accel);
|
||||
|
||||
// Wersja podstawowa: podaj SPIClass* (np. &SPI)
|
||||
bool begin(SPIClass *spi, uint32_t spiHz, Rate rate, Range range, uint8_t options = 0);
|
||||
|
||||
// Wersja wygodna: deleguje do powyższej, używając globalnego SPI
|
||||
inline bool begin(uint32_t spiHz, Rate rate, Range range, uint8_t options = 0) {
|
||||
return begin(&SPI, spiHz, rate, range, options);
|
||||
}
|
||||
|
||||
// Informacje / dostępność
|
||||
inline uint8_t size() const { return presentCnt_; }
|
||||
inline bool isPresent(uint8_t i) const { return (i < count_) && present_[i]; }
|
||||
bool availableAll();
|
||||
|
||||
// Wariant B: zdejmuje po 1 NAJSTARSZEJ próbce z FIFO każdego obecnego sensora
|
||||
// Zwraca liczbę zebranych próbek (== size(), jeśli pełna ramka).
|
||||
uint8_t readAlignedOnce(int16_t* x, int16_t* y, int16_t* z, uint32_t* ts_us);
|
||||
|
||||
// Zgodność: pojedynczy sensor – 1 próbka
|
||||
bool readNewSample(uint8_t idx, int16_t& x, int16_t& y, int16_t& z, bool& ready);
|
||||
|
||||
// Zwraca ilośc podłączonych czujników po inicjalizacji - ale nie jest dynamiczna!
|
||||
inline uint8_t connectedSensorsCount() const { return presentCnt_; }
|
||||
|
||||
// dynamiczne odświeżenie liczby działających czujników zawsze sprawdzi na bieżąco i zwróci przy działającym urządzeniu
|
||||
uint8_t refreshConnectedSensorsCount();
|
||||
|
||||
// Maska aktywnych sensorów wg bieżącego stanu (bit i = sensor i)
|
||||
inline uint8_t activeMask() const {
|
||||
uint8_t m = 0;
|
||||
for (uint8_t i = 0; i < count_; ++i) if (present_[i]) m |= (1u << i);
|
||||
return m;
|
||||
}
|
||||
|
||||
// Odświeża stan (ping) i zwraca zaktualizowaną maskę aktywnych sensorów
|
||||
uint8_t refreshActiveMask();
|
||||
|
||||
private:
|
||||
static constexpr uint8_t MAX_NUM = 4; // MAX Ilość ADXL345
|
||||
|
||||
SPIClass* spi_ = &SPI;
|
||||
ADXL345FreshSPI dev_[MAX_NUM];
|
||||
uint8_t cs_[MAX_NUM]{};
|
||||
bool present_[MAX_NUM]{};
|
||||
uint8_t count_ = 0;
|
||||
uint8_t presentCnt_ = 0;
|
||||
|
||||
uint32_t spiHz_ = 5000000;
|
||||
float odrHz_ = 3200.0f;
|
||||
|
||||
static float mapRateToHz(Rate r) {
|
||||
switch (r) {
|
||||
case RATE_100HZ: return 100.0f;
|
||||
case RATE_200HZ: return 200.0f;
|
||||
case RATE_400HZ: return 400.0f;
|
||||
case RATE_800HZ: return 800.0f;
|
||||
case RATE_1600HZ: return 1600.0f;
|
||||
case RATE_3200HZ: return 3200.0f;
|
||||
default: return 3200.0f;
|
||||
}
|
||||
}
|
||||
static ADXL345FreshSPI::Range mapRange(Range r) {
|
||||
switch (r) {
|
||||
case RANGE_2G: return ADXL345FreshSPI::Range::G2;
|
||||
case RANGE_4G: return ADXL345FreshSPI::Range::G4;
|
||||
case RANGE_8G: return ADXL345FreshSPI::Range::G8;
|
||||
case RANGE_16G: return ADXL345FreshSPI::Range::G16;
|
||||
default: return ADXL345FreshSPI::Range::G16;
|
||||
}
|
||||
}
|
||||
};
|
||||
87
include/ADXL345FreshSPI.h
Normal file
87
include/ADXL345FreshSPI.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "ADXL345Registers.h"
|
||||
#include <Logger.h>
|
||||
|
||||
class ADXL345FreshSPI {
|
||||
public:
|
||||
enum class Range { G2=0, G4=1, G8=2, G16=3 };
|
||||
enum class FIFOmode { BYPASS, FIFO, STREAM, TRIGGER };
|
||||
|
||||
struct SampleI16 {
|
||||
int16_t x, y, z;
|
||||
uint32_t ts_us;
|
||||
};
|
||||
struct SampleSI {
|
||||
float ax_g, ay_g, az_g;
|
||||
float ax_ms2, ay_ms2, az_ms2;
|
||||
uint32_t ts_us;
|
||||
};
|
||||
|
||||
ADXL345FreshSPI() = default;
|
||||
|
||||
// --- Init (SPI only) ---
|
||||
// Uwaga: ADXL345 wymaga SPI MODE3, zegar ≤ ~5 MHz.
|
||||
bool begin(SPIClass *spi, uint8_t csPin, uint32_t clockHz = 5000000);
|
||||
|
||||
// --- Konfiguracja ---
|
||||
bool setODR_Hz(float odr_hz);
|
||||
bool setRange(Range r, bool fullRes=true);
|
||||
bool enableFIFO(FIFOmode mode, uint8_t triggerLevel=16);
|
||||
bool enableDataReadyInterrupt(bool enable=true); // przy pollingu pozostaw false
|
||||
|
||||
// --- Status ---
|
||||
bool ping(); // DEVID == 0xE5 ?
|
||||
bool available(); // DATA_READY z INT_SOURCE
|
||||
|
||||
// --- Odczyty „świeże” (blokujące do timeout_ms) ---
|
||||
bool readFresh(SampleI16& out, uint32_t timeout_ms = 100);
|
||||
bool readFresh(SampleSI& out, uint32_t timeout_ms = 100);
|
||||
|
||||
// --- Zrzut FIFO (do n elementów) ---
|
||||
size_t readFIFOBurst(SampleI16* buf, size_t maxCount);
|
||||
size_t readFIFOBurst(SampleSI* buf, size_t maxCount);
|
||||
|
||||
// --- Parametry pomocnicze ---
|
||||
void setGConstant(float g = 9.80665f) { g_ms2 = g; }
|
||||
|
||||
float lsb_per_g() const { return 1.0f / scale_g_per_lsb; }
|
||||
//bool write8(uint8_t reg, uint8_t val);
|
||||
//bool read8(uint8_t reg, uint8_t& val);
|
||||
|
||||
/*
|
||||
Zwraca tryb pracy akcelerometru
|
||||
*/
|
||||
uint8_t getADXLRange();
|
||||
|
||||
/* Zwraca czy jest FULL_RES w akcelerometrze */
|
||||
bool getADXLFullRes();
|
||||
|
||||
void showRangeFull(String txt="");
|
||||
|
||||
// niskopoziomowe
|
||||
bool write8(uint8_t reg, uint8_t val);
|
||||
bool read8(uint8_t reg, uint8_t& val);
|
||||
|
||||
private:
|
||||
SPIClass* spi = nullptr;
|
||||
uint8_t cs = 255;
|
||||
uint32_t spiHz = 5000000;
|
||||
|
||||
float scale_g_per_lsb = 0.0039f; // full-res ~3.9 mg/LSB
|
||||
float g_ms2 = 9.80665f;
|
||||
bool fullRes = true;
|
||||
|
||||
// niskopoziomowe
|
||||
//bool write8(uint8_t reg, uint8_t val);
|
||||
//bool read8(uint8_t reg, uint8_t& val);
|
||||
bool readMulti(uint8_t reg, uint8_t* dst, size_t n);
|
||||
|
||||
void spiSelect();
|
||||
void spiDeselect();
|
||||
|
||||
bool configurePowerMeasure();
|
||||
uint8_t odrCodeFromHz(float hz);
|
||||
void countsToSI(const SampleI16& in, SampleSI& out);
|
||||
};
|
||||
27
include/ADXL345Registers.h
Normal file
27
include/ADXL345Registers.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#define ADXL345_REG_DEVID 0x00
|
||||
#define ADXL345_REG_BW_RATE 0x2C
|
||||
#define ADXL345_REG_POWER_CTL 0x2D
|
||||
#define ADXL345_REG_INT_ENABLE 0x2E
|
||||
#define ADXL345_REG_INT_MAP 0x2F
|
||||
#define ADXL345_REG_INT_SOURCE 0x30
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_DATAX0 0x32
|
||||
#define ADXL345_REG_DATAX1 0x33
|
||||
#define ADXL345_REG_DATAY0 0x34
|
||||
#define ADXL345_REG_DATAY1 0x35
|
||||
#define ADXL345_REG_DATAZ0 0x36
|
||||
#define ADXL345_REG_DATAZ1 0x37
|
||||
#define ADXL345_REG_FIFO_CTL 0x38
|
||||
#define ADXL345_REG_FIFO_STATUS 0x39
|
||||
|
||||
#define ADXL345_POWER_MEASURE 0x08
|
||||
#define ADXL345_DATA_READY_BIT 0x80 // INT_SOURCE[7]
|
||||
#define ADXL345_DATA_FORMAT_FULL_RES 0x08
|
||||
#define ADXL345_DATA_FORMAT_RANGE_MASK 0x03
|
||||
|
||||
#define ADXL345_FIFO_BYPASS 0x00
|
||||
#define ADXL345_FIFO_FIFO 0x40
|
||||
#define ADXL345_FIFO_STREAM 0x80
|
||||
#define ADXL345_FIFO_TRIGGER 0xC0
|
||||
71
include/Config.h
Normal file
71
include/Config.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <Logger.h>
|
||||
#include <Arduino.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
#define EEPROM_SIZE 1024
|
||||
//constexpr size_t EEPROM_SIZE = sizeof(Config) + 32; // z lekkim zapasem
|
||||
|
||||
extern bool isRebootRequired;
|
||||
extern bool isClearLog;
|
||||
extern bool connected;
|
||||
extern long countConnect;
|
||||
extern long countDisconnect;
|
||||
extern String act_rssi_percent;
|
||||
extern int8_t rssi;
|
||||
extern String actDate;
|
||||
extern String actTime;
|
||||
|
||||
struct Config {
|
||||
bool connect; // czy łączyć z Internetem?
|
||||
bool measure; // true - pomiary ciągłe, false - nie rób nic (tryb konfiguracji)
|
||||
char ip[16];
|
||||
char subnet[16];
|
||||
char gateway[16];
|
||||
char dns[16];
|
||||
char ssid[32];
|
||||
char ntp[50];
|
||||
char password[32];
|
||||
char hostname[32];
|
||||
char place[100]; // miejsce instalacji
|
||||
bool dhcp; // czy włączyć DHCP?
|
||||
char user[10]; // użytkownik konfiguracji
|
||||
char pass[20]; // hasło użytkownika konfiguracji
|
||||
char updateUrl[150]; // adres pliku aktualizacji
|
||||
char restURL[150]; // adres Rest API
|
||||
int restPort; // Port systemu Api na serwerze
|
||||
char restUser[30]; // login RestAPI
|
||||
char restPass[50]; // hasło RestAPi
|
||||
uint8_t apiKey[32]; // Klucz API KEY
|
||||
uint16_t pause; // Pomiar co sekund
|
||||
uint8_t duration; // Czas pomiaru w sekundach 1-25
|
||||
char S0[12]; // nazwy czujników 1-8
|
||||
char S1[12];
|
||||
char S2[12];
|
||||
char S3[12];
|
||||
char S4[12];
|
||||
char S5[12];
|
||||
char S6[12];
|
||||
char S7[12];
|
||||
};
|
||||
|
||||
// Global config declaration
|
||||
extern Config config;
|
||||
|
||||
class ConfigManager {
|
||||
public:
|
||||
ConfigManager();
|
||||
void begin(); // EEPROM initialization
|
||||
void readConfig(); // Odczyt konfiguracji z EEPROM
|
||||
void saveConfig(); // Zapis konfiguracji do EEPROM
|
||||
void resetToDefaults(); // Reset do ustawień domyślnych
|
||||
void showConfig();
|
||||
void generateApiKey(uint8_t *buf, size_t len);
|
||||
|
||||
private:
|
||||
bool isEEPROMEmpty();
|
||||
};
|
||||
|
||||
#endif
|
||||
86
include/Display.h
Normal file
86
include/Display.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#pragma once
|
||||
#include "esp_log.h"
|
||||
#include <Arduino.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
#include <Wire.h>
|
||||
#include <Version.h>
|
||||
#include <RTClib.h>
|
||||
#include <Config.h>
|
||||
|
||||
#define SCREEN_WIDTH 40
|
||||
#define SCREEN_HEIGHT 4
|
||||
#define SCREEN_ADDRESS 0x27
|
||||
|
||||
class Display {
|
||||
public:
|
||||
Display(RTC_DS3231 &rtc,
|
||||
uint8_t address = SCREEN_ADDRESS,
|
||||
uint8_t columns = SCREEN_WIDTH,
|
||||
uint8_t rows = SCREEN_HEIGHT);
|
||||
|
||||
// Zwraca true, jeśli urządzenie na I2C odpowiada
|
||||
bool begin(TwoWire *wire = &Wire);
|
||||
|
||||
/* Ekran startowy urządzenia */
|
||||
void welcomeScreen();
|
||||
|
||||
/* Czyście wiersz */
|
||||
void clearRow(uint16_t line);
|
||||
|
||||
/* Wyświetla wyśrodkowany tekst w danym wierszu */
|
||||
void textCenter(uint8_t line, const char *txt);
|
||||
|
||||
/* Wyświetla tekst w danym wierszu od lewej, bez centrowania */
|
||||
void text(uint8_t line, const char *text);
|
||||
|
||||
/* Status - najniższy wiersz. Czyści przed zapisaniem */
|
||||
void textStatus(const char *text);
|
||||
|
||||
void clear();
|
||||
|
||||
/* Ekran po uruchomieniu */
|
||||
void mainScreen();
|
||||
|
||||
/* Ekran jeśli offline miga co 1 sek */
|
||||
void displayOffline(bool measure, uint8_t count, float freeSpace, long licznik, bool refresh = false);
|
||||
|
||||
/* Podsumowanie po pomiarze: ramki, sampling, etc. */
|
||||
void displaySampleRateSummary(uint32_t reccount, uint32_t captureSeconds, float khz, String filename);
|
||||
|
||||
void updateNetwork(String ip, bool connected);
|
||||
void updateBarWiFi(long signal, bool connected);
|
||||
void textStyle(const uint8_t st, String text);
|
||||
void message(String text);
|
||||
|
||||
void print(String text);
|
||||
void println(String text);
|
||||
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?
|
||||
bool runmes,
|
||||
uint16_t pause, // Czas (s) pomiędzy próbkami
|
||||
uint8_t duration, // Czas (s) trwania próbki
|
||||
bool connect, // Czy połączenie Network
|
||||
long counter, // główny licznik long
|
||||
String gdate, // Data aktualna
|
||||
String gtime); // Czas aktualny
|
||||
|
||||
private:
|
||||
LiquidCrystal_I2C *_lcd;
|
||||
RTC_DS3231 &rtc_;
|
||||
uint8_t _address;
|
||||
uint8_t _columns;
|
||||
uint8_t _rows;
|
||||
};
|
||||
|
||||
#endif
|
||||
15
include/Logger.h
Normal file
15
include/Logger.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
// Wszystkie tagi logowania w jednym miejscu
|
||||
extern const char *TAG_MAIN;
|
||||
extern const char *TAG_DISP;
|
||||
extern const char *TAG_ADXL;
|
||||
extern const char *TAG_CONF;
|
||||
|
||||
// Funkcja inicjalizacji poziomów logowania
|
||||
void init_log_levels();
|
||||
|
||||
#endif
|
||||
127
include/Measure.h
Normal file
127
include/Measure.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef MEASURE_H
|
||||
#define MEASURE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Watchdog.h>
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
#include <FS.h>
|
||||
#include <RTClib.h>
|
||||
#include <Display.h>
|
||||
#include "ADXL345FastSPI.h" // <-- zgodnie z main.cpp
|
||||
#include <Logger.h>
|
||||
#include <Pinout.h>
|
||||
|
||||
// Domyślne parametry akwizycji ADXL345 (mogą być nadpisane w ADXL345FastSPI::begin)
|
||||
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
|
||||
bool exists; // true, jeśli ostatni plik istnieje
|
||||
};
|
||||
|
||||
struct SpaceInfo {
|
||||
double value;
|
||||
const char *unit; // "GB" | "MB" | "UNKNOWN"
|
||||
};
|
||||
|
||||
class DataCapture {
|
||||
// --- Nagłówek pliku WMT (jak w oryginale) ---
|
||||
struct FileHeader {
|
||||
char magic[3]; // "WMT"
|
||||
uint16_t version; // 1
|
||||
uint16_t headerSize; // sizeof(FileHeader)
|
||||
uint32_t sampleSize; // sizeof(Sample)
|
||||
uint32_t timestamp; // UNIX startu akwizycji
|
||||
uint32_t reccount; // liczba rekordów Sample w pliku
|
||||
} __attribute__((packed));
|
||||
|
||||
public:
|
||||
// --- Rekord próbki (jak w oryginale) ---
|
||||
struct Sample {
|
||||
uint32_t offset; // µs od startu akwizycji (wspólny dla ramki)
|
||||
uint8_t sensor_id; // 0..3
|
||||
int16_t x, y, z; // surowe ADXL345
|
||||
bool ready; // 1 = obecna
|
||||
} __attribute__((packed));
|
||||
|
||||
// Konstruktor dopasowany do main.cpp – przyjmuje ADXL345FastSPI
|
||||
DataCapture(ADXL345FastSPI &adxl, Display &display, RTC_DS3231 &rtc, fs::FS &storage, size_t bufferSize = 131072 /* 128 KB */);
|
||||
~DataCapture();
|
||||
|
||||
bool capture(uint32_t captureSeconds, const char *filename);
|
||||
bool captureAuto(uint32_t captureSeconds, const char *baseDirectory = "/logs");
|
||||
|
||||
void printSamplingRate(uint32_t reccount, uint32_t captureSeconds, String filename);
|
||||
void readHeaderAndPrint(const char *path);
|
||||
void stop();
|
||||
bool isActive() const { return measurementActive_; }
|
||||
|
||||
// SD utils
|
||||
SpaceInfo freeSpaceMB();
|
||||
float freeSpaceFloat(bool *isGB = nullptr);
|
||||
String unixToDateTime(uint32_t ts);
|
||||
|
||||
// plik z najwyższym indeksem
|
||||
FileInfo getLastFileInfo();
|
||||
bool deleteAllOnSD();
|
||||
|
||||
bool isExit = false; // true oznacza przerwanie pomiaru
|
||||
|
||||
private:
|
||||
ADXL345FastSPI &adxl_;
|
||||
Display &display_;
|
||||
RTC_DS3231 &rtc_;
|
||||
fs::FS &_fs;
|
||||
|
||||
// Katalogowanie
|
||||
const String _baseDir = "/";
|
||||
const String _ext = ".wmt";
|
||||
const uint8_t _digits = 8;
|
||||
const uint16_t _maxFilesPerDir = 400;
|
||||
|
||||
// Bufor zapisu (PSRAM)
|
||||
uint8_t *buffer_ = nullptr;
|
||||
size_t bufferSize_ = 0;
|
||||
size_t bufferIndex_ = 0;
|
||||
bool measurementActive_ = false;
|
||||
|
||||
bool flushToFile(File &f);
|
||||
static uint8_t crc8(const uint8_t *data, size_t len);
|
||||
void setTestingIndicator_(bool on, String myDir, String myFile);
|
||||
|
||||
bool isEscape(){
|
||||
bool ispress = (GPIO.in & (1UL << BTN_OK)) == 0;
|
||||
if(ispress) {
|
||||
isExit = true;
|
||||
measurementActive_;
|
||||
display_.textStatus("Cancelling. Wait!");
|
||||
}
|
||||
return ispress;
|
||||
} // szybkidigitalRead : czy BTN stop???
|
||||
|
||||
|
||||
// Helpers (zachowane z Twojej wersji)
|
||||
public:
|
||||
bool isAllDigits(const char *s);
|
||||
uint32_t toUint(const char *s);
|
||||
static const char *basenameFromPath(const char* full);
|
||||
bool isWmtWithDigits(const char *name, uint32_t &idxOut) const;
|
||||
String makeIndexedName(uint32_t idx) const;
|
||||
static String joinPath(const String &a, const String &b);
|
||||
bool ensureDir(uint32_t dirNum);
|
||||
uint32_t findHighestNumericDir();
|
||||
void scanDirForWmt(uint32_t dirNum, uint32_t &count, uint32_t &highestIdx);
|
||||
bool recursiveDelete(const String &path);
|
||||
String dirPath(uint32_t dirNum) const;
|
||||
String allocateNextFilePath();
|
||||
String generateNextFilename();
|
||||
void printLastFileInfoSerial();
|
||||
};
|
||||
|
||||
#endif // MEASURE_H
|
||||
92
include/Network.h
Normal file
92
include/Network.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef NETWORK_H
|
||||
#define NETWORK_H
|
||||
|
||||
#include "esp_log.h"
|
||||
#include <Arduino.h>
|
||||
#include <Config.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#endif
|
||||
|
||||
#include <DNSServer.h> // Dodano dla Captive Portal
|
||||
//#include <Pinout.h>
|
||||
//#include <IPAddress.h>
|
||||
//#include <WiFiUdp.h>
|
||||
|
||||
//#include <WiFiClientSecureBearSSL.h>
|
||||
|
||||
// OTA
|
||||
#include <HTTPUpdate.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#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();
|
||||
void begin();
|
||||
void connectToWiFi();
|
||||
void setupAccessPoint(const char *newSSID, const char *newPassword);
|
||||
void checkWiFiConnection();
|
||||
void updateLED();
|
||||
void setupMDNS();
|
||||
void ReadConnection();
|
||||
bool isConnected(); // Zwraca stan połączenia
|
||||
bool convertCharToIPAddress(const char *str, IPAddress& ip);
|
||||
void ledBlink(); // Z innego projektu - niepotrzebne, bo nie ma LED WiFi...
|
||||
bool isWiFiOK(); // True, gdy WiFi połączone i false, gdy brak połączenia
|
||||
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);
|
||||
|
||||
/**
|
||||
* Aktualizacja systemu przez internet z adresu config.updateUrl.
|
||||
* @param allowInsecureTLS true => dla https wyłącz weryfikację certyfikatu.
|
||||
* @param progressCb callback progress (opcjonalnie) (bytes, total).
|
||||
* @return true, jeśli update zakończony sukcesem (urządzenie się zrestartuje).
|
||||
*/
|
||||
bool performOTAUpdate(bool allowInsecureTLS = true, std::function<void(int,int)> progressCb = nullptr);
|
||||
|
||||
int8_t rssi = 0;
|
||||
bool useDHCP = true;
|
||||
IPAddress local_IP;
|
||||
IPAddress gateway;
|
||||
IPAddress subnet;
|
||||
IPAddress dns;
|
||||
String ssidAP = "ACCEL666";
|
||||
String passwordAP = "12345678";
|
||||
|
||||
private:
|
||||
bool isAccessPoint = false;
|
||||
|
||||
// Obiekty serwerów dla Portalu (ESP32/ESP8266)
|
||||
#ifdef ESP32
|
||||
WebServer server{80};
|
||||
#elif defined(ESP8266)
|
||||
ESP8266WebServer server{80};
|
||||
#endif
|
||||
DNSServer dnsServer;
|
||||
void handleRoot();
|
||||
void handleSave();
|
||||
};
|
||||
|
||||
extern WiFiManager wifi;
|
||||
|
||||
#endif // WIFIMANAGER_H
|
||||
52
include/Pinout.h
Normal file
52
include/Pinout.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef PINOUT_H
|
||||
#define PINOUT_H
|
||||
|
||||
#if defined(ESP32)
|
||||
// SPI3 (HSPI) - SD Card nie kolidują z PSRAM
|
||||
#define SD_SCK 16 // 36 //18
|
||||
#define SD_MOSI 17 // 35 //17
|
||||
#define SD_MISO 18 // 37 //16
|
||||
#define SD_CS 15 // 34 //15 ?? 34
|
||||
|
||||
// SPI2 (VSPI) - ADXL345
|
||||
#define MOSI_ADSX 11 // SDA
|
||||
#define CLK_ADSX 12 // SCL
|
||||
#define MISO_ADSX 13 // SDO
|
||||
|
||||
//I2C C3
|
||||
#define PIN_SDA 47 // szary
|
||||
#define PIN_SCL 48 // niebieski
|
||||
|
||||
// Przycisk
|
||||
#define BTN_UP 5
|
||||
#define BTN_OK 6
|
||||
#define BTN_DOWN 7
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
CS {5, 6, 7, 10, 14, 21}
|
||||
SPI2 (VSPI) — preferowane do ADXL345 (wysoka prędkość, stabilność). ASXL345
|
||||
SPI3 (HSPI) — dowolne piny do SD (niższa prędkość, ale elastyczność). SD
|
||||
|
||||
Nie używać ESP32-S3:
|
||||
integrated SPI flash: 26, 27, 28, 29, 30, 31, 32,
|
||||
USB: 19, 20, 43, 44
|
||||
PSRAM 35, 37
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO)
|
||||
#define CLK_ADSX 18
|
||||
#define MOSI_ADSX 19
|
||||
#define MISO_ADSX 16
|
||||
|
||||
#define SD_MISO 12
|
||||
#define SD_CS 13
|
||||
#define SD_SCK 14
|
||||
#define SD_MOSI 15
|
||||
#define I2C_SDA 20
|
||||
#define I2C_SCL 21
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
52
include/Settings.h
Normal file
52
include/Settings.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Pinout.h>
|
||||
#include <Watchdog.h>
|
||||
#include <RTClib.h>
|
||||
#include <Display.h>
|
||||
#include <Logger.h>
|
||||
#include <Config.h>
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
Settings(Display &display, RTC_DS3231 &rtc);
|
||||
~Settings();
|
||||
|
||||
void setTimeRTC();
|
||||
void setConfigDevice();
|
||||
void finishConfigDevice();
|
||||
void begin();
|
||||
|
||||
bool isPressed(uint8_t btnIndex);
|
||||
|
||||
// bool is1and3();
|
||||
|
||||
bool isBtnReset();
|
||||
|
||||
bool readBtnUp() { return digitalRead(BTN_UP) == LOW; }
|
||||
bool readBtnOk() { return digitalRead(BTN_OK) == LOW; }
|
||||
bool readBtnDown() { return digitalRead(BTN_DOWN) == LOW; }
|
||||
|
||||
// Struktura na datę i czas
|
||||
struct RtcDateTime {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
};
|
||||
|
||||
private:
|
||||
Display &display_;
|
||||
RTC_DS3231 &rtc_;
|
||||
|
||||
int editField(int value, int minVal, int maxVal, const char *label);
|
||||
void constrainValue(int &value, int minVal, int maxVal);
|
||||
void printField(const char *label, int value);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
14
include/Tool.h
Normal file
14
include/Tool.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef TOOL_H
|
||||
#define TOOL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "esp_log.h"
|
||||
#include "Watchdog.h"
|
||||
|
||||
void scanI2C();
|
||||
|
||||
/* Funkcja przyjmuje adres jako argument i zwraca true gdy urządzenie odpowiada, w przeciwnym wypadku false */
|
||||
bool isI2CDevPresent(uint8_t address);
|
||||
|
||||
#endif
|
||||
24
include/Uploader.h
Normal file
24
include/Uploader.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef UPLOADER_H
|
||||
#define UPLOADER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <SD.h>
|
||||
#include "Config.h"
|
||||
#include "Display.h"
|
||||
#include "Watchdog.h"
|
||||
|
||||
class Uploader {
|
||||
public:
|
||||
Uploader(Display &display);
|
||||
void processQueue(int maxFiles = 3);
|
||||
|
||||
private:
|
||||
Display &_display;
|
||||
String loadCACert(const char* path);
|
||||
bool sendFile(String filePath, String& caCert);
|
||||
};
|
||||
|
||||
#endif
|
||||
9
include/Version.h
Normal file
9
include/Version.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
#define VERSION "1.3.2"
|
||||
|
||||
// 1: graphical 128x64, 2: LCD I2C Text 4x20
|
||||
#define LCD_TYPE 2
|
||||
|
||||
#endif
|
||||
38
include/Watchdog.h
Normal file
38
include/Watchdog.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
/**
|
||||
* Watchdog — prosty interfejs do inicjalizacji i karmienia WDT z dowolnego modułu.
|
||||
*
|
||||
* Obsługiwane środowiska:
|
||||
* - ESP32 Arduino Core / ESP-IDF (esp_task_wdt)
|
||||
* - Fallback: no-op na innych platformach
|
||||
*
|
||||
* Użycie:
|
||||
* Watchdog::init(5, true);
|
||||
* Watchdog::addThisTask();
|
||||
* ...
|
||||
* Watchdog::feed();
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Watchdog {
|
||||
|
||||
/** Inicjalizacja Task Watchdog (idempotentna). */
|
||||
bool init(int timeout_seconds = 5, bool panic_on_trigger = true);
|
||||
|
||||
/** Dodaje bieżący task (wątki FreeRTOS: wołaj w ciele tego taska). */
|
||||
bool addThisTask();
|
||||
|
||||
/** Usuwa bieżący task z nadzoru WDT. */
|
||||
bool removeThisTask();
|
||||
|
||||
/** Karmi watchdog (reset licznika). */
|
||||
void feed();
|
||||
|
||||
/** Zmienia timeout (wykonuje re-init wewnętrznie, jeśli trzeba). */
|
||||
bool setTimeout(int timeout_seconds);
|
||||
|
||||
/** Czy watchdog jest aktywny (zainicjalizowany)? */
|
||||
bool isActive();
|
||||
|
||||
} // namespace Watchdog
|
||||
140
src/ADXL345FastSPI.cpp
Normal file
140
src/ADXL345FastSPI.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "ADXL345FastSPI.h"
|
||||
|
||||
static const char *TAG_FRESH = "ADXLFAST";
|
||||
|
||||
bool ADXL345FastSPI::begin(SPIClass *spi, uint32_t spiHz, Rate rate, Range range, uint8_t /*options*/){
|
||||
spi_ = spi;
|
||||
spiHz_ = spiHz;
|
||||
odrHz_ = mapRateToHz(rate);
|
||||
auto r = mapRange(range);
|
||||
|
||||
//spi_->begin(); 28.01.2026
|
||||
presentCnt_ = 0;
|
||||
for (uint8_t i = 0; i < count_; ++i) {
|
||||
bool ok = dev_[i].begin(spi_, cs_[i], spiHz_)
|
||||
&& dev_[i].setRange(r, true)
|
||||
&& dev_[i].setODR_Hz(odrHz_);
|
||||
|
||||
dev_[i].showRangeFull(String(i));
|
||||
|
||||
|
||||
if (ok) {
|
||||
dev_[i].enableFIFO(ADXL345FreshSPI::FIFOmode::STREAM, 32); // wariant B
|
||||
present_[i] = true;
|
||||
presentCnt_++;
|
||||
} else {
|
||||
present_[i] = false;
|
||||
}
|
||||
}
|
||||
return presentCnt_ > 0;
|
||||
}
|
||||
|
||||
bool ADXL345FastSPI::availableAll() {
|
||||
for (uint8_t i = 0; i < count_; ++i) {
|
||||
if (!present_[i]) continue;
|
||||
// else {
|
||||
// dev_[i].showRangeFull("AVAILABLE ADXL");
|
||||
// }
|
||||
|
||||
if (!dev_[i].available()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
while (!availableAll()) {
|
||||
// micro-spin; w razie potrzeby można dodać yield() / feed watchdog poza hot-path
|
||||
}
|
||||
|
||||
// 2) Zdejmij najstarszą z każdego sensora
|
||||
uint8_t got = 0;
|
||||
for (uint8_t i = 0; i < count_; ++i) {
|
||||
if (!present_[i]) continue;
|
||||
|
||||
ADXL345FreshSPI::SampleI16 one[1];
|
||||
size_t n = dev_[i].readFIFOBurst(one, 1);
|
||||
if (n == 0) {
|
||||
ADXL345FreshSPI::SampleI16 s;
|
||||
if (!dev_[i].readFresh(s, 1)) break;
|
||||
one[0] = s;
|
||||
}
|
||||
if (x) x[i] = one[0].x;
|
||||
if (y) y[i] = one[0].y;
|
||||
if (z) z[i] = one[0].z;
|
||||
if (ts_us) ts_us[i] = one[0].ts_us;
|
||||
got++;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
bool ADXL345FastSPI::readNewSample(uint8_t idx, int16_t& x, int16_t& y, int16_t& z, bool& ready)
|
||||
{
|
||||
if (idx >= count_ || !present_[idx]) { ready = false; return false; }
|
||||
|
||||
ADXL345FreshSPI::SampleI16 one[1];
|
||||
size_t n = dev_[idx].readFIFOBurst(one, 1);
|
||||
if (n == 0) {
|
||||
ADXL345FreshSPI::SampleI16 s;
|
||||
if (!dev_[idx].readFresh(s, 1)) { ready = false; return false; }
|
||||
one[0] = s;
|
||||
}
|
||||
x = one[0].x; y = one[0].y; z = one[0].z;
|
||||
ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ADXL345FastSPI::refreshConnectedSensorsCount(){
|
||||
uint8_t cnt = 0;
|
||||
for (uint8_t i = 0; i < count_; ++i) {
|
||||
// ping() czyta DEVID (0xE5) wewnętrznie i zwraca true/false
|
||||
if (dev_[i].ping()) {
|
||||
present_[i] = true;
|
||||
cnt++;
|
||||
} else {
|
||||
present_[i] = false;
|
||||
}
|
||||
}
|
||||
presentCnt_ = cnt;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
Zwraca maskę bitową aktywnych sensorów (np. do szybkiej diagnostyki: który CS nie odpowiada)
|
||||
Uzycie:
|
||||
uint8_t mask = adxl.refreshActiveMask();
|
||||
ESP_LOGI(TAG_MAIN, "Aktywne sensory (bitmask): 0x%02X, count=%u",
|
||||
mask, adxl.connectedSensorsCount());
|
||||
|
||||
// Sprawdzenie konkretnego sensora:
|
||||
if (mask & (1u << 3)) {
|
||||
ESP_LOGI(TAG_MAIN, "Sensor #3 online");
|
||||
} else {
|
||||
ESP_LOGW(TAG_MAIN, "Sensor #3 offline");
|
||||
}
|
||||
*/
|
||||
uint8_t ADXL345FastSPI::refreshActiveMask(){
|
||||
uint8_t m = 0;
|
||||
uint8_t cnt = 0;
|
||||
|
||||
for (uint8_t i = 0; i < count_; ++i) {
|
||||
if (dev_[i].ping()) {
|
||||
present_[i] = true;
|
||||
m |= (1u << i);
|
||||
cnt++;
|
||||
} else {
|
||||
present_[i] = false;
|
||||
}
|
||||
}
|
||||
presentCnt_ = cnt;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Pobiera zakres wybranego akcelerometru
|
||||
uint8_t ADXL345FastSPI::getRange(uint8_t accel){
|
||||
return dev_[accel].getADXLRange();
|
||||
}
|
||||
|
||||
bool ADXL345FastSPI::getFullRes(uint8_t accel){
|
||||
return dev_[accel].getADXLFullRes();
|
||||
}
|
||||
242
src/ADXL345FreshSPI.cpp
Normal file
242
src/ADXL345FreshSPI.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "ADXL345FreshSPI.h"
|
||||
|
||||
static const char *TAG_FRESH = "ADXLFRESH";
|
||||
|
||||
static inline int16_t u8pair_to_i16(uint8_t lo, uint8_t hi) {
|
||||
return (int16_t)((hi << 8) | lo);
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::begin(SPIClass* s, uint8_t csPin, uint32_t clockHz) {
|
||||
spi = s; cs = csPin; spiHz = clockHz;
|
||||
pinMode(cs, OUTPUT); digitalWrite(cs, HIGH);
|
||||
//spi->begin(); // tymczas
|
||||
delay(1);
|
||||
|
||||
if (!ping()) return false;
|
||||
|
||||
// 1. Wymuś STANDBY przed jakąkolwiek konfiguracją
|
||||
write8(ADXL345_REG_POWER_CTL, 0x00);
|
||||
delay(1);
|
||||
|
||||
// 2. Skonfiguruj format danych (Range i Full_Res)
|
||||
if (!setRange(Range::G16, true)) { // było if (!setRange(Range::G2, true)) return false;
|
||||
ESP_LOGI(TAG_FRESH, "Range G16 ERROR!");
|
||||
return false;
|
||||
}
|
||||
// 3. Skonfiguruj ODR
|
||||
if (!setODR_Hz(100.0f)) return false; // domyślnie 100 Hz
|
||||
// 4. Dopiero teraz włącz pomiar
|
||||
if (!configurePowerMeasure()) return false;
|
||||
showRangeFull();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ADXL345FreshSPI::ping() {
|
||||
uint8_t id=0; if (!read8(ADXL345_REG_DEVID, id)) return false;
|
||||
return id == 0xE5;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::configurePowerMeasure() {
|
||||
return write8(ADXL345_REG_POWER_CTL, ADXL345_POWER_MEASURE);
|
||||
}
|
||||
|
||||
|
||||
bool ADXL345FreshSPI::setRange(Range r, bool fullRes_) {
|
||||
fullRes = fullRes_;
|
||||
// Wymuś STANDBY (MEASURE=0) – kluczowe dla zmiany RANGE/FULL_RES
|
||||
write8(ADXL345_REG_POWER_CTL, 0x00);
|
||||
delayMicroseconds(5);
|
||||
|
||||
uint8_t fmt = 0;
|
||||
if (!read8(ADXL345_REG_DATA_FORMAT, fmt)) return false;
|
||||
|
||||
fmt &= ~ADXL345_DATA_FORMAT_RANGE_MASK; // bity 1:0
|
||||
fmt |= (uint8_t)r;
|
||||
|
||||
if (fullRes) fmt |= ADXL345_DATA_FORMAT_FULL_RES; // bit 3
|
||||
else fmt &= ~ADXL345_DATA_FORMAT_FULL_RES;
|
||||
|
||||
if (!write8(ADXL345_REG_DATA_FORMAT, fmt)) return false;
|
||||
|
||||
// Kontrola: odczyt po zapisie
|
||||
//uint8_t verify = 0;
|
||||
//read8(ADXL345_REG_DATA_FORMAT, verify);
|
||||
// tu możesz logować verify
|
||||
|
||||
// Wróć do MEASURE
|
||||
write8(ADXL345_REG_POWER_CTL, ADXL345_POWER_MEASURE);
|
||||
scale_g_per_lsb = fullRes ? 0.0039f : (1.0f/256.0f) * (2 << (uint8_t)r);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint8_t ADXL345FreshSPI::odrCodeFromHz(float hz) {
|
||||
struct { uint8_t code; float f; } map[] = {
|
||||
{0x06, 6.25f},{0x07,12.5f},{0x08,25.f},{0x09,50.f},{0x0A,100.f},
|
||||
{0x0B,200.f},{0x0C,400.f},{0x0D,800.f},{0x0E,1600.f},{0x0F,3200.f}
|
||||
};
|
||||
uint8_t best=0x0A; float bestErr=1e9f;
|
||||
for (auto &e: map){ float err=fabsf(e.f-hz); if (err<bestErr){bestErr=err; best=e.code;}}
|
||||
return best;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::setODR_Hz(float hz) {
|
||||
return write8(ADXL345_REG_BW_RATE, odrCodeFromHz(hz));
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::enableFIFO(FIFOmode mode, uint8_t triggerLevel) {
|
||||
triggerLevel = constrain(triggerLevel, (uint8_t)1, (uint8_t)32);
|
||||
uint8_t m = ADXL345_FIFO_BYPASS;
|
||||
switch(mode){
|
||||
case FIFOmode::BYPASS: m = ADXL345_FIFO_BYPASS; break;
|
||||
case FIFOmode::FIFO: m = ADXL345_FIFO_FIFO; break;
|
||||
case FIFOmode::STREAM: m = ADXL345_FIFO_STREAM; break;
|
||||
case FIFOmode::TRIGGER: m = ADXL345_FIFO_TRIGGER;break;
|
||||
}
|
||||
return write8(ADXL345_REG_FIFO_CTL, (uint8_t)(m | ((triggerLevel-1) & 0x1F)));
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::enableDataReadyInterrupt(bool enable) {
|
||||
uint8_t ie=0; if (!read8(ADXL345_REG_INT_ENABLE, ie)) return false;
|
||||
if (enable) ie |= ADXL345_DATA_READY_BIT;
|
||||
else ie &= ~ADXL345_DATA_READY_BIT;
|
||||
return write8(ADXL345_REG_INT_ENABLE, ie);
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::available() {
|
||||
uint8_t src=0; if (!read8(ADXL345_REG_INT_SOURCE, src)) return false;
|
||||
return (src & ADXL345_DATA_READY_BIT) != 0;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::readFresh(SampleI16& out, uint32_t timeout_ms) {
|
||||
uint32_t start = millis();
|
||||
while (!available()) {
|
||||
if ((millis() - start) > timeout_ms) return false;
|
||||
delayMicroseconds(200);
|
||||
}
|
||||
uint8_t buf[6];
|
||||
if (!readMulti(ADXL345_REG_DATAX0, buf, 6)) return false;
|
||||
out.x = u8pair_to_i16(buf[0], buf[1]);
|
||||
out.y = u8pair_to_i16(buf[2], buf[3]);
|
||||
out.z = u8pair_to_i16(buf[4], buf[5]);
|
||||
out.ts_us = micros();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::readFresh(SampleSI& out, uint32_t timeout_ms) {
|
||||
SampleI16 raw;
|
||||
if (!readFresh(raw, timeout_ms)) return false;
|
||||
countsToSI(raw, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ADXL345FreshSPI::readFIFOBurst(SampleI16* buf, size_t maxCount) {
|
||||
if (!buf || maxCount==0) return 0;
|
||||
uint8_t status=0; if (!read8(ADXL345_REG_FIFO_STATUS, status)) return 0;
|
||||
uint8_t entries = status & 0x3F; // 0..32
|
||||
size_t n = min<size_t>(entries, maxCount);
|
||||
for (size_t i=0;i<n;i++){
|
||||
uint8_t d[6];
|
||||
if (!readMulti(ADXL345_REG_DATAX0, d, 6)) return i;
|
||||
buf[i].x = u8pair_to_i16(d[0], d[1]);
|
||||
buf[i].y = u8pair_to_i16(d[2], d[3]);
|
||||
buf[i].z = u8pair_to_i16(d[4], d[5]);
|
||||
buf[i].ts_us = micros();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t ADXL345FreshSPI::readFIFOBurst(SampleSI* buf, size_t maxCount) {
|
||||
if (!buf || maxCount==0) return 0;
|
||||
size_t n = 0;
|
||||
while (n < maxCount) {
|
||||
uint8_t status=0; if (!read8(ADXL345_REG_FIFO_STATUS, status)) break;
|
||||
uint8_t entries = status & 0x3F;
|
||||
if (entries == 0) break;
|
||||
uint8_t d[6];
|
||||
uint32_t t = micros();
|
||||
if (!readMulti(ADXL345_REG_DATAX0, d, 6)) break;
|
||||
SampleI16 s;
|
||||
s.x = u8pair_to_i16(d[0], d[1]);
|
||||
s.y = u8pair_to_i16(d[2], d[3]);
|
||||
s.z = u8pair_to_i16(d[4], d[5]);
|
||||
s.ts_us = t;
|
||||
countsToSI(s, buf[n]);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void ADXL345FreshSPI::countsToSI(const SampleI16& in, SampleSI& out) {
|
||||
out.ax_g = in.x * scale_g_per_lsb;
|
||||
out.ay_g = in.y * scale_g_per_lsb;
|
||||
out.az_g = in.z * scale_g_per_lsb;
|
||||
out.ax_ms2 = out.ax_g * g_ms2;
|
||||
out.ay_ms2 = out.ay_g * g_ms2;
|
||||
out.az_ms2 = out.az_g * g_ms2;
|
||||
out.ts_us = in.ts_us;
|
||||
}
|
||||
|
||||
// ---------- Low-level SPI -----------
|
||||
bool ADXL345FreshSPI::write8(uint8_t reg, uint8_t val) {
|
||||
spi->beginTransaction(SPISettings(spiHz, MSBFIRST, SPI_MODE3));
|
||||
spiSelect();
|
||||
spi->transfer(reg & 0x3F); // write, single
|
||||
spi->transfer(val);
|
||||
spiDeselect();
|
||||
spi->endTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::read8(uint8_t reg, uint8_t& val) {
|
||||
spi->beginTransaction(SPISettings(spiHz, MSBFIRST, SPI_MODE3));
|
||||
spiSelect();
|
||||
spi->transfer(0x80 | (reg & 0x3F)); // read, single
|
||||
val = spi->transfer(0x00);
|
||||
spiDeselect();
|
||||
spi->endTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ADXL345FreshSPI::readMulti(uint8_t reg, uint8_t *dst, size_t n) {
|
||||
if (n==0) return true;
|
||||
spi->beginTransaction(SPISettings(spiHz, MSBFIRST, SPI_MODE3));
|
||||
spiSelect();
|
||||
spi->transfer(0xC0 | (reg & 0x3F)); // read, multi (MB=1, R/W=1)
|
||||
for (size_t i=0;i<n;i++) dst[i] = spi->transfer(0x00);
|
||||
spiDeselect();
|
||||
spi->endTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADXL345FreshSPI::spiSelect() { digitalWrite(cs, LOW); }
|
||||
void ADXL345FreshSPI::spiDeselect() { digitalWrite(cs, HIGH); }
|
||||
|
||||
|
||||
uint8_t ADXL345FreshSPI::getADXLRange() {
|
||||
uint8_t format = 0;
|
||||
if (!read8(0x31, format)) return 255; // albo 0
|
||||
switch (format & 0x03) {
|
||||
case 0: return 2;
|
||||
case 1: return 4;
|
||||
case 2: return 8;
|
||||
case 3: return 16;
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
|
||||
bool ADXL345FreshSPI::getADXLFullRes() {
|
||||
uint8_t format = 0;
|
||||
if (!read8(0x31, format)) return false;
|
||||
//bool ok = read8(0x31, format);
|
||||
//ESP_LOGI(TAG_FRESH, "read8 ok=%d DATA_FORMAT=0x%02X", ok, format);
|
||||
return (format & 0x08) != 0;
|
||||
}
|
||||
|
||||
void ADXL345FreshSPI::showRangeFull(String txt) {
|
||||
uint8_t format = getADXLRange();
|
||||
bool full_res = getADXLFullRes();
|
||||
ESP_LOGI(TAG_FRESH, "%s ADXL345: RANGE=%dG FULL_RES=%d", txt, format, full_res);
|
||||
}
|
||||
184
src/Config.cpp
Normal file
184
src/Config.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <Config.h>
|
||||
|
||||
Config config;
|
||||
|
||||
ConfigManager::ConfigManager() {}
|
||||
|
||||
// EEPROM initialize
|
||||
void ConfigManager::begin() {
|
||||
ESP_LOGI(TAG_CONF, "Begin config");
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
if (isEEPROMEmpty()) {
|
||||
ESP_LOGI(TAG_CONF, "EEPROM Empty");
|
||||
resetToDefaults();
|
||||
generateApiKey(config.apiKey, sizeof(config.apiKey));
|
||||
saveConfig();
|
||||
readConfig();
|
||||
} else {
|
||||
//Logger::getInstance().log(LOG_INFO, "READ Config");
|
||||
readConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// Check is EEPROM is empty
|
||||
bool ConfigManager::isEEPROMEmpty() {
|
||||
if (EEPROM.read(0) != 251){
|
||||
ESP_LOGI(TAG_CONF, "EEPROM is new!");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read configuration from EEPROM
|
||||
void ConfigManager::readConfig() {
|
||||
ESP_LOGI(TAG_CONF, "Read config from EEPROM");
|
||||
EEPROM.get(1, config);
|
||||
}
|
||||
|
||||
// Save config to EEPROM
|
||||
void ConfigManager::saveConfig() {
|
||||
ESP_LOGI(TAG_CONF, "SAVE CONFIG");
|
||||
EEPROM.put(1, config);
|
||||
EEPROM.write(0, 251);
|
||||
if (EEPROM.commit()) {
|
||||
ESP_LOGI(TAG_CONF, "Config saved");
|
||||
} else {
|
||||
ESP_LOGE(TAG_CONF, "Error save config");
|
||||
}
|
||||
EEPROM.end();
|
||||
}
|
||||
|
||||
void ConfigManager::generateApiKey(uint8_t *buf, size_t len) {
|
||||
for (size_t i = 0; i < len; i += 4) {
|
||||
uint32_t r = esp_random(); // losowe 32 bity z TRNG ESP32
|
||||
size_t chunk = (len - i >= 4) ? 4 : (len - i); // ostatnia iteracja może być < 4 bajtów
|
||||
memcpy(buf + i, &r, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
// Factory reset EEPROM
|
||||
void ConfigManager::resetToDefaults() {
|
||||
ESP_LOGI(TAG_CONF, "EEPROM RESET FACTORY");
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
for (int i = 0; i < EEPROM_SIZE; i++) {
|
||||
EEPROM.write(i, 0);
|
||||
}
|
||||
EEPROM.write(0, 0);
|
||||
strcpy(config.ssid, "politechnika");
|
||||
strcpy(config.password, "");
|
||||
strcpy(config.hostname, "WMT001");
|
||||
strcpy(config.place, "WMT Stalowa Wola");
|
||||
config.dhcp = 1;
|
||||
strcpy(config.ip, "192.168.0.10");
|
||||
strcpy(config.subnet, "255.255.255.0");
|
||||
strcpy(config.gateway, "192.168.0.1");
|
||||
strcpy(config.dns, "8.8.8.8");
|
||||
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.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.S0, "ACCEL1");
|
||||
strcpy(config.S1, "ACCEL2");
|
||||
strcpy(config.S2, "ACCEL3");
|
||||
strcpy(config.S3, "ACCEL4");
|
||||
strcpy(config.S4, "ACCEL5");
|
||||
strcpy(config.S5, "ACCEL6");
|
||||
strcpy(config.S6, "ACCEL7");
|
||||
strcpy(config.S7, "ACCEL8");
|
||||
EEPROM.put(1, config);
|
||||
EEPROM.write(0, 251);
|
||||
saveConfig();
|
||||
readConfig();
|
||||
isRebootRequired = true;
|
||||
}
|
||||
|
||||
void ConfigManager::showConfig(){
|
||||
ESP_LOGI(TAG_CONF, "Config size: %d bytes max %d", sizeof(config), EEPROM_SIZE);
|
||||
String ii;
|
||||
|
||||
if(config.connect)
|
||||
ii = "online";
|
||||
else
|
||||
ii = "offline";
|
||||
ESP_LOGI(TAG_CONF, "Mode: %s", ii.c_str());
|
||||
|
||||
if(config.measure)
|
||||
ii = "auto";
|
||||
else
|
||||
ii = "manual";
|
||||
ESP_LOGI(TAG_CONF, "MEASURE: %s", ii.c_str());
|
||||
|
||||
ii = "PLACE: " + String(config.place);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "HOSTNAME: " + String(config.hostname);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
|
||||
ii = "WIFI SSID: " + String(config.ssid);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
|
||||
ii = "WIFI PASS: " + String(config.password);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
|
||||
if(config.dhcp)
|
||||
ii = "yes";
|
||||
else
|
||||
ii = "no";
|
||||
ESP_LOGI(TAG_CONF, "DHCP: %s", ii.c_str());
|
||||
|
||||
ii = "IP: " + String(config.ip);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "MASK: " + String(config.subnet);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "GATEWAY: " + String(config.gateway);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "DNS: " + String(config.dns);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "USER: " + String(config.user);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "USER PASS: " + String(config.pass);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
if(config.connect) {
|
||||
ii = "URL: " + String(config.restURL);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "PORT: " + String(config.restPort);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "USER: " + String(config.restUser);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "PASS: " + String(config.restPass);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
if (config.apiKey[i] < 0x10) Serial.print("0");
|
||||
Serial.print(config.apiKey[i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
ii = "Delay: " + String(config.pause) + "ms";
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S1: " + String(config.S0);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S2: " + String(config.S1);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S3: " + String(config.S2);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S4: " + String(config.S3);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S5: " + String(config.S4);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S6: " + String(config.S5);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S7: " + String(config.S6);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
ii = "S8: " + String(config.S7);
|
||||
ESP_LOGI(TAG_CONF, "%s", ii.c_str());
|
||||
}
|
||||
216
src/Display.cpp
Normal file
216
src/Display.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
#include <Arduino.h>
|
||||
#include "Display.h"
|
||||
|
||||
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;
|
||||
_columns = columns;
|
||||
_rows = rows;
|
||||
}
|
||||
|
||||
bool Display::begin(TwoWire *wire) {
|
||||
wire->begin();
|
||||
wire->beginTransmission(_address);
|
||||
if (wire->endTransmission() != 0) {
|
||||
return false; // brak odpowiedzi — error
|
||||
}
|
||||
|
||||
// Inicjalizacja LCD
|
||||
_lcd->begin(_columns, _rows);
|
||||
_lcd->backlight();
|
||||
_lcd->clear();
|
||||
delay(100);
|
||||
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) {
|
||||
clear();
|
||||
textCenter(0, "** Measure params **");
|
||||
String conting = measure ? "yes":"no";
|
||||
String st2 = "Continous:";
|
||||
String allt = st2 + conting;
|
||||
textCenter(1, allt.c_str());
|
||||
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);
|
||||
}
|
||||
|
||||
void Display::clear() {
|
||||
_lcd->clear();
|
||||
}
|
||||
|
||||
void Display::mainScreen() {
|
||||
// Twoja logika mainScreen
|
||||
_lcd->clear();
|
||||
textCenter(1, "MAIN SCREEN");
|
||||
}
|
||||
|
||||
void Display::print(String text) {
|
||||
_lcd->print(text);
|
||||
}
|
||||
|
||||
void Display::println(String text) {
|
||||
_lcd->print(text);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
_lcd->setCursor(0, 1);
|
||||
_lcd->print("SD FREE: ");
|
||||
_lcd->print(freeSpace, 2);
|
||||
_lcd->print(" GB ");
|
||||
|
||||
if ((oadxlcnt != count) || (omode != config.measure) || (refresh)){
|
||||
oadxlcnt = count;
|
||||
omode = config.measure;
|
||||
_lcd->setCursor(0, 2);
|
||||
_lcd->print("ADXL:");
|
||||
_lcd->print(count);
|
||||
_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 ? "*" : " ");
|
||||
}
|
||||
}
|
||||
|
||||
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->setCursor(0, 2);
|
||||
_lcd->print(myFile);
|
||||
}
|
||||
|
||||
void Display::displaySampleRateSummary(uint32_t reccount, uint32_t captureSeconds, float khz, String filename){
|
||||
clear();
|
||||
_lcd->setCursor(0, 0);
|
||||
if (captureSeconds == 0) {
|
||||
_lcd->print("Sampling time 0!");
|
||||
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");
|
||||
}
|
||||
16
src/Logger.cpp
Normal file
16
src/Logger.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <Logger.h>
|
||||
|
||||
const char *TAG_MAIN = "MAIN";
|
||||
const char *TAG_DISPLAY = "DISPLAY";
|
||||
const char *TAG_WIFI = "WiFi";
|
||||
const char *TAG_ADXL = "ADXL345";
|
||||
const char *TAG_CONF = "CONFIG";
|
||||
|
||||
|
||||
void init_log_levels() {
|
||||
esp_log_level_set(TAG_MAIN, ESP_LOG_INFO);
|
||||
esp_log_level_set(TAG_DISPLAY, ESP_LOG_INFO);
|
||||
esp_log_level_set(TAG_WIFI, ESP_LOG_INFO);
|
||||
esp_log_level_set(TAG_ADXL, ESP_LOG_INFO);
|
||||
esp_log_level_set(TAG_CONF, ESP_LOG_INFO);
|
||||
}
|
||||
594
src/Measure.cpp
Normal file
594
src/Measure.cpp
Normal file
@@ -0,0 +1,594 @@
|
||||
#include <Measure.h>
|
||||
|
||||
static const char *TAG_CAPTURE = "CAPTURE";
|
||||
|
||||
// Był na sztywno przydzielony bufor 128 KB. Zmieniam tak, aby zaalokować 4 MB lub 6 MB (trzeba zostawić resztę na potrzeby systemu).
|
||||
DataCapture::DataCapture(ADXL345FastSPI &adxl, Display &display, RTC_DS3231 &rtc, fs::FS &storage, size_t bufferSize)
|
||||
: adxl_(adxl), display_(display), rtc_(rtc), _fs(storage){
|
||||
// Ustawiamy duży bufor dla PSRAM (np. 4 MB = 4194304 bajty)
|
||||
// 4 * 1024 * 1024 / 12 bajtów = ~349 525 próbek
|
||||
// Możesz to przekazać jako parametr lub wpisać na sztywno:
|
||||
if (bufferSize < 4 * 1024 * 1024) bufferSize = 4 * 1024 * 1024;
|
||||
bufferSize_ = bufferSize;
|
||||
|
||||
#if defined(ESP32)
|
||||
// ps_malloc jest kluczowy dla PSRAM. Wymuszamy alokację w zewnętrznym PSRAM
|
||||
buffer_ = (uint8_t*)ps_malloc(bufferSize_);
|
||||
#endif
|
||||
|
||||
if (!buffer_) buffer_ = (uint8_t*)malloc(bufferSize_);
|
||||
if (!buffer_) {
|
||||
ESP_LOGE(TAG_CAPTURE, "No mem for buffer (%u B)", (unsigned)bufferSize_);
|
||||
} else {
|
||||
ESP_LOGI(TAG_CAPTURE, "Capture buffer: %u B", (unsigned)bufferSize_);
|
||||
}
|
||||
}
|
||||
|
||||
DataCapture::~DataCapture() {
|
||||
if (buffer_) { free(buffer_); buffer_ = nullptr; }
|
||||
}
|
||||
|
||||
void DataCapture::stop() { measurementActive_ = false; }
|
||||
|
||||
// --- ZAPIS BUFORA NA SD ---
|
||||
bool DataCapture::flushToFile(File& f) {
|
||||
if (bufferIndex_ == 0) return true;
|
||||
Watchdog::feed();
|
||||
size_t written = f.write(buffer_, bufferIndex_);
|
||||
if (written != bufferIndex_) {
|
||||
ESP_LOGE(TAG_CAPTURE, "SD save error (written=%u, expected=%u)", (unsigned)written, (unsigned)bufferIndex_);
|
||||
display_.textStatus("SD save error");
|
||||
bufferIndex_ = 0;
|
||||
return false;
|
||||
}
|
||||
bufferIndex_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// CRC8 (zostawione – nieużywane domyślnie)
|
||||
uint8_t DataCapture::crc8(const uint8_t* data, size_t len) {
|
||||
uint8_t crc = 0x00;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t b = 0; b < 8; ++b) {
|
||||
if (crc & 0x80) crc = (crc << 1) ^ 0x07; else crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void DataCapture::setTestingIndicator_(bool on, String myDir, String myFile) {
|
||||
display_.displayOnOffM(on, myDir, myFile);
|
||||
}
|
||||
|
||||
// --- automatyczna nazwa pliku ---
|
||||
bool DataCapture::captureAuto(uint32_t captureSeconds, const char * /*baseDirectory*/) {
|
||||
isExit = false;
|
||||
Watchdog::feed();
|
||||
String path = allocateNextFilePath();
|
||||
if (path.isEmpty()) {
|
||||
ESP_LOGE(TAG_CAPTURE, "Error generate file name in %s", _baseDir.c_str());
|
||||
display_.textStatus("Error file");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG_CAPTURE, "Autogenerate file: %s", path.c_str());
|
||||
display_.textStatus(path.c_str());
|
||||
Watchdog::feed();
|
||||
return capture(captureSeconds, path.c_str());
|
||||
}
|
||||
|
||||
// --- main measure function ---
|
||||
// bool DataCapture::capture(uint32_t captureSeconds, const char *filename) {
|
||||
// Watchdog::feed();
|
||||
// if (!buffer_) {
|
||||
// ESP_LOGE(TAG_CAPTURE, "No buffer - cancel.");
|
||||
// display_.textStatus("Buffer error");
|
||||
// return false;
|
||||
// }
|
||||
// ESP_LOGI(TAG_CAPTURE, "Capture %u sec. -> %s", (unsigned)captureSeconds, filename);
|
||||
|
||||
// File dataFile = _fs.open(filename, FILE_WRITE);
|
||||
// if (!dataFile) {
|
||||
// ESP_LOGE(TAG_CAPTURE, "Can't open file: %s", filename);
|
||||
// char buf[100];
|
||||
// snprintf(buf, sizeof(buf), "Can't open: %s", filename);
|
||||
// display_.textStatus(buf);
|
||||
// //s2etTestingIndicator_(false, _baseDir, filename);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Bufory RAMKI (po 1 próbce z każdego sensora)
|
||||
// static const uint8_t MAXN = 7; // zgodnie z projektem
|
||||
// int16_t X[MAXN]{}, Y[MAXN]{}, Z[MAXN]{};
|
||||
// uint32_t TS[MAXN]{};
|
||||
|
||||
// Watchdog::feed();
|
||||
// const uint32_t tStart_us = micros();
|
||||
// const uint32_t captureDuration_us = captureSeconds * 1000000UL;
|
||||
|
||||
// // Czas startu (UTC) — tylko w nagłówku!
|
||||
// const DateTime now = rtc_.now();
|
||||
// const int32_t unix_start = now.unixtime();
|
||||
|
||||
// // Nagłówek
|
||||
// FileHeader hdr;
|
||||
// memcpy(hdr.magic, "WMT", 3);
|
||||
// hdr.version = 1;
|
||||
// hdr.headerSize = sizeof(FileHeader);
|
||||
// hdr.sampleSize = sizeof(Sample);
|
||||
// hdr.timestamp = unix_start;
|
||||
// hdr.reccount = 0;
|
||||
|
||||
// if (dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr)) != sizeof(hdr)) {
|
||||
// ESP_LOGE(TAG_CAPTURE, "Header write failed");
|
||||
// display_.textStatus("Header SD failed");
|
||||
// dataFile.close();
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// measurementActive_ = true;
|
||||
// bufferIndex_ = 0;
|
||||
// setTestingIndicator_(true, _baseDir, filename);
|
||||
|
||||
// uint32_t frames = 0; // liczba zebranych „ramek”
|
||||
// const uint8_t presentCnt = adxl_.size(); // wykryte sensory (wg begin)
|
||||
|
||||
// // --- Pętla akwizycji wyrównanych ramek ---
|
||||
// while (measurementActive_) {
|
||||
// if(isEscape()) break; // wyjście z pętli gdy OK przerwie
|
||||
// uint32_t now_us = micros();
|
||||
// if ((now_us - tStart_us) >= captureDuration_us) break;
|
||||
|
||||
// // 1) Czekamy aż KAŻDY obecny sensor ma >= 1 próbkę w FIFO (DATA_READY)
|
||||
// while (!adxl_.availableAll()) {
|
||||
// if(isEscape()) break; //????? sprawdź kHz pomiaru!
|
||||
// // krótki spin; unikamy delay(1), aby nie zrywać 3.2 kHz
|
||||
// // (opcjonalnie yield(); jeśli system wymaga)
|
||||
// }
|
||||
|
||||
// // 2) Zdejmij po 1 NAJSTARSZEJ próbce z każdego sensora (STREAM FIFO)
|
||||
// const uint8_t got = adxl_.readAlignedOnce(X, Y, Z, TS);
|
||||
// if (got != presentCnt) {
|
||||
// // rzadki przypadek – niepełna ramka; pomiń
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // 3) Wspólny timestamp ramki: minimalny z TS[i]
|
||||
// uint32_t tmin = UINT32_MAX;
|
||||
// for (uint8_t i = 0; i < MAXN; ++i) {
|
||||
// if (!adxl_.isPresent(i)) continue;
|
||||
// if (TS[i] < tmin) tmin = TS[i];
|
||||
// }
|
||||
// const uint32_t frame_offset_us = tmin - tStart_us;
|
||||
|
||||
// // 4) Zapis 7 rekordów Sample do bufora
|
||||
// for (uint8_t i = 0; i < MAXN; ++i) {
|
||||
// if (!adxl_.isPresent(i)) continue;
|
||||
|
||||
// // Konstruuj rekord bezpośrednio w buforze (bez memcpy)
|
||||
// if (bufferIndex_ + sizeof(Sample) > bufferSize_) {
|
||||
// if (!flushToFile(dataFile)) { measurementActive_ = false; break; }
|
||||
// }
|
||||
// Sample *dst = reinterpret_cast<Sample*>(buffer_ + bufferIndex_);
|
||||
// dst->offset = frame_offset_us;
|
||||
// dst->sensor_id = i;
|
||||
// dst->x = X[i]; dst->y = Y[i]; dst->z = Z[i];
|
||||
// dst->ready = true;
|
||||
// bufferIndex_ += sizeof(Sample);
|
||||
// }
|
||||
|
||||
// if (!measurementActive_) break;
|
||||
// frames++;
|
||||
// // Watchdog rzadziej, żeby nie zwiększać jittera
|
||||
// if ((frames & 0xFF) == 0) Watchdog::feed();
|
||||
// } // while
|
||||
// // Statystyki
|
||||
// ESP_LOGI(TAG_CAPTURE, "Frames: %u, sensors: %u", (unsigned)frames, (unsigned)presentCnt);
|
||||
// printSamplingRate(frames, captureSeconds); // liczymy ramki/sek.
|
||||
|
||||
// // Domknij bufor
|
||||
// if (bufferIndex_ > 0) {
|
||||
// if (!flushToFile(dataFile)) {
|
||||
// Watchdog::feed();
|
||||
// ESP_LOGE(TAG_CAPTURE, "Finish save error.");
|
||||
// display_.textStatus("Save SD error");
|
||||
// delay(1000);
|
||||
// }
|
||||
// }
|
||||
// ESP_LOGI(TAG_CAPTURE, "Saved to SD successful");
|
||||
// display_.textStatus("Saved SD OK");
|
||||
// delay(700);
|
||||
// // Uzupełnij nagłówek o liczbę rekordów (Sample)
|
||||
// hdr.reccount = frames * presentCnt;
|
||||
// dataFile.seek(0);
|
||||
// dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr));
|
||||
// dataFile.flush();
|
||||
// dataFile.close();
|
||||
|
||||
// //setTestingIndicator_(false, _baseDir, filename);
|
||||
|
||||
// bool ok = measurementActive_;
|
||||
// measurementActive_ = false;
|
||||
|
||||
// if (ok) {
|
||||
// ESP_LOGI(TAG_CAPTURE, "Successful");
|
||||
// display_.textStatus("Successfull");
|
||||
// Watchdog::feed();
|
||||
// return true;
|
||||
// } else {
|
||||
// ESP_LOGE(TAG_CAPTURE, "Measurement aborted");
|
||||
// display_.textStatus("Aborted");
|
||||
// Watchdog::feed();
|
||||
// return false;f
|
||||
// }
|
||||
// }
|
||||
|
||||
// --- main measure function --- V2
|
||||
bool DataCapture::capture(uint32_t captureSeconds, const char *filename) {
|
||||
Watchdog::feed();
|
||||
if (!buffer_) {
|
||||
ESP_LOGE(TAG_CAPTURE, "No buffer - cancel.");
|
||||
display_.textStatus("Buffer error");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obliczamy ile danych maksymalnie może wejść do bufora
|
||||
const uint8_t presentCnt = adxl_.size();
|
||||
const size_t frameSize = presentCnt * sizeof(Sample);
|
||||
|
||||
ESP_LOGI(TAG_CAPTURE, "Start RAM capture: %u sec.", (unsigned)captureSeconds);
|
||||
display_.textStatus("Sampling...");
|
||||
|
||||
static const uint8_t MAXN = 7;
|
||||
int16_t X[MAXN]{}, Y[MAXN]{}, Z[MAXN]{};
|
||||
uint32_t TS[MAXN]{};
|
||||
|
||||
measurementActive_ = true;
|
||||
bufferIndex_ = 0;
|
||||
uint32_t frames = 0;
|
||||
|
||||
const uint32_t tStart_us = micros();
|
||||
const uint32_t captureDuration_us = captureSeconds * 1000000UL;
|
||||
const DateTime now_start = rtc_.now(); // Czas dla nagłówka
|
||||
|
||||
// --- KRYTYCZNA PĘTLA POMIAROWA (ZERO SD) ---
|
||||
while (measurementActive_) {
|
||||
if(isEscape()) break;
|
||||
uint32_t now_us = micros();
|
||||
if ((now_us - tStart_us) >= captureDuration_us) break;
|
||||
|
||||
// Sprawdzenie czy mamy miejsce w PSRAM na kolejną pełną ramkę
|
||||
if (bufferIndex_ + frameSize > bufferSize_) {
|
||||
ESP_LOGW(TAG_CAPTURE, "PSRAM Buffer Full! Stopping.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Czekamy na dane z sensora
|
||||
while (!adxl_.availableAll()) {
|
||||
if(isEscape()) break;
|
||||
}
|
||||
|
||||
const uint8_t got = adxl_.readAlignedOnce(X, Y, Z, TS);
|
||||
if (got != presentCnt) continue;
|
||||
|
||||
// Wspólny timestamp ramki
|
||||
uint32_t tmin = UINT32_MAX;
|
||||
for (uint8_t i = 0; i < MAXN; ++i) {
|
||||
if (!adxl_.isPresent(i)) continue;
|
||||
if (TS[i] < tmin) tmin = TS[i];
|
||||
}
|
||||
const uint32_t frame_offset_us = tmin - tStart_us;
|
||||
|
||||
// Zapis do PSRAM
|
||||
for (uint8_t i = 0; i < MAXN; ++i) {
|
||||
if (!adxl_.isPresent(i)) continue;
|
||||
Sample *dst = reinterpret_cast<Sample*>(buffer_ + bufferIndex_);
|
||||
dst->offset = frame_offset_us;
|
||||
dst->sensor_id = i;
|
||||
dst->x = X[i]; dst->y = Y[i]; dst->z = Z[i];
|
||||
dst->ready = true;
|
||||
bufferIndex_ += sizeof(Sample);
|
||||
}
|
||||
frames++;
|
||||
if ((frames & 0x3FF) == 0) Watchdog::feed(); // Rzadziej, by nie siać jittera
|
||||
} // --- KONIEC PĘTLI POMIAROWEJ ---
|
||||
|
||||
measurementActive_ = false;
|
||||
ESP_LOGI(TAG_CAPTURE, "Capture finished. Samples in RAM: %u. Writing to SD...", (unsigned)frames);
|
||||
display_.textStatus("Saving to SD...");
|
||||
|
||||
// Dopiero teraz otwieramy plik na SD
|
||||
File dataFile = _fs.open(filename, FILE_WRITE);
|
||||
if (!dataFile) {
|
||||
ESP_LOGE(TAG_CAPTURE, "Can't open SD file!");
|
||||
display_.textStatus("SD Open Error");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Przygotowanie nagłówka
|
||||
FileHeader hdr;
|
||||
memcpy(hdr.magic, "WMT", 3);
|
||||
hdr.version = 1;
|
||||
hdr.headerSize = sizeof(FileHeader);
|
||||
hdr.sampleSize = sizeof(Sample);
|
||||
hdr.timestamp = now_start.unixtime();
|
||||
hdr.reccount = frames * presentCnt;
|
||||
|
||||
// Zapis nagłówka
|
||||
dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr));
|
||||
|
||||
// Zapis całego bufora PSRAM jednym ciągiem
|
||||
size_t written = dataFile.write(buffer_, bufferIndex_);
|
||||
|
||||
if (written == bufferIndex_) {
|
||||
ESP_LOGI(TAG_CAPTURE, "SD Save Successful: %u bytes", (unsigned)written);
|
||||
//display_.textStatus("Save OK");
|
||||
display_.textStatus(basenameFromPath(filename));
|
||||
delay(2000); // Tutaj zrob to inaczej
|
||||
} else {
|
||||
ESP_LOGE(TAG_CAPTURE, "SD Write Error!");
|
||||
display_.textStatus("SD Write Err");
|
||||
}
|
||||
|
||||
dataFile.close();
|
||||
printSamplingRate(frames, captureSeconds, filename);
|
||||
return (written == bufferIndex_);
|
||||
}
|
||||
|
||||
|
||||
// --- Narzędzia SD i pierdoły ---
|
||||
String DataCapture::unixToDateTime(uint32_t ts) {
|
||||
DateTime dt(ts);
|
||||
char buf[25];
|
||||
snprintf(buf, sizeof(buf), "%04u-%02u-%02u %02u:%02u:%02u",
|
||||
dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second());
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
void DataCapture::readHeaderAndPrint(const char *path) {
|
||||
Watchdog::feed();
|
||||
File f = SD.open(path, FILE_READ);
|
||||
if (!f) { ESP_LOGE(TAG_CAPTURE,"Error open file %s", path); return; }
|
||||
|
||||
FileHeader hdr;
|
||||
size_t rd = f.read((uint8_t*)&hdr, sizeof(hdr));
|
||||
f.close();
|
||||
if (rd != sizeof(hdr)) { ESP_LOGE(TAG_CAPTURE,"Header error"); return; }
|
||||
|
||||
if (memcmp(hdr.magic, "WMT", 3) != 0) { ESP_LOGE(TAG_CAPTURE,"File signature error"); return; }
|
||||
|
||||
ESP_LOGI(TAG_CAPTURE,"===== WMT file header =====");
|
||||
ESP_LOGI(TAG_CAPTURE,"Signature: %.3s", hdr.magic);
|
||||
ESP_LOGI(TAG_CAPTURE,"Version: %u", hdr.version);
|
||||
ESP_LOGI(TAG_CAPTURE,"Header size:%u B", hdr.headerSize);
|
||||
ESP_LOGI(TAG_CAPTURE,"Sample size:%u B", hdr.sampleSize);
|
||||
ESP_LOGI(TAG_CAPTURE,"Timestamp: %d", hdr.timestamp);
|
||||
ESP_LOGI(TAG_CAPTURE,"Reccount: %u", hdr.reccount);
|
||||
ESP_LOGI(TAG_CAPTURE,"===========================");
|
||||
}
|
||||
|
||||
void DataCapture::printSamplingRate(uint32_t reccount, uint32_t captureSeconds, String filename) {
|
||||
Watchdog::feed();
|
||||
if (captureSeconds == 0) { ESP_LOGW(TAG_CAPTURE, "Sampling time = 0!"); return; }
|
||||
float fs = (float)reccount / (float)captureSeconds; // Hz (ramki/s)
|
||||
float fs_kHz = fs / 1000.0f;
|
||||
ESP_LOGI(TAG_CAPTURE,"Frames: %u", (unsigned)reccount);
|
||||
ESP_LOGI(TAG_CAPTURE,"Time: %u s", (unsigned)captureSeconds);
|
||||
ESP_LOGI(TAG_CAPTURE,"Rate: %.3f kHz (frames)", fs_kHz);
|
||||
display_.displaySampleRateSummary(reccount, captureSeconds, fs_kHz, filename);
|
||||
display_.textStatus("Summary");
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
// --- Zarządzanie katalogami/plikiem ---
|
||||
String DataCapture::allocateNextFilePath() {
|
||||
Watchdog::feed();
|
||||
uint32_t highestDir = findHighestNumericDir();
|
||||
if (highestDir == 0) {
|
||||
highestDir = 1;
|
||||
if (!ensureDir(highestDir)) return String();
|
||||
}
|
||||
uint32_t count = 0, highestIdx = 0;
|
||||
scanDirForWmt(highestDir, count, highestIdx);
|
||||
|
||||
uint32_t targetDir = highestDir;
|
||||
uint32_t nextIdx = 0;
|
||||
|
||||
if (count > _maxFilesPerDir) {
|
||||
targetDir = highestDir + 1;
|
||||
if (!ensureDir(targetDir)) return String();
|
||||
nextIdx = 1;
|
||||
} else {
|
||||
nextIdx = (highestIdx == 0) ? 1 : (highestIdx + 1);
|
||||
}
|
||||
return joinPath(dirPath(targetDir), makeIndexedName(nextIdx));
|
||||
}
|
||||
|
||||
String DataCapture::generateNextFilename() {
|
||||
Watchdog::feed();
|
||||
uint32_t highestDir = findHighestNumericDir();
|
||||
if (highestDir == 0) return String();
|
||||
|
||||
uint32_t count = 0, highestIdx = 0;
|
||||
scanDirForWmt(highestDir, count, highestIdx);
|
||||
ESP_LOGI(TAG_CAPTURE, "highestDir %u, count %u, highestIdx %u", (unsigned)highestDir, (unsigned)count, (unsigned)highestIdx);
|
||||
if (highestIdx == 0) return String();
|
||||
return joinPath(dirPath(highestDir), makeIndexedName(highestIdx));
|
||||
}
|
||||
|
||||
FileInfo DataCapture::getLastFileInfo() {
|
||||
FileInfo info{String(), 0u, false};
|
||||
Watchdog::feed();
|
||||
String lastPath = generateNextFilename();
|
||||
if (lastPath.isEmpty()) return info;
|
||||
|
||||
if (_fs.exists(lastPath)) {
|
||||
File f = _fs.open(lastPath, FILE_READ);
|
||||
if (f) {
|
||||
info.exists = true;
|
||||
info.path = lastPath;
|
||||
info.size = f.size();
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool DataCapture::deleteAllOnSD() {
|
||||
Watchdog::feed();
|
||||
File root = _fs.open(_baseDir);
|
||||
if (!root || !root.isDirectory()) return false;
|
||||
|
||||
for (File e = root.openNextFile(); e; e = root.openNextFile()) {
|
||||
Watchdog::feed();
|
||||
String child = String(e.name());
|
||||
e.close();
|
||||
if (!recursiveDelete(child)) return false;
|
||||
}
|
||||
root.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
SpaceInfo DataCapture::freeSpaceMB() {
|
||||
Watchdog::feed();
|
||||
SpaceInfo si{0.0, "UNKNOWN"};
|
||||
#if defined(ESP32)
|
||||
uint64_t total = SD.totalBytes();
|
||||
uint64_t used = SD.usedBytes();
|
||||
if (total >= used) {
|
||||
uint64_t freeB = total - used;
|
||||
const double oneGB = 1024.0 * 1024.0 * 1024.0;
|
||||
const double oneMB = 1024.0 * 1024.0;
|
||||
if (freeB >= (uint64_t)oneGB) {
|
||||
si.value = (double)freeB / oneGB; si.unit = "GB";
|
||||
} else {
|
||||
si.value = (double)freeB / oneMB; si.unit = "MB";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return si;
|
||||
}
|
||||
|
||||
float DataCapture::freeSpaceFloat(bool* isGB) {
|
||||
Watchdog::feed();
|
||||
SpaceInfo si = freeSpaceMB();
|
||||
if (!si.unit || strcmp(si.unit, "UNKNOWN") == 0) {
|
||||
if (isGB) *isGB = false;
|
||||
return -1.0f;
|
||||
}
|
||||
if (isGB) *isGB = (strcmp(si.unit, "GB") == 0);
|
||||
return static_cast<float>(si.value);
|
||||
}
|
||||
|
||||
// --- helpery pomocnicze ---
|
||||
|
||||
bool DataCapture::isAllDigits(const char *s) {
|
||||
if (!s || !*s) return false;
|
||||
while (*s) { if (*s < '0' || *s > '9') return false; ++s; }
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t DataCapture::toUint(const char *s) {
|
||||
uint32_t v = 0; while (*s) { v = v*10u + uint32_t(*s - '0'); ++s; } return v;
|
||||
}
|
||||
|
||||
String DataCapture::dirPath(uint32_t dirNum) const { return joinPath(_baseDir, String(dirNum)); }
|
||||
|
||||
const char *DataCapture::basenameFromPath(const char *full) { const char *slash = strrchr(full, '/'); return slash ? slash + 1 : full; }
|
||||
|
||||
bool DataCapture::isWmtWithDigits(const char* name, uint32_t& idxOut) const {
|
||||
size_t nlen = strlen(name), extLen = _ext.length();
|
||||
if (nlen != (size_t)_digits + extLen) return false;
|
||||
if (strncmp(name + _digits, _ext.c_str(), extLen) != 0) return false;
|
||||
for (uint8_t i = 0; i < _digits; ++i) if (name[i] < '0' || name[i] > '9') return false;
|
||||
char buf[16]; memcpy(buf, name, _digits); buf[_digits] = '\0';
|
||||
idxOut = (uint32_t)strtoul(buf, nullptr, 10); return true;
|
||||
}
|
||||
|
||||
String DataCapture::makeIndexedName(uint32_t idx) const {
|
||||
char fmt[8]; snprintf(fmt, sizeof(fmt), "%%0%ulu", (unsigned long)_digits);
|
||||
char num[24]; snprintf(num, sizeof(num), fmt, (unsigned long)idx);
|
||||
return String(num) + _ext;
|
||||
}
|
||||
|
||||
String DataCapture::joinPath(const String &a, const String &b) { if (a.endsWith("/")) return a + b; return a + "/" + b; }
|
||||
bool DataCapture::ensureDir(uint32_t dirNum) {
|
||||
String path = dirPath(dirNum);
|
||||
if (_fs.exists(path)) { File f = _fs.open(path); bool ok = f && f.isDirectory(); if (f) f.close(); return ok; }
|
||||
return _fs.mkdir(path);
|
||||
}
|
||||
|
||||
uint32_t DataCapture::findHighestNumericDir() {
|
||||
File root = _fs.open(_baseDir);
|
||||
if (!root || !root.isDirectory()) return 0;
|
||||
uint32_t maxDir = 0;
|
||||
for (File f = root.openNextFile(); f; f = root.openNextFile()) {
|
||||
if (f.isDirectory()) {
|
||||
const char *nm = basenameFromPath(f.name());
|
||||
if (isAllDigits(nm)) { uint32_t v = toUint(nm); if (v > maxDir) maxDir = v; }
|
||||
}
|
||||
}
|
||||
return maxDir;
|
||||
}
|
||||
|
||||
void DataCapture::scanDirForWmt(uint32_t dirNum, uint32_t &count, uint32_t &highestIdx) {
|
||||
Watchdog::feed();
|
||||
count = 0; highestIdx = 0;
|
||||
String path = dirPath(dirNum);
|
||||
File dir = _fs.open(path);
|
||||
if (!dir || !dir.isDirectory()) return;
|
||||
for (File f = dir.openNextFile(); f; f = dir.openNextFile()) {
|
||||
if (f.isDirectory()) { Watchdog::feed(); f.close(); continue; }
|
||||
const char* base = basenameFromPath(f.name());
|
||||
uint32_t idx = 0;
|
||||
if (isWmtWithDigits(base, idx)) { count++; if (idx > highestIdx) highestIdx = idx; }
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool DataCapture::recursiveDelete(const String &path) {
|
||||
File e = _fs.open(path);
|
||||
if (!e) return false;
|
||||
if (!e.isDirectory()) { e.close(); return _fs.remove(path); }
|
||||
// katalog
|
||||
for (File c = e.openNextFile(); c; c = e.openNextFile()) {
|
||||
String child = String(c.name()); c.close();
|
||||
if (!recursiveDelete(child)) { e.close(); return false; }
|
||||
}
|
||||
e.close();
|
||||
if (path == _baseDir) return true; // nie usuwamy katalogu bazowego
|
||||
return _fs.rmdir(path);
|
||||
}
|
||||
|
||||
void DataCapture::printLastFileInfoSerial() {
|
||||
Watchdog::feed();
|
||||
const FileInfo info = getLastFileInfo();
|
||||
if (!info.exists) {
|
||||
ESP_LOGE(TAG_CAPTURE, "No .wmt file exists. Last file not exists");
|
||||
return;
|
||||
}
|
||||
|
||||
const double kb = static_cast<double>(info.size) / 1024.0;
|
||||
const double mb = kb / 1024.0;
|
||||
|
||||
#if defined(ESP32)
|
||||
// ESP32 wspiera Serial.printf z %llu
|
||||
ESP_LOGI(TAG_CAPTURE, "Last file: %s", info.path.c_str());
|
||||
ESP_LOGI(TAG_CAPTURE, "Size: %llu B (%.2f KB, %.2f MB)", static_cast<unsigned long long>(info.size), kb, mb);
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "Size:%.2f KB", kb);
|
||||
display_.textStatus(buf);
|
||||
#else
|
||||
// Wariant zachowawczy dla platform bez %llu; pokazujemy rozmiar w MB/KB.
|
||||
ESP_LOGI(TAG_CAPTURE, "Last file info: "));
|
||||
ESP_LOGI(TAG_CAPTURE, info.path);
|
||||
ESP_LOGI(TAG_CAPTURE, "Size: "));
|
||||
ESP_LOGI(TAG_CAPTURE, mb);
|
||||
//ESP_LOGI(TAG_CAPTURE, " MB"));
|
||||
//Serial.print(F(" ("));
|
||||
//Serial.print(kb, 2);
|
||||
//Serial.println(F(" KB)"));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
292
src/Network.cpp
Normal file
292
src/Network.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
#include <Network.h>
|
||||
#include "Watchdog.h" // Dodano dla feed() w portalu
|
||||
|
||||
static const char *WIFI = "wifi";
|
||||
|
||||
WiFiManager::WiFiManager() {}
|
||||
|
||||
void WiFiManager::begin() {
|
||||
ESP_LOGI(WIFI, "Start network");
|
||||
isAccessPoint = config.connect;
|
||||
if (!isAccessPoint) {
|
||||
setupAccessPoint(ssidAP.c_str(), passwordAP.c_str());
|
||||
} else {
|
||||
ESP_LOGI(WIFI, "WiFi client");
|
||||
ReadConnection();
|
||||
connectToWiFi();
|
||||
}
|
||||
getRSSI();
|
||||
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) {
|
||||
uint8_t octets[4];
|
||||
int parsed = sscanf(str, "%hhu.%hhu.%hhu.%hhu", &octets[0], &octets[1], &octets[2], &octets[3]);
|
||||
if (parsed == 4) {
|
||||
ip = IPAddress(octets[0], octets[1], octets[2], octets[3]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WiFiManager::ReadConnection() {
|
||||
ESP_LOGE(WIFI, "Network config error");
|
||||
isAccessPoint = config.connect;
|
||||
useDHCP = config.dhcp;
|
||||
convertCharToIPAddress(config.ip, local_IP);
|
||||
convertCharToIPAddress(config.gateway, gateway);
|
||||
convertCharToIPAddress(config.subnet, subnet);
|
||||
convertCharToIPAddress(config.dns, dns);
|
||||
}
|
||||
|
||||
void WiFiManager::connectToWiFi() {
|
||||
ESP_LOGI(WIFI, "WiFi STA mode");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(config.ssid, config.password);
|
||||
|
||||
if (!useDHCP) {
|
||||
if (!WiFi.config(local_IP, gateway, subnet, dns)) {
|
||||
ESP_LOGE(WIFI, "Network static IP failed");
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(WIFI, "WiFi connecting");
|
||||
int retries = 0;
|
||||
while (WiFi.status() != WL_CONNECTED && retries < 20) {
|
||||
delay(500);
|
||||
retries++;
|
||||
updateLED();
|
||||
}
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
ESP_LOGI(WIFI, "SSID: %s", config.ssid);
|
||||
String ipString = "IP: " + WiFi.localIP().toString();
|
||||
ESP_LOGI(WIFI, "%s", ipString.c_str());
|
||||
String gatewayInfo = "GATEWAY: " + WiFi.gatewayIP().toString();
|
||||
ESP_LOGI(WIFI, "%s", gatewayInfo.c_str());
|
||||
String dnsInfo = "DNS: " + WiFi.dnsIP().toString();
|
||||
ESP_LOGI(WIFI, "%s", dnsInfo.c_str());
|
||||
} else {
|
||||
String infoWiFi = "WIFI CONNECTION ERROR: " + String(config.ssid);
|
||||
ESP_LOGI(WIFI, "%s", infoWiFi.c_str());
|
||||
}
|
||||
updateLED();
|
||||
}
|
||||
|
||||
void WiFiManager::setupAccessPoint(const char *newSSID, const char *newPassword) {
|
||||
ESP_LOGI(WIFI, "Start AP mode");
|
||||
// Wyłączenie zapisywania konfiguracji do flash
|
||||
//WiFi.persistent(false);
|
||||
// Usunięcie zapisanej konfiguracji trybu stacji
|
||||
//WiFi.disconnect(true);
|
||||
//delay(1000);
|
||||
//WiFi.eraseAP();
|
||||
//WiFi.enableAP(false);
|
||||
//WiFi.enableAP(true);
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPsetHostname(config.hostname);
|
||||
WiFi.softAP(newSSID, newPassword);
|
||||
delay(1000);
|
||||
String ssi = "AP SSID: " + String(WiFi.softAPSSID());
|
||||
ESP_LOGI(WIFI, "%s", ssi.c_str());
|
||||
String sspass = "AP PASS: " + String(newPassword);
|
||||
ESP_LOGI(WIFI, "%s", sspass.c_str());
|
||||
IPAddress IP = WiFi.softAPIP();
|
||||
String ipString = "AP IP: " + IP.toString();
|
||||
ESP_LOGI(WIFI, "%s", ipString.c_str());
|
||||
setupMDNS();
|
||||
getRSSI();
|
||||
}
|
||||
|
||||
bool WiFiManager::isWiFiOK(){
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
updateLED();
|
||||
}
|
||||
}
|
||||
|
||||
int WiFiManager::rssiToPercent(int rssi) {
|
||||
if (rssi <= -100) {
|
||||
return 0;
|
||||
} else
|
||||
if (rssi >= -50) {
|
||||
return 100;
|
||||
}
|
||||
// Dla wartości pomiędzy -100 a -50 dBm stosujemy prostą liniową skalę
|
||||
else {
|
||||
return 2 * (rssi + 100); // Przykładowa liniowa zależność
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiManager::updateLED() {
|
||||
getRSSI();
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
int8_t rssi = WiFi.RSSI();
|
||||
if (rssi > -70) {
|
||||
;
|
||||
//digitalWrite(LED_PIN, HIGH); // Silny sygnał
|
||||
} else {
|
||||
String signalInfo = "WIFI WEAK SIGNAL: " + String(rssi) + " " + rssiToPercent(rssi) + "%";
|
||||
ESP_LOGW(WIFI, "%s", signalInfo.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t WiFiManager::getRSSI() {
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
rssi = WiFi.RSSI();
|
||||
return rssi;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WiFiManager::setupMDNS() {
|
||||
ESP_LOGI(WIFI, "mDNS start");
|
||||
if (!MDNS.begin(config.hostname)) {
|
||||
ESP_LOGE(WIFI, "mDNS error");
|
||||
} else {
|
||||
String mdnsstr = "MDNS: http://" + String(config.hostname) + ".local";
|
||||
ESP_LOGI(WIFI, "%s", mdnsstr.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool WiFiManager::performOTAUpdate(bool allowInsecureTLS, std::function<void(int,int)> progressCb){
|
||||
// 1) Pobierz i sprawdź URL
|
||||
String url = String(config.updateUrl); // z Config.h – globalny 'config'
|
||||
url.trim();
|
||||
if (url.isEmpty()) {
|
||||
ESP_LOGE(WIFI, "[OTA] Pusty config.updateUrl – przerwano.");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(WIFI, "[OTA] URL: %s", url.c_str());
|
||||
|
||||
// 2) Wymagamy aktywnego Wi-Fi w trybie klienta
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
ESP_LOGE(WIFI, "[OTA] Brak połączenia Wi-Fi – przerwano.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3) Callback postępu (opcjonalny)
|
||||
if (progressCb) {
|
||||
httpUpdate.onProgress([&](int cur, int total){ progressCb(cur, total); });
|
||||
}
|
||||
|
||||
// 4) Konfiguracja klienta i wywołanie aktualizacji
|
||||
httpUpdate.rebootOnUpdate(true); // po sukcesie – reboot
|
||||
t_httpUpdate_return ret;
|
||||
|
||||
if (url.startsWith("https://")) {
|
||||
WiFiClientSecure client;
|
||||
if (allowInsecureTLS) {
|
||||
client.setInsecure(); // UWAGA: testy/dev; w produkcji lepiej setCACert(...)
|
||||
}
|
||||
client.setTimeout(15000);
|
||||
ret = httpUpdate.update(client, url); // HTTPS
|
||||
} else {
|
||||
WiFiClient client;
|
||||
client.setTimeout(15000);
|
||||
ret = httpUpdate.update(client, url); // HTTP
|
||||
}
|
||||
|
||||
// 5) Obsługa rezultatów
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_OK:
|
||||
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).");
|
||||
return false;
|
||||
case HTTP_UPDATE_FAILED:
|
||||
default:
|
||||
ESP_LOGE(WIFI, "[OTA] Błąd (%d): %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
168
src/Settings.cpp
Normal file
168
src/Settings.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <Settings.h>
|
||||
|
||||
// Lokalny tag logów
|
||||
static const char *TAG_SETTINGS = "SETTINGS";
|
||||
|
||||
Settings::Settings(Display &display, RTC_DS3231 &rtc): display_(display), rtc_(rtc){}
|
||||
|
||||
Settings::~Settings() {}
|
||||
|
||||
void Settings::begin() {
|
||||
pinMode(BTN_UP, INPUT_PULLUP);
|
||||
pinMode(BTN_OK, INPUT_PULLUP);
|
||||
pinMode(BTN_DOWN, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
// Zwraca true, jeśli przycisk jest wciśnięty
|
||||
bool Settings::isPressed(uint8_t btnIndex) {
|
||||
//ESP_LOGI(TAG_SETTINGS, "BTN check");
|
||||
switch (btnIndex) {
|
||||
case 1: return digitalRead(BTN_UP) == LOW;
|
||||
case 2: return digitalRead(BTN_OK) == LOW;
|
||||
case 3: return digitalRead(BTN_DOWN) == LOW;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Kombinacja 1 i 3 jednocześnie
|
||||
bool Settings::isBtnReset() {
|
||||
ESP_LOGI(TAG_SETTINGS, "BTN reset check");
|
||||
return (digitalRead(BTN_UP) == LOW && digitalRead(BTN_DOWN) == LOW);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Ustawienie opcji
|
||||
*/
|
||||
void Settings::setConfigDevice() {
|
||||
ESP_LOGI(TAG_SETTINGS, "Set config device");
|
||||
display_.clear();
|
||||
delay(300);
|
||||
|
||||
config.duration = editField(config.duration, 1, 20, "Test duration");
|
||||
uint16_t dur = config.pause/1000;
|
||||
dur = editField(dur, 1, 20, "Pause between");
|
||||
config.pause = dur * 1000;
|
||||
config.measure = editField(config.measure, 0, 1, "Autorun test");
|
||||
ESP_LOGI(TAG_SETTINGS, "Config finish");
|
||||
}
|
||||
|
||||
void Settings::finishConfigDevice(){
|
||||
display_.clear();
|
||||
display_.textCenter(1, "CONFIG UPDATED");
|
||||
display_.textCenter(2, "RESTARTING");
|
||||
delay(1000);
|
||||
display_.clear();
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Ustawianie czasu RTC:
|
||||
* Kolejność: rok -> miesiąc -> dzień -> godzina -> minuta -> sekunda
|
||||
* ------------------------------------------------------------------*/
|
||||
void Settings::setTimeRTC() {
|
||||
DateTime now = rtc_.now();
|
||||
RtcDateTime t;
|
||||
t.year = now.year();
|
||||
t.month = now.month();
|
||||
t.day = now.day();
|
||||
t.hour = now.hour();
|
||||
t.minute = now.minute();
|
||||
t.second = now.second();
|
||||
|
||||
ESP_LOGI(TAG_SETTINGS,
|
||||
"Start RTC edit: %04u-%02u-%02u %02u:%02u:%02u",
|
||||
t.year, t.month, t.day, t.hour, t.minute, t.second);
|
||||
|
||||
display_.clear();
|
||||
delay(300);
|
||||
|
||||
// Kolejne pola
|
||||
t.year = editField(t.year, 2024, 2099, "YEAR");
|
||||
t.month = editField(t.month, 1, 12, "MONTH");
|
||||
// uproszczenie: 1–31 bez sprawdzania długości miesiąca
|
||||
t.day = editField(t.day, 1, 31, "DAY");
|
||||
t.hour = editField(t.hour, 0, 23, "HOUR");
|
||||
t.minute = editField(t.minute, 0, 59, "MINUTE");
|
||||
t.second = editField(t.second, 0, 59, "SECOND");
|
||||
|
||||
// Zapis do RTC
|
||||
rtc_.adjust(DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second));
|
||||
|
||||
ESP_LOGI(TAG_SETTINGS,
|
||||
"RTC set to: %04u-%02u-%02u %02u:%02u:%02u",
|
||||
t.year, t.month, t.day, t.hour, t.minute, t.second);
|
||||
|
||||
display_.clear();
|
||||
display_.textCenter(1, "RTC UPDATED");
|
||||
display_.textCenter(2, "RESTARTING");
|
||||
//display_.textCenter(2, "OK TO CONTINUE");
|
||||
delay(1000);
|
||||
display_.clear();
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Edycja jednej wartości (rok/miesiąc/dzień/godz/min/sek)
|
||||
int Settings::editField(int value, int minVal, int maxVal, const char *label) {
|
||||
bool lastUp = false;
|
||||
bool lastDown = false;
|
||||
bool lastOk = false;
|
||||
|
||||
constrainValue(value, minVal, maxVal);
|
||||
printField(label, value);
|
||||
|
||||
while (true) {
|
||||
bool up = readBtnUp();
|
||||
bool ok = readBtnOk();
|
||||
bool down = readBtnDown();
|
||||
|
||||
// Zmiana przy puszczeniu/wciśnięciu (zbocze narastające)
|
||||
if (up && !lastUp) {
|
||||
value++;
|
||||
if (value > maxVal) value = minVal;
|
||||
printField(label, value);
|
||||
}
|
||||
|
||||
if (down && !lastDown) {
|
||||
value--;
|
||||
if (value < minVal) value = maxVal;
|
||||
printField(label, value);
|
||||
}
|
||||
|
||||
if (ok && !lastOk) {
|
||||
ESP_LOGI(TAG_SETTINGS, "[OK] %s = %d", label, value);
|
||||
// mały debounce na wyjście z pola
|
||||
delay(200);
|
||||
return value;
|
||||
}
|
||||
|
||||
lastUp = up;
|
||||
lastDown = down;
|
||||
lastOk = ok;
|
||||
|
||||
delay(80); // prosty debounce + ograniczenie odpytywania
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::constrainValue(int &value, int minVal, int maxVal) {
|
||||
if (value < minVal) value = minVal;
|
||||
if (value > maxVal) value = maxVal;
|
||||
}
|
||||
|
||||
// Wyświetlanie aktualnie edytowanego pola na LCD
|
||||
void Settings::printField(const char *label, int value) {
|
||||
char buf[21];
|
||||
display_.clear();
|
||||
// Linia 0: nazwa pola
|
||||
snprintf(buf, sizeof(buf), "%s:", label);
|
||||
display_.text(0, buf);
|
||||
|
||||
// Linia 1: wartość
|
||||
snprintf(buf, sizeof(buf), "%d", value);
|
||||
display_.text(1, buf);
|
||||
|
||||
// Linia 3: podpowiedź
|
||||
display_.textStatus("UP/DOWN, OK=Next");
|
||||
ESP_LOGI(TAG_SETTINGS, "%s = %d", label, value);
|
||||
}
|
||||
48
src/Tool.cpp
Normal file
48
src/Tool.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <Tool.h>
|
||||
|
||||
static const char *TOOL = "tool";
|
||||
|
||||
bool isI2CDevPresent(uint8_t address) {
|
||||
Wire.beginTransmission(address);
|
||||
uint8_t error = Wire.endTransmission();
|
||||
|
||||
if (error == 0) {
|
||||
ESP_LOGI(TOOL, "I2C response from 0x%02X", address);
|
||||
return true;
|
||||
} else {
|
||||
if (error == 4) {
|
||||
ESP_LOGW(TOOL, "I2C unknown error at 0x%02X", address);
|
||||
} else {
|
||||
ESP_LOGI(TOOL, "No I2C device at 0x%02X", address);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void scanI2C() {
|
||||
byte error, address;
|
||||
int nDevices = 0;
|
||||
ESP_LOGI(TOOL, "I2C start scan");
|
||||
for (address = 1; address < 127; address++) {
|
||||
Wire.beginTransmission(address);
|
||||
error = Wire.endTransmission();
|
||||
|
||||
if (error == 0) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "I2C device: 0x%02X", address);
|
||||
ESP_LOGI(TOOL, "%s", buf);
|
||||
nDevices++;
|
||||
Watchdog::feed();
|
||||
}
|
||||
else if (error == 4) {
|
||||
ESP_LOGW(TOOL, "I2C error at address: 0x%02X", address);
|
||||
}
|
||||
}
|
||||
|
||||
if (nDevices == 0) {
|
||||
ESP_LOGE(TOOL, "I2C no devices found");
|
||||
} else {
|
||||
ESP_LOGI(TOOL, "I2C scan finished. Found %d device(s)", nDevices);
|
||||
}
|
||||
Watchdog::feed();
|
||||
}
|
||||
79
src/Uploader.cpp
Normal file
79
src/Uploader.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "Uploader.h"
|
||||
|
||||
Uploader::Uploader(Display &display) : _display(display) {}
|
||||
|
||||
void Uploader::processQueue(int maxFiles) {
|
||||
if (WiFi.status() != WL_CONNECTED) return;
|
||||
|
||||
String caCert = loadCACert("/cert.pem");
|
||||
if (caCert == "") {
|
||||
ESP_LOGE("UPLOADER", "Brak cert.pem na SD!");
|
||||
return;
|
||||
}
|
||||
|
||||
int sentCount = 0;
|
||||
File root = SD.open("/");
|
||||
|
||||
// Przeszukiwanie folderów numerycznych stworzonych przez Measure.cpp
|
||||
while (File folder = root.openNextFile()) {
|
||||
if (sentCount >= maxFiles) break;
|
||||
if (folder.isDirectory()) {
|
||||
File dir = SD.open(folder.path());
|
||||
while (File file = dir.openNextFile()) {
|
||||
if (sentCount >= maxFiles) break;
|
||||
|
||||
String fileName = file.name();
|
||||
if (fileName.endsWith(".wmt")) {
|
||||
_display.textStatus("SSL UPLOADING...");
|
||||
if (sendFile(String(file.path()), caCert)) {
|
||||
String oldPath = String(file.path());
|
||||
String newPath = oldPath;
|
||||
newPath.replace(".wmt", ".sent");
|
||||
file.close();
|
||||
SD.rename(oldPath.c_str(), newPath.c_str());
|
||||
sentCount++;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
folder.close();
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
|
||||
bool Uploader::sendFile(String filePath, String& caCert) {
|
||||
File f = SD.open(filePath, FILE_READ);
|
||||
if (!f) return false;
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setCACert(caCert.c_str());
|
||||
HTTPClient http;
|
||||
bool success = false;
|
||||
|
||||
if (http.begin(client, "https://api.pwojtaszek.codes/upload")) {
|
||||
http.addHeader("Content-Type", "application/octet-stream");
|
||||
http.addHeader("X-File-Name", filePath);
|
||||
http.addHeader("X-Device-ID", config.hostname);
|
||||
|
||||
int code = http.sendRequest("POST", &f, f.size());
|
||||
if (code == 200) {
|
||||
ESP_LOGI("UPLOADER", "Upload OK: %s", filePath.c_str());
|
||||
success = true;
|
||||
} else {
|
||||
ESP_LOGE("UPLOADER", "Error: %d", code);
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
f.close();
|
||||
return success;
|
||||
}
|
||||
|
||||
String Uploader::loadCACert(const char* path) {
|
||||
File f = SD.open(path);
|
||||
if (!f) return "";
|
||||
String cert = f.readString();
|
||||
f.close();
|
||||
return cert;
|
||||
}
|
||||
81
src/Watchdog.cpp
Normal file
81
src/Watchdog.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "Watchdog.h"
|
||||
|
||||
// Wykrywanie środowiska
|
||||
#if defined(ESP_PLATFORM) || defined(ESP32)
|
||||
#include <esp_task_wdt.h>
|
||||
#define WDOG_HAS_ESP 1
|
||||
#else
|
||||
#define WDOG_HAS_ESP 0
|
||||
#endif
|
||||
|
||||
namespace Watchdog {
|
||||
|
||||
static bool s_initialized = false;
|
||||
|
||||
bool init(int timeout_seconds, bool panic_on_trigger) {
|
||||
#if WDOG_HAS_ESP
|
||||
if (s_initialized) return true;
|
||||
esp_err_t err = esp_task_wdt_init(timeout_seconds, panic_on_trigger);
|
||||
if (err == ESP_OK || err == ESP_ERR_INVALID_STATE) {
|
||||
// ESP_ERR_INVALID_STATE: już zainicjalizowany — traktujemy jako OK
|
||||
s_initialized = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
(void)timeout_seconds; (void)panic_on_trigger;
|
||||
s_initialized = true; // no-op, aby nie blokować wywołań w kodzie
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool addThisTask() {
|
||||
#if WDOG_HAS_ESP
|
||||
if (!s_initialized) return false;
|
||||
esp_err_t err = esp_task_wdt_add(nullptr); // nullptr = bieżący task
|
||||
return (err == ESP_OK || err == ESP_ERR_INVALID_STATE);
|
||||
#else
|
||||
return true; // no-op
|
||||
#endif
|
||||
}
|
||||
|
||||
bool removeThisTask() {
|
||||
#if WDOG_HAS_ESP
|
||||
if (!s_initialized) return false;
|
||||
esp_err_t err = esp_task_wdt_delete(nullptr); // bieżący task
|
||||
return (err == ESP_OK || err == ESP_ERR_INVALID_STATE);
|
||||
#else
|
||||
return true; // no-op
|
||||
#endif
|
||||
}
|
||||
|
||||
void feed() {
|
||||
#if WDOG_HAS_ESP
|
||||
esp_task_wdt_reset();
|
||||
#else
|
||||
// no-op
|
||||
#endif
|
||||
}
|
||||
|
||||
bool setTimeout(int timeout_seconds) {
|
||||
#if WDOG_HAS_ESP
|
||||
if (!s_initialized) {
|
||||
// jeśli ktoś nie zainicjalizował — zrób to teraz
|
||||
return init(timeout_seconds, true);
|
||||
}
|
||||
// W ESP-IDF/Arduino brak prostego API na „live update” — re-init:
|
||||
esp_err_t err = esp_task_wdt_deinit();
|
||||
(void)err; // nie każdy port raportuje OK/INVALID_STATE spójnie
|
||||
s_initialized = false;
|
||||
return init(timeout_seconds, true);
|
||||
#else
|
||||
(void)timeout_seconds;
|
||||
return true; // no-op
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isActive() {
|
||||
return s_initialized;
|
||||
}
|
||||
|
||||
} // namespace Watchdog
|
||||
433
src/main.cpp
Normal file
433
src/main.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
#include <Logger.h>
|
||||
#include "Watchdog.h"
|
||||
#include <Arduino.h>
|
||||
#include <Config.h>
|
||||
#include <Pinout.h>
|
||||
#include <Version.h>
|
||||
#include <Display.h>
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
#include "ADXL345FastSPI.h"
|
||||
#include "RTClib.h"
|
||||
#include <Wire.h>
|
||||
#include <Network.h>
|
||||
#include <Thread.h>
|
||||
#include <Measure.h>
|
||||
#include <Tool.h>
|
||||
#include <Settings.h>
|
||||
#include <Uploader.h> // Dodano moduł wysyłki
|
||||
|
||||
#define WDT_TIMEOUT 60 // Czas watchdoga do restartu
|
||||
#define MAX_ADXL345_SENSORS 4 // Maksymalna ilość podłączanych sensorów
|
||||
|
||||
SPIClass SPI_ADXL(FSPI); // SPI2 (VSPI)
|
||||
SPIClass SPI_SD(HSPI); // SPI3 (HSPI)
|
||||
|
||||
float x, y, z = 0; // Dane odczytane z akcelerometru
|
||||
String name;
|
||||
|
||||
bool isRebootRequired = false;
|
||||
bool isAccelExists = false; // Czy istnieje jakiś podłączony do SPI czujnik
|
||||
bool testingNow = false; // Czy trwa test (gdy tak, trzeba wszystko inne wyłączyć)
|
||||
bool runMeasure = false; // czy włączyć pomiar?
|
||||
|
||||
// Buttony: 5, 6, 7
|
||||
// piny SPI CS dla ASXL345
|
||||
const uint8_t csPins[MAX_ADXL345_SENSORS] = {9, 10, 14, 21};
|
||||
|
||||
long licznik = 0;
|
||||
|
||||
ConfigManager configManager;
|
||||
RTC_DS3231 rtc;
|
||||
|
||||
ADXL345FastSPI adxl(csPins, MAX_ADXL345_SENSORS);
|
||||
Display display(rtc, 0x27, 20, 4);
|
||||
Settings settings(display, rtc); // obsługa przycisków
|
||||
WiFiManager wifi;
|
||||
|
||||
DataCapture capture(adxl, display, rtc, SD, 8192); // NEW !!! MEASURE!!!
|
||||
|
||||
// Inicjalizacja Uploadera
|
||||
Uploader uploader(display);
|
||||
|
||||
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
|
||||
|
||||
//////// PROTOTYPY /////////////////
|
||||
void setup();
|
||||
void loop();
|
||||
void reboot();
|
||||
void resetBtnClick();
|
||||
void checkWiFi();
|
||||
void showOfflineScreen();
|
||||
void measure();
|
||||
void runUploader();
|
||||
void toogleMode();
|
||||
void settingsDevice();
|
||||
void showError(String err);
|
||||
|
||||
/* ******************* SETUP() ************************* */
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(500);
|
||||
// przy USB-CDC warto poczekać chwilę na enumerację (z timeoutem):
|
||||
unsigned long t0 = millis();
|
||||
while (!Serial && millis()-t0 < 2000) { delay(10); }
|
||||
settings.begin();
|
||||
esp_log_level_set("*", ESP_LOG_INFO); // _ERROR, _WARN, _INFO, _DEBUG, _VERBOSE
|
||||
delay(500); // było 1000
|
||||
|
||||
ESP_LOGI(TAG_MAIN, "----------------------");
|
||||
ESP_LOGI(TAG_MAIN, "WMT Stalowa Wola A.Chmielowiec & L.Klich");
|
||||
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());
|
||||
|
||||
// Inicjalizacja Watchdoga na 5 sek, panic_on_trigger = true
|
||||
if (Watchdog::init(25, true)) {
|
||||
ESP_LOGI(TAG_MAIN, "Watchdog init ok.");
|
||||
} else {
|
||||
ESP_LOGE(TAG_MAIN, "Watchdog init error.");
|
||||
}
|
||||
|
||||
Wire.begin(PIN_SDA, PIN_SCL, 100000);
|
||||
scanI2C(); // Skanowanie magistrali I2C na UART (RTC-0x68, LCD-0x27)
|
||||
|
||||
configManager.begin(); // konfiguracja EEPROM urządzenia
|
||||
|
||||
// Test LCD I2C
|
||||
if (!isI2CDevPresent(0x27)) {
|
||||
ESP_LOGE(TAG_MAIN, "LCD 0x27 wire error!");
|
||||
}
|
||||
ESP_LOGI(TAG_MAIN, "Display init");
|
||||
if (!display.begin()) {
|
||||
ESP_LOGE(TAG_MAIN, "Display init failed");
|
||||
while (true) delay(1000);
|
||||
}
|
||||
ESP_LOGI(TAG_MAIN, "Display OK");
|
||||
display.welcomeScreen();
|
||||
delay(1000);
|
||||
|
||||
// Przycisk reset w przypadku factory reset
|
||||
if(settings.isBtnReset()) resetBtnClick();
|
||||
|
||||
// MCU Info
|
||||
ESP_LOGI(TAG_MAIN, "MCU info");
|
||||
display.textStatus(ESP.getChipModel());
|
||||
delay(500);
|
||||
char buf[20];
|
||||
sprintf(buf, "Freq: %d MHz", ESP.getCpuFreqMHz());
|
||||
display.textStatus(buf);
|
||||
delay(500);
|
||||
|
||||
// Test PSRAM
|
||||
display.textStatus("PSRAM:");
|
||||
ESP_LOGI(TAG_MAIN, "PSRAM init");
|
||||
if (!psramFound()) {
|
||||
ESP_LOGE(TAG_MAIN, "PSRAM not found");
|
||||
display.print("FAILED");
|
||||
while (true) delay(1000);
|
||||
}
|
||||
display.print("OK");
|
||||
delay(500);
|
||||
|
||||
// Test RTC
|
||||
ESP_LOGI(TAG_MAIN, "RTC test");
|
||||
if (!isI2CDevPresent(0x68)) {
|
||||
ESP_LOGE(TAG_MAIN, "RTC 0x68 wire error!");
|
||||
display.textStatus("RTC wire error!");
|
||||
while (true) delay(1000);
|
||||
}
|
||||
display.textStatus("RTC:");
|
||||
ESP_LOGI(TAG_MAIN, "RTC init");
|
||||
if (!rtc.begin()) {
|
||||
display.print("FAILED");
|
||||
ESP_LOGE(TAG_MAIN, "Can't find RTC");
|
||||
while (true) delay(1000);
|
||||
} else {
|
||||
display.print("OK");
|
||||
ESP_LOGI(TAG_MAIN, "RTC OK");
|
||||
if (rtc.lostPower()) {
|
||||
ESP_LOGE(TAG_MAIN, "RTC power lost! Set clock");
|
||||
display.textStatus("RTC: SET TIME");
|
||||
delay(1000);
|
||||
settings.setTimeRTC();
|
||||
}
|
||||
}
|
||||
delay(500);
|
||||
|
||||
// Karta SD
|
||||
ESP_LOGI(TAG_MAIN, "SD Card init");
|
||||
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)) {
|
||||
ESP_LOGE(TAG_MAIN, "SD mount failed");
|
||||
display.print("FAILED");
|
||||
delay(4000);
|
||||
display.textStatus("SD CARD:");
|
||||
delay(500);
|
||||
}
|
||||
ESP_LOGI(TAG_MAIN, "SD Card OK");
|
||||
display.print("OK");
|
||||
|
||||
ESP_LOGI(TAG_MAIN, "ADXL345 SPI3 SCK: %d, MISO: %d, MOSI: %d", CLK_ADSX, MISO_ADSX, MOSI_ADSX);
|
||||
SPI_ADXL.begin(CLK_ADSX, MISO_ADSX, MOSI_ADSX);
|
||||
|
||||
// Inicjalizacja ADXL345
|
||||
ESP_LOGI(TAG_MAIN, "ADXL345 init");
|
||||
display.textStatus("ADXL345: ");
|
||||
if(!adxl.begin(&SPI_ADXL, 5000000, ADXL345FastSPI::RATE_3200HZ, ADXL345FastSPI::RANGE_16G, 1)){
|
||||
ESP_LOGE(TAG_MAIN, "ADXL345 Error");
|
||||
display.print("FAILED");
|
||||
isAccelExists = false;
|
||||
uint8_t counter = 0;
|
||||
while(counter<5){ counter++; delay(1000); }
|
||||
display.clear();
|
||||
display.textCenter(0, "PLEASE CONNECT");
|
||||
display.textCenter(1, "SENSOR MODULE");
|
||||
display.textCenter(2, "ANY KEY RESTART");
|
||||
display.textCenter(3, "GURU MEDITATION");
|
||||
while(true){
|
||||
if(digitalRead(BTN_UP) == LOW) reboot();
|
||||
if(digitalRead(BTN_OK) == LOW) reboot();
|
||||
if(digitalRead(BTN_DOWN) == LOW) reboot();
|
||||
}
|
||||
} else {
|
||||
display.print(String(adxl.size()));
|
||||
ESP_LOGI(TAG_MAIN, "ADXL345 OK: %d COUNT", adxl.size()); // liczba wykrytych
|
||||
isAccelExists = true;
|
||||
}
|
||||
|
||||
DateTime now = rtc.now();
|
||||
ESP_LOGI(TAG_MAIN, "RTC TIME: %d:%d:%d %d:%d:%d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
offlineThread.onRun(showOfflineScreen);
|
||||
offlineThread.setInterval(1000);
|
||||
}
|
||||
|
||||
measureThread.onRun(measure);
|
||||
measureThread.setInterval(config.pause);
|
||||
|
||||
if (Watchdog::addThisTask()) {
|
||||
ESP_LOGI(TAG_MAIN, "Added main task to Watchdog");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG_MAIN, "System ready");
|
||||
display.clear();
|
||||
}
|
||||
|
||||
void runUploader() {
|
||||
if (!testingNow) {
|
||||
uploader.processQueue(3);
|
||||
}
|
||||
}
|
||||
|
||||
void resetBtnClick(){
|
||||
uint8_t counter = 10;
|
||||
ESP_LOGI(TAG_MAIN, "RESET BTN to 10 sec.");
|
||||
while(settings.isBtnReset()){
|
||||
Serial.print(counter); Serial.print(",");
|
||||
counter--;
|
||||
delay(1000);
|
||||
display.clear();
|
||||
display.textCenter(0,"WARNING!!!");
|
||||
display.textCenter(1, "Factory reset");
|
||||
display.textCenter(2, "keep holding for");
|
||||
display.textStatus(String(counter).c_str());
|
||||
if (counter <= 1) {
|
||||
display.clear();
|
||||
display.textCenter(0, "RESET CONFIG");
|
||||
display.textCenter(1, "release key");
|
||||
ESP_LOGI(TAG_MAIN, "RESET CONFIG");
|
||||
while(settings.isBtnReset()){;}
|
||||
configManager.resetToDefaults();
|
||||
display.textStatus("RESTARTING!");
|
||||
delay(500);
|
||||
isRebootRequired = true;
|
||||
} else {
|
||||
isRebootRequired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showError(String err){
|
||||
Watchdog::feed();
|
||||
display.clear();
|
||||
display.println(err);
|
||||
ESP_LOGE(TAG_MAIN, "%s", err.c_str());
|
||||
delay(3000);
|
||||
}
|
||||
|
||||
void checkWiFi(){
|
||||
Watchdog::feed();
|
||||
bool isConnected = WiFi.isConnected();
|
||||
String ip = WiFi.localIP().toString();
|
||||
wifi.checkWiFiConnection();
|
||||
display.updateBarWiFi(WiFi.RSSI(), isConnected);
|
||||
display.updateNetwork(ip, isConnected);
|
||||
}
|
||||
|
||||
void showOfflineScreen(){
|
||||
if((!config.connect) && (!testingNow)) {
|
||||
bool isGB;
|
||||
Watchdog::feed();
|
||||
float freeSpace = capture.freeSpaceFloat(&isGB);
|
||||
display.displayOffline(testingNow, adxl.connectedSensorsCount(), capture.freeSpaceFloat(), licznik, false);
|
||||
}
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
display.clear();
|
||||
display.textCenter(1, "SYSTEM");
|
||||
display.textCenter(2, "RESTARTING");
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO)
|
||||
watchdog_enable(1, 1);
|
||||
while (true);
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
ESP_LOGI(TAG_MAIN, "RESTART");
|
||||
ESP.restart();
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_STM32)
|
||||
NVIC_SystemReset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void measure(){
|
||||
Watchdog::feed();
|
||||
testingNow = true;
|
||||
offlineThread.enabled = false;
|
||||
DateTime now = rtc.now();
|
||||
ESP_LOGI(TAG_MAIN, "RTC TIME: %d:%d:%d %d:%d:%d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
|
||||
char bdate[15]; char btime[15];
|
||||
snprintf(bdate, sizeof(bdate), "%04d-%02d-%02d", now.year(), now.month(), now.day());
|
||||
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");
|
||||
|
||||
capture.captureAuto(config.duration, "/");
|
||||
testingNow = false;
|
||||
Watchdog::feed();
|
||||
if(!capture.isExit){
|
||||
capture.printLastFileInfoSerial();
|
||||
capture.readHeaderAndPrint(capture.generateNextFilename().c_str());
|
||||
ESP_LOGI(TAG_MAIN, "MEASURE FINISH");
|
||||
} else {
|
||||
ESP_LOGI(TAG_MAIN, "MEASURE INTERRUPT");
|
||||
}
|
||||
runMeasure = false;
|
||||
ESP_LOGI(TAG_MAIN, "DISPLAY Offline");
|
||||
display.displayOffline(testingNow, adxl.connectedSensorsCount(), capture.freeSpaceFloat(), licznik, true);
|
||||
offlineThread.enabled = true;
|
||||
}
|
||||
|
||||
void toogleMode(){
|
||||
config.measure = !config.measure;
|
||||
configManager.saveConfig();
|
||||
display.textStatus("Mode changed");
|
||||
delay(500);
|
||||
display.textStatus("");
|
||||
}
|
||||
|
||||
void settingsDevice(){
|
||||
settings.setConfigDevice();
|
||||
configManager.saveConfig();
|
||||
settings.finishConfigDevice();
|
||||
}
|
||||
|
||||
///////////// LOOP ////////////////////////////////////////////
|
||||
void loop() {
|
||||
if (settings.isPressed(2)){
|
||||
Watchdog::feed();
|
||||
if(runMeasure) {
|
||||
capture.isExit = true;
|
||||
runMeasure = false;
|
||||
display.textStatus("Stopped");
|
||||
delay(3000);
|
||||
} else runMeasure = true;
|
||||
if(runMeasure) ESP_LOGI(TAG_MAIN, "BTN MEASURE: START"); else ESP_LOGI(TAG_MAIN, "BTN MEASURE: STOP");
|
||||
|
||||
Watchdog::feed();
|
||||
delay(300);
|
||||
if (runMeasure) measureThread.run();
|
||||
}
|
||||
|
||||
if(settings.isPressed(3)) settingsDevice(); // DOWN
|
||||
if(settings.isPressed(1)) toogleMode(); // UP
|
||||
|
||||
if(testingNow) {
|
||||
if((runMeasure) && (settings.isPressed(2))){
|
||||
runMeasure = false;
|
||||
display.textStatus("STOPPING. WAIT");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(wifiTestThread.shouldRun() && (config.connect))
|
||||
wifiTestThread.run();
|
||||
|
||||
if(uploadThread.shouldRun() && (config.connect))
|
||||
uploadThread.run();
|
||||
|
||||
if(offlineThread.shouldRun() && (!config.connect)){
|
||||
offlineThread.run();
|
||||
}
|
||||
|
||||
if(!isAccelExists) {
|
||||
ESP_LOGE(TAG_MAIN, "ADXL module error:halt");
|
||||
display.clear();
|
||||
delay(500);
|
||||
display.textCenter(0, "SENSORS");
|
||||
display.textCenter(1, "NOT EXISTS");
|
||||
display.textCenter(2, "SYSTEM STOPPED");
|
||||
Watchdog::feed();
|
||||
delay(2000);
|
||||
} else {
|
||||
if(measureThread.shouldRun() && (config.measure) && (!runMeasure)){
|
||||
Watchdog::feed();
|
||||
ESP_LOGI(TAG_MAIN, "Measure thread run");
|
||||
measureThread.run();
|
||||
}
|
||||
}
|
||||
|
||||
if (isRebootRequired) {
|
||||
ESP_LOGI(TAG_MAIN, "Reboot required");
|
||||
Watchdog::feed();
|
||||
delay(1000);
|
||||
reboot();
|
||||
}
|
||||
|
||||
Watchdog::feed();
|
||||
delay(20);
|
||||
licznik ++;
|
||||
}
|
||||
Reference in New Issue
Block a user