/*
* @Title: Slot & Play: ESP32-Driven Cloud Integrated Charging Hub
* @Author: Princetoniño B. Piscos
* Course: BCA188 | Internet of Things
* Members: Enlawan, Piscos
* @Date: 10/31/23 15:40:36 GMT +08:00
*
*/
//#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
//#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
//#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
//#define HTTPC_ERROR_NOT_CONNECTED (-4)
//#define HTTPC_ERROR_CONNECTION_LOST (-5)
//#define HTTPC_ERROR_NO_STREAM (-6)
//#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
//#define HTTPC_ERROR_TOO_LESS_RAM (-8)
//#define HTTPC_ERROR_ENCODING (-9)
//#define HTTPC_ERROR_STREAM_WRITE (-10)
//#define HTTPC_ERROR_READ_TIMEOUT (-11)
#define ChannelID 53986900473679872
#define TINAPA_API "http://api.olympusworld.online:1336/api/esp/"
// 4-DIGIT CONF
#define CLK 19
#define DIO 14
// Other Elec. Components
#define BTN_PIN 36
#define RELAY_PIN 10
// Button
#define DEBOUNCE_DELAY 100
//WiFi
#define _SSID "Wokwi-GUEST"
#define _PASS ""
#include <WiFi.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
#include <TM1637.h>
#include <queue>
#include <Ticker.h>
// SLOT BOX CONFIG
const int coinValuePer = 120; // 3 mins per coin (180 seconds)
unsigned int remainingSec = 0;
const long interval = 1000; // 1 second
unsigned int accumulated = 0;
unsigned long lastTime = 0;
// BUTTON AS COIN SENSOR MUNA NGEKNGOK
int prevBtnState = LOW;
unsigned long lastDebounceTime = 0;
const String urlEp = String(TINAPA_API) + String(ChannelID);
TM1637 tm(CLK, DIO);
WiFiClient client;
HTTPClient http;
Ticker push2Tinapa;
Ticker releaserTinapa;
struct HttpRequest
{
String ep;
String data;
};
std::queue<HttpRequest> requestQueue;
void setup()
{
Serial.begin(115200);
pinMode(BTN_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
tm.init();
tm.set(BRIGHT_TYPICAL);
WiFi.mode(WIFI_STA);
WiFi.begin(_SSID, _PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.print("Connected to Wi-Fi network with IP Address: ");
Serial.println(WiFi.localIP());
http.addHeader("Connection", "Keep-Alive");
http.addHeader("Content-Type", "application/json");
http.setReuse(true); // reuse ang connection for ease post
//send heartbeat pra online status
http.begin("http://api.olympusworld.online:1336/api/tinapa_ping/" + String(ChannelID));
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
Serial.println("Successfully sent heartbeat");
http.end(); // end heartbeat, proceed to tinapa data
http.begin(client, urlEp);
updateDisplay(0); // init display 0000
Serial.println("Tinapa is ready na");
push2Tinapa.attach_ms(5000, pushToTinapa); // push data to Tinapa every 5 sec.
releaserTinapa.attach_ms(15000, releaseTinapa); // release connection every 20 sec.
} else Serial.println("Failed to connect to tinapa.");
}
void loop()
{
unsigned long currTime = millis();
if ((currTime - lastTime) >= interval)
{
lastTime = currTime;
if (remainingSec > 0)
{
remainingSec--;
// update 4dig disp
updateDisplay(remainingSec);
digitalWrite(RELAY_PIN, HIGH);
} else digitalWrite(RELAY_PIN, LOW);
}
int btnRead = digitalRead(BTN_PIN);
if (btnRead == HIGH && prevBtnState == LOW)
{
if ((currTime - lastDebounceTime) >= DEBOUNCE_DELAY)
{
remainingSec += (coinValuePer + 1); // add time sa timer
String data = "{\"accumulated\":" + String(++accumulated) + "}";
pushTinapa(urlEp, data); // queue ang mga nahulog sa user, then fire after sometime
}
lastDebounceTime = currTime;
}
prevBtnState = btnRead;
delayMicroseconds(10);
}
void pushTinapa(String ep, String data)
{
HttpRequest newRequest;
newRequest.ep = ep;
newRequest.data = data;
requestQueue.push(newRequest);
Serial.println("Tinapa pushed to queue");
}
void pushToTinapa()
{
while (!requestQueue.empty())
{
HttpRequest nextRequest = requestQueue.front();
if (!http.connected())
{
http.addHeader("Connection", "Keep-Alive");
http.addHeader("Content-Type", "application/json");
http.setReuse(true); // reuse ang connection for ease post
// reinitialize HTTP client if wla nka connect
http.begin(client, nextRequest.ep);
Serial.println("Connection to Tinapa has been established");
}
int httpResponseCode = http.POST(nextRequest.data);
if (httpResponseCode > 0)
Serial.println("Successfully sent data to Tinapa");
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
if (httpResponseCode == -1 || httpResponseCode == -5)
pushTinapa(nextRequest.ep, nextRequest.data); // requeue kung fail
}
requestQueue.pop();
}
}
void releaseTinapa()
{
if (requestQueue.empty())
{
http.end(); // release resources after sometime
Serial.println("Released tinapa");
}
}
void updateDisplay(unsigned int seconds)
{
unsigned int mins = seconds / 60;
if (seconds >= 3600)
{
tm.point(false);
// 4100 = 0065
unsigned int hundredsOfMins = mins / 100;
unsigned int remainingMins = mins % 100;
tm.display(0, hundredsOfMins / 10); // 9
tm.display(1, hundredsOfMins % 10); // 9
tm.display(2, remainingMins / 10); // 9
tm.display(3, remainingMins % 10); // 9
} else {
tm.point(true);
unsigned int remainingSecs = seconds % 60;
tm.display(0, mins / 10); // 5
tm.display(1, mins % 10); // 9:
tm.display(2, remainingSecs / 10); // 5
tm.display(3, remainingSecs % 10); // 9
}
}