/*
THIS ONLY CAN GO ONE WAY FOR NOW!!! You CAN add more functionality, lines and directions, but I won't bother for now.
*/
#include <WiFi.h>
#include <WiFiMulti.h>
#include <AsyncUDP.h>
#include <LiquidCrystal.h>
#include <time.h>
const String stops[] = {"Ambleside Village", "Ambleside Funland", "Ambleside Serpent", "Broadway Village", "Broadway Farmland", "Broadway roundabout", "Bamburgh Remoted", "Bamburgh Castle", "Bamburgh City", "Edinburgh Lighthouse", "Edinburgh Limits", "Edinburgh City", "Edinburgh Tramstop", "Edinburgh Roundabout"};
const int artmmin[] = {0, 5, 15, 30, 40, 45, 5, 25, 35, 50, 30, 35, 45, 55};
const int arthr[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2};
byte stopsnum = 14;
byte curstop = 0;
int busoffam[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // kolko ludi kde vystupuje, bude nahodne generovane, aby nemusel sofer vyberat
LiquidCrystal lcd(18, 19, 20, 21, 22, 23);
IPAddress staticIP(192, 168, 1, 197);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(1,1,1,1);
WiFiMulti wifiMulti;
AsyncUDP udp;
byte* ulozisko;
long dlzka;
float kmh = 0;
byte prevod = 0;
String timeStamp = "";
bool isstopped = false;
bool isbusstop = false;
unsigned long minulost = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n=== SETUP START ===");
Serial.println("1. Initializing LCD...");
lcd.begin(20, 4);
delay(50);
lcd.clear();
lcd.print("Booting...");
Serial.println(" LCD initialized");
Serial.println("2. Setting up pins...");
pinMode(9, INPUT_PULLUP);
stopsnum = stopsnum - 1;
Serial.println(" Pins configured");
Serial.println("3. Configuring WiFi...");
WiFi.config(staticIP, gateway, subnet, dns);
wifiMulti.addAP("ZTE-XNGDAD", "54s572vk4j9h");
Serial.println(" WiFi configured");
Serial.println("4. Connecting to WiFi...");
lcd.clear();
lcd.print("Wifi connecting... ");
int loops = 20;
while (loops > 0 && wifiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
loops--;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\nERROR: WiFi failed!");
lcd.clear();
lcd.print("WiFi FAILED");
delay(2000);
ESP.restart();
}
Serial.println("\n WiFi connected!");
Serial.print(" IP: ");
Serial.println(WiFi.localIP());
lcd.clear();
lcd.print("WiFi OK");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
Serial.println("5. Synchronizing time with NTP...");
lcd.clear();
lcd.print("Syncing time...");
// Configure time using ESP32's built-in NTP
configTime(0, 0, "sk.pool.ntp.org", "pool.ntp.org", "time.google.com");
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
tzset();
Serial.println(" Waiting for NTP sync...");
Serial.print(" Starting epoch time: ");
time_t now = time(nullptr);
Serial.println(now);
int attempts = 0;
while (attempts < 60) {
delay(500);
now = time(nullptr);
Serial.print(" Attempt ");
Serial.print(attempts);
Serial.print(" - Epoch: ");
Serial.print(now);
Serial.print(" - Time: ");
struct tm timeinfo = *localtime(&now);
char buffer[20];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeinfo);
Serial.println(buffer);
if (now > 1000000000) { // Valid timestamp (after Sept 2001)
Serial.println(" NTP sync successful!");
lcd.clear();
lcd.print("Time synced!");
break;
}
attempts++;
}
if (now <= 1000000000) {
Serial.println(" NTP sync FAILED after 30 seconds");
Serial.println(" WARNING: Time will be incorrect");
lcd.clear();
lcd.print("NTP FAILED");
}
delay(2000);
Serial.println("6. Setting up UDP listener on port 5685...");
if (udp.listen(5685)) {
Serial.println(" UDP listening");
udp.onPacket([](AsyncUDPPacket packet) {
ulozisko = packet.data();
dlzka = packet.length();
spracuj();
});
} else {
Serial.println(" ERROR: UDP listen failed!");
}
Serial.println("=== SETUP COMPLETE ===\n");
lcd.clear();
lcd.print("Ready!");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(5000);
lcd.clear();
}
union {
byte b[4];
float f;
} rymps;
void loop() {
yield();
// Get current time
time_t now = time(nullptr);
struct tm timeinfo = *localtime(&now);
// Format as HH:MM:SS
char buffer[10];
strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
timeStamp = String(buffer);
if (millis() - minulost >= 350) {
lcd.clear(); // lcd
lcd.setCursor(0, 0);
lcd.print(timeStamp);
lcd.setCursor(9, 0);
lcd.print("");
// separator
Serial.print(timeStamp); // serial
Serial.println(" 57 -> 8");
Serial.println("Normal: 3.45 Eur");
Serial.println(stops[curstop]);
Serial.println(stops[((curstop + 3) % (stopsnum + 1))]);
minulost = millis();
}
if (kmh < 1) {
isstopped = 1;
} else if (kmh >= 9 && kmh < 11 && isbusstop) {
curstop = curstop + 1;
curstop = curstop % (stopsnum + 1);
isbusstop = 0;
} else if (kmh >= 10.01) {
isstopped = 0;
}
if (isstopped && !isbusstop) {
isbusstop = !digitalRead(9);
}
if(digitalRead(9)) {
delay(10);
for(int i = 0; i < random(1, 100); i = i + 1) {
int arin = ((esp_random()) % 14);
busoffam[arin] = busoffam[arin] + 1;
}
}
delay(50);
}
void spracuj() {
rozbalvel();
zobergear();
}
void rozbalvel() {
if (dlzka > 263) {
rymps.b[0] = ulozisko[256];
rymps.b[1] = ulozisko[257];
rymps.b[2] = ulozisko[258];
rymps.b[3] = ulozisko[259];
kmh = (rymps.f) * 3.6;
}
}
void zobergear() {
if (dlzka > 319) {
prevod = ulozisko[319];
}
}