#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 #include "Display.h" #include "ChronoReadingManager.h" #include "ChronoWebServer.h" #include "FileManager.h" #include #include #include //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); }