How To
The setup process is fairly straight forward.
You need to have ESP32/ESP8266 and a LoRa Module.
Connect LoRa Module to ESP32 GPIO Pins.
Arduino Setup
Code for Nodes
#include <Arduino.h>
#include <LoRa.h>
#include <SPI.h>
#include <DNSServer.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
//define the pins used by the LoRa transceiver module
#define ss 18
#define rst 14
#define dio0 26
// Start WebServer and DNS Server
DNSServer dnsServer;
AsyncWebServer server(80);
// REPLACE WITH YOUR NETWORK CREDENTIALS
// To be used in WiFi client mode
//const char* ssid = "OPPO 2.4";
//const char* password = "mynameissajith";
const char* singleString = "singleString";
// Emergency Website in HTML
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]> <html class="no-js"> <!--<![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Emergency Portal</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
</head>
<body onload="getLocation()">
<div class="heading"> <h1>Emergency Portal</h1> </div>
<div class="more_info">
<div> <p>Your location is: </p>Latitude: 11.2587531 <br> Longitude: 75.78041 <p id="gps"></p></div>
<!-- <div> <p>The nearest support location is: </p> <p id="sptloc"></p> </div> -->
</div>
<div class="form">
<form action="/get">
<label for="fname">Name:</label><br>
<input type="text" onfocus="getLocation" id="fname" name="fname" value="Piku"><br>
<label for="location">Nearby Location:</label><br>
<input type="text" id="location" name="location" value="Goa Beach"><br>
<p><b>What is your Emergency?</b></p>
<label for="Medical">Medical</label><br>
<input type="checkbox" id="medical" name="Medical"><br>
<label for="Fire">Fire/Explosion</label><br>
<input type="checkbox" id="fire" name="fire"><br>
<label for="Collapse">Building Collapse</label><br>
<input type="checkbox" id="Collapse" name="Collapse"><br>
<label for="message">Enter a Message (120 Characters) </label><br>
<input type="text" id="message" name="message"><br>
<!-- <label for="gpsHidden"></label> -->
<!-- <input type="hidden" id="gpsHidden" name="gpsHidden" value="3487"> -->
<input type="hidden" id="singleString" name="singleString" value="3487">
<br>
<input onclick="toSingleString()" type="submit" value="Submit">
</form>
</div>
</body>
<script>
var x = document.getElementById("gps");
let gpsHidden = document.getElementById("gpsHidden");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude +
"<br>Longitude: " + position.coords.longitude;
// gpsHidden.value = position.coords.latitude + "S" + position.coords.longitude;
}
function toSingleString(){
let singleString;
// document.getElementById("singleString").value = "lol";
let ss = document.getElementById("singleString");
singleString = document.getElementById("fname").value;
singleString += "<!>";
singleString += document.getElementById("location").value;
singleString += "<@>";
singleString += document.getElementById("medical").checked;
singleString += "<#>";
singleString += document.getElementById("fire").checked;
singleString += "<$>";
singleString += document.getElementById("Collapse").checked;
singleString += "<%>";
singleString += document.getElementById("message").value;
singleString += "<&>";
singleString += "11.499797|75.688635";
ss.value = singleString;
}
</script>
</html>
)rawliteral";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void setup() {
Serial.begin(115200);
// To be used in Wifi Client mode
// WiFi.mode(WIFI_STA);
// WiFi.begin(ssid, password);
// if (WiFi.waitForConnectResult() != WL_CONNECTED) {
// Serial.println("WiFi Failed!");
// return;
// }
// Enter AP Name here
WiFi.softAP("EMERGENCY-NETWORK");
// Default IP is 192.168.4.1
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Set LoRa pins. Definition of these pins are mentioned above
LoRa.setPins(ss, rst, dio0);
Serial.println();
// To be used in WiFi client mode
// Serial.print("IP Address: ");
// Serial.println(WiFi.localIP());
// Setup LoRa Frequency and also set sync word
// Currently using 868MHz
while (!LoRa.begin(866E6)) {
Serial.println(".");
delay(500);
}
LoRa.setSyncWord(0xF3);
Serial.println("LoRa Initializing OK!");
// Code for Captive Portal
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(AsyncWebServerRequest *request){
//request->addInterestingHeader("ANY");
return true;
}
};
// Send web page to client.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html);
});
// Send a GET request to get value in singleString
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
String inputParam;
// If a value is returned, store that value in variables.
if (request->hasParam(singleString)) {
inputMessage = request->getParam(singleString)->value();
inputParam = singleString;
}
else {
inputMessage = "No message sent";
inputParam = "none";
}
// Print the input of the client
Serial.println(inputMessage);
// Client input to organised text.
int pos1 = inputMessage.indexOf("<!>");
int pos2 = inputMessage.indexOf("<@>");
int pos3 = inputMessage.indexOf("<#>");
int pos4 = inputMessage.indexOf("<$>");
int pos5 = inputMessage.indexOf("<%>");
int pos6 = inputMessage.indexOf("<&>");
String cName = inputMessage.substring(0,inputMessage.indexOf("<"))+ " ";
String location = inputMessage.substring(pos1+3,pos2)+ " ";
String medical = inputMessage.substring(pos2+3,pos3) + " ";
String fire = inputMessage.substring(pos3+3, pos4) + " ";
String collapse = inputMessage.substring(pos4+3, pos5) + " ";
String message = inputMessage.substring(pos5+3, pos6) + " ";
String gps = inputMessage.substring(pos6+3);
String singleString = gps + "|" + cName + "|" + location + "|" + medical + "|" + fire + "|" + collapse + "|" + message;
sender(cName,location,medical,fire,collapse,message,gps, singleString);
// Show a success page
request->send(200, "text/html", "Your input has been registered. Please wait while we try to contact you ("
+ inputParam + ") with value: " + inputMessage +
"<br><a href=\"/\">Return to Home Page</a>");
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
}
// Send the fromatted data received from client to LoRa master
void sender(String cName, String location, String medical, String fire, String collapse, String message, String gps, String singleString){
// Send macID so the device can be recognised.
byte mac [6];
String macID;
WiFi.macAddress(mac);
for(int looper = 0; looper < 6; looper++){
if(looper==0){
macID = String(mac[looper], HEX);
} else {
macID = macID + String(mac[looper], HEX);
}
}
singleString = macID + "|" + singleString;
LoRa.beginPacket();
// LoRa.println(macID);
LoRa.println(singleString);
// LoRa.println(cName);
// LoRa.println(location);
// LoRa.println(medical);
// LoRa.println(fire);
// LoRa.println(collapse);
// LoRa.println(message);
// LoRa.println(gps);
LoRa.endPacket();
}After correct configuration you can upload it to the microcontroller.
Code for Master
#include <SPI.h>
#include <LoRa.h>
//define the pins used by the transceiver module
#define ss 18
#define rst 14
#define dio0 2
void setup() {
//initialize Serial Monitor
Serial.begin(115200);
while (!Serial);
// Serial.println("LoRa Receiver");
//setup LoRa transceiver module
LoRa.setPins(ss, rst, dio0);
//replace the LoRa.begin(---E-) argument with your location's frequency
//433E6 for Asia
//866E6 for Europe
//915E6 for North America
while (!LoRa.begin(866E6)) {
// Serial.println(".");
delay(500);
}
// Change sync word (0xF3) to match the receiver
// The sync word assures you don't get LoRa messages from other LoRa transceivers
// ranges from 0-0xFF
LoRa.setSyncWord(0xF3);
// Serial.println("LoRa Initializing OK!");
}
void loop() {
// try to parse packet
int packetSize = LoRa.parsePacket();
if (packetSize) {
// read packet
while (LoRa.available()) {
String LoRaData = LoRa.readString();
Serial.print(LoRaData);
}
// print RSSI of packet
// Serial.print("' with RSSI ");
// Serial.println(LoRa.packetRssi());
}
}
// void sender(){
// Serial.println("Sending Packet: ");
// Serial.println();
// LoRa.beginPacket();
// LoRa.print("lol");
// LoRa.endPacket();
// }Setup PuTTY
PuTTY is required to monitor the Serial outputs and to export them to a text file.

Choose the correct port and port speed.

The log file should be saved and the configuration should be similar to this.
Connecting ESP32s
Now that the configuration is complete, we can connect the ESP32s to PC/Laptop.
In a few seconds the webserver will be ready.
Connect your Phone/any device to "EMERGENCY-NETWORK"
Now you can access the Emergency Portal and input data to it.

After clicking Submit, the user inputted data will be transferred to the master via LoRa.
Using the collected data
The data will be collected through PuTTY and then exported to a log and imported as an Excel sheet.


Plotting the collected data
The collected data can be plotted using Folium.
import pandas as pd
import folium
# read excel data as dataframe
dataDf = pd.read_excel('emergency_data.xlsx')
# initialize a map with center and zoom
mapObj = folium.Map(location=[11.499785, 75.688642],
zoom_start=16, tiles='openstreetmap')
# create a layer for bubble map using FeatureGroup
emergencyDataLayer = folium.FeatureGroup("Emergency Data")
# add the created layer to the map
emergencyDataLayer.add_to(mapObj)
# iterate through each dataframe row
for i in range(len(dataDf)):
name = dataDf.iloc[i]['name']
location = dataDf.iloc[i]['location']
medical = dataDf.iloc[i]['medical']
fire = dataDf.iloc[i]['fire']
collapse = dataDf.iloc[i]['collapse']
Message = dataDf.iloc[i]['message']
# set circle color
clr = "red"
# set circle radius
radius = 30
# Derive popup HTML content
popUpStr = 'Name - {0}<br>Location - {1}<br>Needs Medical Attention? - {2}<br>Fire Hazard? - {3}<br>Buillding Collapse? - {4}<br>Message: - {5} MW'.format(
name, location, medical, fire, collapse, Message)
# draw a circle for the emergency reported location
folium.Circle(
location=[dataDf.iloc[i]['lat'], dataDf.iloc[i]['lng']],
popup=folium.Popup(popUpStr, min_width=100, max_width=700),
radius=radius,
color=clr,
weight=2,
fill=True,
fill_color=clr,
fill_opacity=0.1
).add_to(emergencyDataLayer)
# add layer control over the map
folium.LayerControl().add_to(mapObj)
# html to be injected for displaying legend
legendHtml = '''
<div style="position: fixed;
bottom: 50px; left: 50px; width: 150px; height: 70px;
border:2px solid grey; z-index:9999; font-size:14px;
"> Legend <br>
<i class="fa fa-circle"
style="color:red"></i> Emergency<br>
</div>
'''
# inject html corresponding to the legend into the map
mapObj.get_root().html.add_child(folium.Element(legendHtml))
# save the map as html file
mapObj.save('output.html')This will output an HTML file which can be used to visualize the collected data.

Last updated