1+ #include < ESP8266WiFi.h>
2+ #include < ESP8266WebServer.h>
3+ #include < EEPROM.h>
4+ #include < ArduinoJson.h>
5+
6+ #define GREEN_LED D1
7+ #define RED_LED D2
8+ #define BLUE_LED D3
9+ #define BUZZER D4
10+ #define LM35_PIN A0
11+
12+ const char * ssid = " ResQTemp" ;
13+ const char * password = " 11223344" ;
14+ ESP8266WebServer server (80 );
15+
16+ const float REAL_TEMP_THRESHOLD = 33.0 ;
17+ const unsigned long HOLD_TIME_REAL = 5000UL ;
18+ const unsigned long HOLD_TIME_DEMO = 10000UL ;
19+ const unsigned long LOOP_PERIOD_MS = 500UL ;
20+
21+ float temperature = REAL_TEMP_THRESHOLD - 5 ;
22+ bool demoMode = false ;
23+ bool alertSent = false ;
24+ bool isGsmActive = false ;
25+ bool cancelCallFlag = false ;
26+ unsigned long tempStartTime = 0 ;
27+ unsigned long lastLoopTime = 0 ;
28+
29+ const int EEPROM_DEMO_ADDRESS = 0 ;
30+ const int EEPROM_LOG_COUNT_ADDRESS = 1 ;
31+ String phoneNumber = " 03218010098" ;
32+
33+ const char PAGE[] PROGMEM = R"====(
34+ <!DOCTYPE html>
35+ <html>
36+ <head>
37+ <meta name="viewport" content="width=device-width, initial-scale=1">
38+ <title>ResQTemp</title>
39+ <style>
40+ body{margin:0;font-family:sans-serif;background:#0f2027;color:white;}
41+ .container{max-width:420px;margin:auto;padding:20px}
42+ .card{background:rgba(255,255,255,0.12);border-radius:18px;padding:25px;text-align:center;position:relative;box-shadow:0 4px 6px rgba(0,0,0,0.1);}
43+ .temp{font-size:60px;font-weight:bold;margin-bottom:10px;}
44+ .status{margin-top:12px;padding:10px;border-radius:10px;font-weight:600;}
45+ .normal{background:#2ecc71;color:#000;}
46+ .alert{background:#e74c3c}
47+ .calling{background:#3498db}
48+ .btn-group{margin-top:20px}
49+ button{width:100%;padding:12px;border-radius:10px;border:0;background:#f1c40f;font-size:15px;margin-top:10px;cursor:pointer;transition:background 0.2s;}
50+ button.reset-btn{background:#e74c3c;color:white;}
51+ button.cancel{background:#e74c3c;color:white;}
52+ .indicator{position:absolute;top:15px;right:15px;width:12px;height:12px;border-radius:50%;}
53+ .indicator.connected{background:#2ecc71;}
54+ .indicator.disconnected{background:#e74c3c;}
55+ .mode-info{font-size:14px;color:rgba(255,255,255,0.7);margin-top:10px;}
56+ </style>
57+ </head>
58+ <body>
59+ <div class="container">
60+ <h3>ResQTemp System</h3>
61+ <div class="card">
62+ <div class="indicator disconnected" id="conn"></div>
63+ <div class="temp" id="temp">--</div>
64+ <div class="status" id="status">Waiting...</div>
65+ <div class="mode-info" id="modeInfo"></div>
66+ <div class="mode-info" id="countdownInfo"></div>
67+ <div class="btn-group">
68+ <button id="demoBtn" onclick="toggleDemo()">Toggle Demo Mode</button>
69+ <button onclick="window.location.reload()">Refresh</button>
70+ <button class="reset-btn" onclick="resetEsp()">Reset ESP</button>
71+ <button id="cancelBtn" class="cancel" onclick="cancelCall()" style="display:none;">Cancel Call</button>
72+ </div>
73+ <div style="font-size:13px;margin-top:15px">Location: NFC-IET Multan Lab 114 AI Depart</div>
74+ </div>
75+ </div>
76+ <script>
77+ let lastUpdate=0;
78+ function updateUI(d){
79+ lastUpdate=Date.now();
80+ document.getElementById("conn").className="indicator connected";
81+ document.getElementById("temp").innerHTML=d.temp+" °C";
82+ let st=document.getElementById("status");
83+ st.innerHTML=d.state;
84+ st.className="status "+d.state.toLowerCase();
85+ let demoBtn=document.getElementById("demoBtn");
86+ let modeInfo=document.getElementById("modeInfo");
87+ let countdownInfo=document.getElementById("countdownInfo");
88+ let cancelBtn=document.getElementById("cancelBtn");
89+ if(d.demo==="true"){
90+ demoBtn.innerText="Disable Demo Mode";
91+ modeInfo.innerHTML="Demo Mode Active (Simulation to "+d.limit+" °C)";
92+ }else{
93+ demoBtn.innerText="Enable Demo Mode";
94+ modeInfo.innerHTML="Real Mode Active (Threshold: "+d.limit+" °C)";
95+ }
96+ if(d.state==="NORMAL") st.innerHTML="Normal";
97+ if(d.state==="ALARM") st.innerHTML="Overheating Alert";
98+ if(d.state==="CALLING") st.innerHTML="Emergency Alert Sent";
99+ if(d.countdown>0 && d.state==="ALARM"){
100+ countdownInfo.innerHTML="Alert in: "+d.countdown+" sec";
101+ cancelBtn.style.display="block";
102+ } else {
103+ countdownInfo.innerHTML="";
104+ cancelBtn.style.display="none";
105+ }
106+ }
107+ function fetchData(){
108+ fetch('/data').then(r=>r.json()).then(updateUI).catch(()=>document.getElementById("conn").className="indicator disconnected");
109+ }
110+ function toggleDemo(){ fetch('/demo').then(()=>fetchData()); }
111+ function resetEsp(){ fetch('/reset').then(()=>setTimeout(()=>{window.location.reload();},2500)); }
112+ function cancelCall(){ fetch('/cancelCall').then(()=>fetchData()); }
113+ fetchData();
114+ setInterval(fetchData,1500);
115+ setInterval(()=>{ if(Date.now()-lastUpdate>3000) document.getElementById("conn").className="indicator disconnected"; },1000);
116+ </script>
117+ </body>
118+ </html>
119+ )====" ;
120+
121+ void runGsmSequence (){
122+ static int gsmState=0 ;
123+ static unsigned long stateStartTime=0 ;
124+
125+ if (cancelCallFlag){ // Stop GSM immediately
126+ gsmState = 0 ;
127+ cancelCallFlag = false ;
128+ isGsmActive = false ;
129+ return ;
130+ }
131+
132+ if (!isGsmActive) return ;
133+
134+ unsigned long now = millis ();
135+ unsigned long dialDelay = demoMode ? HOLD_TIME_DEMO : HOLD_TIME_REAL;
136+
137+ switch (gsmState){
138+ case 0 :
139+ stateStartTime = now;
140+ gsmState = 1 ;
141+ break ;
142+ case 1 :
143+ if (now - stateStartTime >= 500 ){
144+ Serial.println (" AT+CMGF=1" );
145+ stateStartTime = now;
146+ gsmState = 2 ;
147+ }
148+ break ;
149+ case 2 :
150+ if (now - stateStartTime >= 500 ){
151+ Serial.print (" AT+CMGS=\" " );
152+ Serial.print (phoneNumber);
153+ Serial.println (" \" " );
154+ stateStartTime = now;
155+ gsmState = 3 ;
156+ }
157+ break ;
158+ case 3 :
159+ if (now - stateStartTime >= 500 ){
160+ Serial.println (" ResQTemp Emergency: Temperature has crossed the safe limit!" );
161+ stateStartTime = now;
162+ gsmState = 4 ;
163+ }
164+ break ;
165+ case 4 :
166+ if (now - stateStartTime >= 300 ){
167+ Serial.write (26 ); // CTRL+Z
168+ stateStartTime = now;
169+ gsmState = 5 ;
170+ }
171+ break ;
172+ case 5 :
173+ if (now - stateStartTime >= dialDelay){
174+ Serial.print (" ATD" );
175+ Serial.print (phoneNumber);
176+ Serial.println (" ;" );
177+ stateStartTime = now;
178+ gsmState = 6 ;
179+ }
180+ break ;
181+ case 6 :
182+ if (now - stateStartTime >= 15000 ){
183+ Serial.println (" ATH" );
184+ isGsmActive = false ;
185+ gsmState = 0 ;
186+ }
187+ break ;
188+ default :
189+ gsmState = 0 ;
190+ break ;
191+ }
192+
193+ server.handleClient ();
194+ }
195+
196+ void startGsmAlert (){
197+ if (!isGsmActive){
198+ isGsmActive=true ;
199+ cancelCallFlag=false ;
200+ unsigned int count;
201+ EEPROM.get (EEPROM_LOG_COUNT_ADDRESS,count);
202+ count++;
203+ EEPROM.put (EEPROM_LOG_COUNT_ADDRESS,count);
204+ EEPROM.commit ();
205+ }
206+ }
207+
208+ void handleRoot (){ server.send_P (200 ," text/html" ,PAGE); }
209+ void handleResetEsp (){ server.send (200 ," text/plain" ," Restarting..." ); delay (500 ); ESP.restart (); }
210+ void handleData (){
211+ StaticJsonDocument<200 > doc;
212+ float limit = REAL_TEMP_THRESHOLD;
213+ String state = " NORMAL" ;
214+ unsigned long holdTime = demoMode ? HOLD_TIME_DEMO : HOLD_TIME_REAL;
215+ unsigned long remainingSec = 0 ;
216+
217+ if (isGsmActive) state = " CALLING" ;
218+ else if (temperature>=limit){
219+ state = alertSent ? " CALLING" : " ALARM" ;
220+ if (tempStartTime>0 ) remainingSec = max (0UL ,(holdTime-(millis ()-tempStartTime))/1000UL );
221+ }
222+
223+ doc[" temp" ] = String (temperature,1 );
224+ doc[" demo" ] = demoMode ? " true" : " false" ;
225+ doc[" state" ] = state;
226+ doc[" limit" ] = String (limit,1 );
227+ doc[" countdown" ] = remainingSec;
228+
229+ String json; serializeJson (doc,json);
230+ server.send (200 ," application/json" ,json);
231+ }
232+
233+ void handleDemo (){
234+ demoMode = !demoMode;
235+ EEPROM.write (EEPROM_DEMO_ADDRESS, demoMode?1 :0 );
236+ EEPROM.commit ();
237+ alertSent=false ;
238+ tempStartTime=0 ;
239+ isGsmActive=false ;
240+ cancelCallFlag=false ;
241+ temperature = REAL_TEMP_THRESHOLD - 5 ;
242+ server.send (200 ," text/plain" , demoMode?" DEMO_ON" :" DEMO_OFF" );
243+ }
244+
245+ void handleCancelCall (){
246+ if (isGsmActive || alertSent){
247+ cancelCallFlag = true ;
248+ alertSent = false ;
249+ isGsmActive = false ;
250+ }
251+ server.send (200 ," text/plain" ," CALL_CANCELED" );
252+ }
253+
254+
255+ void setup (){
256+ Serial.begin (9600 );
257+ delay (1000 );
258+
259+ EEPROM.begin (32 );
260+ demoMode = (EEPROM.read (EEPROM_DEMO_ADDRESS)==1 );
261+
262+ pinMode (GREEN_LED,OUTPUT);
263+ pinMode (RED_LED,OUTPUT);
264+ pinMode (BLUE_LED,OUTPUT);
265+ pinMode (BUZZER,OUTPUT);
266+
267+ digitalWrite (GREEN_LED,LOW);
268+ digitalWrite (RED_LED,LOW);
269+ digitalWrite (BLUE_LED,LOW);
270+ digitalWrite (BUZZER,LOW);
271+ WiFi.mode (WIFI_AP);
272+ WiFi.softAP (ssid, password);
273+
274+ server.on (" /" , handleRoot);
275+ server.on (" /data" , handleData);
276+ server.on (" /demo" , handleDemo);
277+ server.on (" /reset" , handleResetEsp);
278+ server.on (" /cancelCall" , handleCancelCall);
279+
280+ server.begin ();
281+ }
282+
283+ void readTemperature (){
284+ if (!demoMode) temperature = analogRead (LM35_PIN)*(3.3 /1024.0 )*100.0 ;
285+ }
286+
287+ void simulateTempRise (){
288+ if (demoMode) temperature = min (temperature + 0.5 * (LOOP_PERIOD_MS/1000.0 ), REAL_TEMP_THRESHOLD + 5.0 );
289+ }
290+
291+ void handleAlert (float limit, unsigned long hold){
292+ unsigned long now = millis ();
293+ if (temperature < limit){
294+ alertSent=false ;
295+ tempStartTime=0 ;
296+ isGsmActive=false ;
297+ cancelCallFlag=false ;
298+ digitalWrite (GREEN_LED,HIGH);
299+ digitalWrite (RED_LED,LOW);
300+ digitalWrite (BLUE_LED,LOW);
301+ digitalWrite (BUZZER,LOW);
302+ } else {
303+ digitalWrite (GREEN_LED,LOW);
304+ digitalWrite (RED_LED,HIGH);
305+ digitalWrite (BLUE_LED,LOW);
306+ digitalWrite (BUZZER,HIGH);
307+ if (tempStartTime==0 ) tempStartTime=now;
308+ if (now-tempStartTime>=hold && !alertSent && !isGsmActive && !cancelCallFlag){
309+ alertSent=true ;
310+ startGsmAlert ();
311+ }
312+ if (alertSent){
313+ digitalWrite (GREEN_LED,LOW);
314+ digitalWrite (RED_LED,LOW);
315+ digitalWrite (BLUE_LED,HIGH);
316+ digitalWrite (BUZZER,LOW);
317+ }
318+ }
319+ }
320+
321+ void loop (){
322+ runGsmSequence ();
323+ server.handleClient ();
324+ unsigned long now = millis ();
325+ if (now - lastLoopTime >= LOOP_PERIOD_MS){
326+ lastLoopTime = now;
327+ readTemperature ();
328+ simulateTempRise ();
329+ unsigned long hold = demoMode ? HOLD_TIME_DEMO : HOLD_TIME_REAL;
330+ handleAlert (REAL_TEMP_THRESHOLD, hold);
331+ }
332+ }
0 commit comments