Files
ChronoDisplay/ChronoDisplay.ino
2026-05-28 13:59:56 -04:00

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);
}