211 lines
5.4 KiB
Arduino
211 lines
5.4 KiB
Arduino
#include "TFT_eSPI.h"
|
|
#include "ChronoBLE.h"
|
|
#include "ChronoVersion.h"
|
|
|
|
// External declaration for BLE reading flag and value
|
|
extern volatile bool newReadingAvailable;
|
|
extern volatile int lastReadFPS;
|
|
#include "ChronoReading.h"
|
|
#include <LinkedList.h>
|
|
#include "Display.h"
|
|
#include "ChronoReadingManager.h"
|
|
#include "ChronoWebServer.h"
|
|
#include "FileManager.h"
|
|
#include <WiFi.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
|
|
//testing mode variables
|
|
static bool testMode=false;
|
|
static long testModeLastAdd=0;
|
|
|
|
//general variables
|
|
static char currentTime[64];
|
|
|
|
|
|
//chronograph setup
|
|
static bool chronoStateConnected=false;
|
|
static ChronoBLE chronoBLE;
|
|
static ChronoWebServer webServer;
|
|
|
|
|
|
|
|
void speedCallback(int fps){
|
|
addReading(fps);
|
|
}
|
|
void addReading(int fps){
|
|
ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance();
|
|
|
|
//add reading to manager
|
|
chronoReadings.addReading(fps);
|
|
|
|
//update display
|
|
Display::updateEntireDisplay();
|
|
|
|
if (chronoReadings.getShotCount() % 10 == 0){
|
|
// Get filesystem information
|
|
unsigned int totalBytes = LittleFS.totalBytes();
|
|
unsigned int usedBytes = LittleFS.usedBytes();
|
|
unsigned int freeBytes = totalBytes - usedBytes;
|
|
|
|
Serial.printf("\t(%u) Available Disk / Memory: %u / %u\n", chronoReadings.getShotCount(), freeBytes, ESP.getFreeHeap());
|
|
}
|
|
|
|
|
|
}
|
|
void toggleTestMode(){
|
|
if (testMode){
|
|
Serial.println("disabling test mode....");
|
|
testMode=false;
|
|
}
|
|
else {
|
|
Serial.println("enabling test mode....");
|
|
testMode=true;
|
|
}
|
|
}
|
|
void setup()
|
|
{
|
|
Serial.begin(115200);
|
|
Serial.println("starting......");
|
|
|
|
//initialize the file manager
|
|
FileManager& files = FileManager::getInstance();
|
|
files.init();
|
|
|
|
//initialize web portal (also connects to WiFi)
|
|
webServer.init();
|
|
webServer.setTestModeCallback(toggleTestMode);
|
|
|
|
//sync time via NTP after WiFi is connected
|
|
setDateTime();
|
|
|
|
//initialize display
|
|
Display::init();
|
|
|
|
|
|
//initialize gun and projectile profile
|
|
ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance();
|
|
chronoReadings.initializeProfiles();
|
|
//Display::updateProfile(chronoReadings.getProfile());
|
|
|
|
|
|
//initialize bluetooth
|
|
chronoBLE.init();
|
|
chronoBLE.setSpeedCallback(speedCallback);
|
|
}
|
|
void setDateTime(){
|
|
// NTP server configuration - ESP32 API uses gmtOffset (seconds) and dstOffset (seconds)
|
|
// EST = UTC-5, EDT = UTC-4, so gmtOffset = -5*3600 = -18000
|
|
configTime(-18000, 3600, "pool.ntp.org", "time.nist.gov");
|
|
|
|
// Wait for time to be set
|
|
time_t now;
|
|
struct tm timeinfo;
|
|
int retry = 0;
|
|
const int maxRetry = 20; // 10 seconds max
|
|
|
|
while (retry < maxRetry) {
|
|
time(&now);
|
|
localtime_r(&now, &timeinfo);
|
|
|
|
// Check if we have a valid time (year > 2020)
|
|
if (timeinfo.tm_year > 120) { // 120 = year 2020
|
|
Serial.println("Time synced via NTP");
|
|
break;
|
|
}
|
|
Serial.print("Waiting for NTP sync... ");
|
|
Serial.println(retry);
|
|
delay(500);
|
|
retry++;
|
|
}
|
|
|
|
// If still not synced, fall back to a default date
|
|
if (timeinfo.tm_year <= 120) {
|
|
Serial.println("NTP sync failed, using fallback date");
|
|
timeinfo.tm_year = 2024 - 1900;
|
|
timeinfo.tm_mon = 0; // January
|
|
timeinfo.tm_mday = 1;
|
|
timeinfo.tm_hour = 0;
|
|
timeinfo.tm_min = 0;
|
|
timeinfo.tm_sec = 0;
|
|
timeinfo.tm_isdst = -1;
|
|
|
|
now = mktime(&timeinfo);
|
|
struct timeval tv = {.tv_sec = now};
|
|
settimeofday(&tv, NULL);
|
|
}
|
|
}
|
|
void updateTime(){
|
|
time_t now;
|
|
char strftime_buf[64];
|
|
struct tm timeinfo;
|
|
time(&now);
|
|
// Use the timezone that was configured via configTime
|
|
localtime_r(&now, &timeinfo);
|
|
strftime(strftime_buf, sizeof(strftime_buf), "%R", &timeinfo);
|
|
|
|
if (strcmp(currentTime, strftime_buf) != 0) {
|
|
strcpy(currentTime, strftime_buf);
|
|
Display::updateTime(strftime_buf);
|
|
Display::refreshDisplay();
|
|
}
|
|
}
|
|
void checkBLE(){
|
|
if (chronoBLE.isIdle()){
|
|
if (chronoStateConnected){
|
|
chronoStateConnected=false;
|
|
Display::showChronographDisconnected();
|
|
Display::refreshDisplay();
|
|
}
|
|
chronoBLE.startScan();
|
|
}
|
|
else if (chronoBLE.isConnected()){
|
|
//update display
|
|
if (!chronoStateConnected){
|
|
chronoStateConnected=true;
|
|
Display::showChronographConnected();
|
|
Display::refreshDisplay();
|
|
}
|
|
}
|
|
else if (chronoBLE.foundDevice()){
|
|
chronoBLE.connectToDevice();
|
|
}
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
//process BLE connection
|
|
checkBLE();
|
|
|
|
//process web server
|
|
webServer.process();
|
|
|
|
//update time
|
|
updateTime();
|
|
|
|
//////////////////////////////////////////////////////
|
|
//run testmode commands if in test mode
|
|
if (testMode){
|
|
if (testModeLastAdd==0){Display::showChronographConnected();}
|
|
if (millis()>(testModeLastAdd+5000)){
|
|
long randomNumber = random(890, 920);
|
|
addReading(randomNumber);
|
|
testModeLastAdd=millis();
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////
|
|
|
|
// Check for new BLE readings and process them
|
|
// This is done here instead of in the BLE callback to avoid heap fragmentation
|
|
// and display updates in interrupt context
|
|
if (newReadingAvailable) {
|
|
newReadingAvailable = false;
|
|
addReading(lastReadFPS);
|
|
}
|
|
|
|
//update display
|
|
Display::refreshDisplay();
|
|
|
|
delay(2);
|
|
}
|