Kalau kita kembali belajar IOT di awal-awal maka akan dikenalkan dengan yang namanya piramida IOT. Bagian paling ujung dari piramida ini adalah pelaporan ke orang yang akan membutuhkan informasi melalui IOT. Salah satu contoh pelaporan adalah GIS (Geographic Information System) dimana data yang diambil oleh sensor dan dikirimkan secara IOT akan ditampilkan pada peta. Tentunya cuap-cuap saya diawal ini akan terasa omong kosong kalau alat GPS nya seperti yang pernah saya bahas disini, susah di koneksikan...ala mak jang !
Sadar atau tidak sadar, pada kenyataannya, smartphone yang kita pakai mesti ada penerima GPS lho mas broo ! nah..benda yang selalu kita pegang tiap saat ini seharusnya dan pastinya dapat memberikan koordinat bujur dan lintang selama berada pada lokasi terbuka. Masalahnya bagaimana cara membagi pembacaan hardware GPS pada smartphone android ke perangkat IOT?
Aplikasi android yang saya gunakan adalah netGPS yang akan mengirimkan data GPS secara TCP (berfungsi sebagai TCP server) dan mengirimkan sebagai teks log dari berbagai macam standar data GPS yang digunakan oleh smartphone android. Aplikasi ini cukup jadul jadi jika ingin menginstallnya harap gunakan cara selain google play jika ternyata HP kamu ditolak.
Contoh standar pembacaan teks NMEA (National Marine Electronics Association) yang saya gunakan sederhana saja cukup sebaris ( karena saya butuhnya hanya koordinat saja) dan saya pilihkan standar $GPRMC. Saya butuh membayar 15ribu rupiah ke aplikasi ini untuk dapat memilih secara custom pesannya seperti pada gambar diatas. Dari sebuah website saya dapatkan standar pesannya adalah sebagai berikut:
$GPRMC
Recommended minimum specific GPS/Transit data
eg1. $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 eg2. $GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68
225446 Time of fix 22:54:46 UTC A Navigation receiver warning A = OK, V = warning 4916.45,N Latitude 49 deg. 16.45 min North 12311.12,W Longitude 123 deg. 11.12 min West 000.5 Speed over ground, Knots 054.7 Course Made Good, True 191194 Date of fix 19 November 1994 020.3,E Magnetic variation 20.3 deg East *68 mandatory checksum
Dan kemudian saya parsing data dari teks $GPRMC, namun karena saya tukang koding abal-abal, butuh bantuan chatGPT dan hasilnya saya diberikan script yang sangat jitu !
#include <ESP8266WiFi.h> const char* ssid = "YourHotspotSSID"; const char* password = "YourHotspotPassword"; // Example NMEA GPRMC string String nmeaString = "$GPRMC,043652,A,0713.64753,S,11243.78,E,,,290524,000.7,E,A*33"; void setup() { Serial.begin(115200); parseNMEA(nmeaString); } void loop() { // No code needed here for this example } double convertNMEACoordinate(String nmeaCoord, bool isLongitude) { // Determine the length of degrees part (3 for longitude, 2 for latitude) int degreesLength = isLongitude ? 3 : 2; // Extract the degrees part double degrees = nmeaCoord.substring(0, degreesLength).toInt(); // Extract the minutes part and convert to decimal double minutes = nmeaCoord.substring(degreesLength).toFloat(); degrees += minutes / 60.0; return degrees; } void parseNMEA(String nmea) { // Check if the string starts with "$GPRMC" if (nmea.startsWith("$GPRMC")) { // Buffer for each field in the NMEA string char buf[75]; nmea.toCharArray(buf, 75); // Tokenize the NMEA string by comma char* token = strtok(buf, ","); // Index to track the position in the NMEA string int index = 0; // Variables to hold parsed data String time, status, latitude, latitudeDirection; String longitude, longitudeDirection, date; while (token != NULL) { switch (index) { case 1: time = String(token); break; case 2: status = String(token); break; case 3: latitude = String(token); break; case 4: latitudeDirection = String(token); break; case 5: longitude = String(token); break; case 6: longitudeDirection = String(token); break; case 9: date = String(token); break; } token = strtok(NULL, ","); index++; } // Convert latitude and longitude double latitudeDegrees = convertNMEACoordinate(latitude, false); if (latitudeDirection == "S") { latitudeDegrees = -latitudeDegrees; } double longitudeDegrees = convertNMEACoordinate(longitude, true); if (longitudeDirection == "W") { longitudeDegrees = -longitudeDegrees; } // Prepare buffer for formatted longitude char longitudeStr[20]; dtostrf(longitudeDegrees, 1, 7, longitudeStr); // Prepare buffer for the final concatenated string char resultStr[40]; // Adjust size based on expected combined length strcpy(resultStr, "latitude "); strcat(resultStr, longitudeStr); // Print the parsed data Serial.println("Time: " + time); Serial.println("Status: " + status); Serial.print("Latitude: "); Serial.println(latitudeDegrees, 7); Serial.print("Longitude: "); Serial.println(longitudeDegrees, 7); Serial.print("Concatenated String: "); Serial.println(resultStr); // Print concatenated string Serial.println("Date: " + date); // Convert and print additional data if necessary // Convert time to HH:MM:SS format String hours = time.substring(0, 2); String minutes = time.substring(2, 4); String seconds = time.substring(4, 6); Serial.println("Formatted Time: " + hours + ":" + minutes + ":" + seconds); // Convert date to DD/MM/YY format String day = date.substring(0, 2); String month = date.substring(2, 4); String year = date.substring(4, 6); Serial.println("Formatted Date: " + day + "/" + month + "/" + year); } else { Serial.println("Invalid NMEA string"); } }
Penjelasan:
1. Function konversi NMEA (convertNMEACoordinate):
- Mengonversi koordinat NMEA ke format derajat desimal.
2. Function Penguraian (parseNMEA):
- Memberi token pada string NMEA dan mengekstrak bidang yang relevan.
- Mengonversi lintang dan bujur menjadi derajat desimal.
- Menyesuaikan tanda berdasarkan arah (N/S untuk garis lintang, E/W untuk garis bujur).
3. Memformat Bujur/Lintang:
- Fungsi dtostrf memformat garis bujur/lintang menjadi string dengan 7 tempat desimal.
4. Concatenate string:
- Buffer resultStr disiapkan untuk menampung hasil gabungan.
- Fungsi strcpy menginisialisasi resultStr dengan label "latitude".
- Fungsi strcat menambahkan string garis bujur yang diformat ke resultStr.
5. Print / output :
- String gabungan dicetak ke Serial Monitor.
Kode ini mem-parsing string NMEA GPRMC, mengubah garis lintang dan garis bujur menjadi derajat desimal, memformat garis bujur menjadi string dengan 7 tempat desimal, dan menggabungkannya dengan label longitude dan latitude. String gabungan kemudian dicetak pada serial monitor.
Untuk menerima pesan $GPRMC dari smartphone, saya akan membuat ESP8266-wemos saya sebagai client dan terhubung dengan hotpsot dari Smartphone yang saya gunakan. Hal ini akan memudahkan saya karena Smartphone saya fungsikan sebagai TCP server di IP Gatewaynya ( 192.168.43.1) pada port yg saya pilih 8088. IP ini adalah IP umum yang digunakan oleh hotspot smartphone android, namun sudah saya akali pada script agar TCP server yang di-bind-kan adalah IP gatewaynya, selanjutnya menunggu data GPS berupa teks $GPRMC.
/* GPS SMARTPHONE (netGPS Android) to ESP8266
Hardware : DHT 11 & OLED SSD1306 128x64
This script is incomplete, you should add data sending
via IOT protocol, MQTT, HTTP or Lora
www.aisi555.com
*/
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DHT.h"
#define SCREEN_WIDTH 128 // display display width, in pixels
#define SCREEN_HEIGHT 64 // display display height, in pixels
#define DHT_PIN D4 // pin sensor DS18B20
#define display_RESET -1 // Reset pin
#define SCREEN_ADDRESS 0x3C ///address i2c oled
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, display_RESET);
#define DHTTYPE DHT11 //Sensor DHT11
DHT dht(DHT_PIN, DHTTYPE);
const char* ssid = "myHotspot";
const char* password = "99999999";
// Server details
IPAddress serverIP; // Define server IP address (gateway IP)
const uint16_t serverPort = 8088;
WiFiClient client;
bool gpsOK = false;
char longitudeStr[20];
char latitudeStr[20];
void setup() {
Serial.begin(9600);
Serial.println("");
Serial.println(F(" ...www.aisi555.com GPS From Smartphone ...."));
// initialize display display with address 0x3C for 128x64
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
while (true);
}
delay(2000); // wait for initializing
display.clearDisplay(); // clear display
display.setTextSize(1); // text size
display.setTextColor(WHITE); // text color
displayDisplayCenter("www.aisi555.com", 4);
displayDisplayCenter("Connecting to wifi.. ",56);
// Connect to Wi-Fi network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(1000);
display.clearDisplay(); // clear display
displayDisplayCenter("www.aisi555.com", 4);
displayDisplayCenter("Connecting to Aps..",56);
IPAddress gatewayIP = WiFi.gatewayIP();
serverIP = gatewayIP; // Use the gateway IP as server IP
// Attempt to connect to the server
if (!client.connect(serverIP, serverPort)) {
Serial.println("Connection to server failed");
} else {
Serial.println("Connected to server");
}
dht.begin();
}
void writeTempHumid(){ //kirim data pengukuran DHT
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
}
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.println(" °C ");
String tempString;
tempString = String(t, 1); //
tempString += (char)247;
tempString += "C | ";
tempString += String(h, 0);;
tempString += " %";
//biar ketumpuk hitam dulu biar bersih
display.fillRect(0,17,128,48,BLACK);
//lanjut tulis warna putih
display.setTextColor(WHITE);
String latString;
latString = "Lat: ";
latString += String(latitudeStr);
displayDisplayCenter(latString,20);
String lonString;
lonString = "Lon: ";
lonString += String(longitudeStr);
displayDisplayCenter(lonString,30);
displayDisplayCenter(tempString,42);
if(gpsOK)displayDisplayCenter("GPS is Connected",56);
else displayDisplayCenter("GPS disconnected !",56);
/* ADD YOUR IOT DATA SENDING SCRIPT BELOW */
}
void loop() {
if (runEvery(5000)) {
writeTempHumid();
}
if (client.connected()) {
// Check if data is available to read
gpsOK=true;
while (client.available()) {
String receivedData = client.readStringUntil('\n'); // Read data until newline
Serial.print("Received data: ");
Serial.println(receivedData);
// Process the received data
// Add your data processing logic here
parseNMEA(receivedData);
}
} else {
// Reconnect if connection is lost
if (!client.connect(serverIP, serverPort)) {
Serial.println("Reconnection to server failed");
gpsOK=false;
delay(1000); // Wait before retrying
} else {
Serial.println("Reconnected to server");
}
}
}
//function agar tulisan rata tengah (center)
void displayDisplayCenter(String text, int posisi) {
int16_t x1;
int16_t y1;
uint16_t width;
uint16_t height;
display.getTextBounds(text, 0, 0, &x1, &y1, &width, &height);
display.setCursor((SCREEN_WIDTH - width) / 2, posisi);
display.println(text); // text to display
display.display();
}
double convertNMEACoordinate(String nmeaCoord, bool isLongitude) {
// Determine the length of degrees part (3 for longitude, 2 for latitude)
int degreesLength = isLongitude ? 3 : 2;
// Extract the degrees part
double degrees = nmeaCoord.substring(0, degreesLength).toInt();
// Extract the minutes part and convert to decimal
double minutes = nmeaCoord.substring(degreesLength).toFloat();
degrees += minutes / 60.0;
return degrees;
}
void parseNMEA(String nmea) {
// Check if the string starts with "$GPRMC"
if (nmea.startsWith("$GPRMC")) {
// Buffer for each field in the NMEA string
char buf[75];
nmea.toCharArray(buf, 75);
// Tokenize the NMEA string by comma
char* token = strtok(buf, ",");
// Index to track the position in the NMEA string
int index = 0;
// Variables to hold parsed data
String time, status, latitude, latitudeDirection;
String longitude, longitudeDirection,date;
while (token != NULL) {
switch (index) {
case 1: time = String(token); break;
case 2: status = String(token); break;
case 3: latitude = String(token); break;
case 4: latitudeDirection = String(token); break;
case 5: longitude = String(token); break;
case 6: longitudeDirection = String(token); break;
case 9: date = String(token); break;
}
token = strtok(NULL, ",");
index++;
}
// Convert latitude and longitude
double latitudeDegrees = convertNMEACoordinate(latitude, false);
if (latitudeDirection == "S") {
latitudeDegrees = -latitudeDegrees;
}
double longitudeDegrees = convertNMEACoordinate(longitude, true);
if (longitudeDirection == "W") {
longitudeDegrees = -longitudeDegrees;
}
//kirim ke display
dtostrf(longitudeDegrees, 1, 7, longitudeStr);
dtostrf(latitudeDegrees, 1, 7, latitudeStr);
// Print the parsed data
Serial.println("Time: " + time);
Serial.println("Status: " + status);
Serial.print("Latitude: ");
Serial.println(latitudeDegrees, 7);
Serial.print("Longitude: ");
Serial.println(longitudeDegrees, 7);
Serial.println("Date: " + date);
// Convert and print additional data if necessary
// Convert time to HH:MM:SS format
String hours = time.substring(0, 2);
String minutes = time.substring(2, 4);
String seconds = time.substring(4, 6);
Serial.println("Formatted Time: " + hours + ":" + minutes + ":" + seconds);
// Convert date to DD/MM/YY format
String day = date.substring(0, 2);
String month = date.substring(2, 4);
String year = date.substring(4, 6);
Serial.println("Formatted Date: " + day + "/" + month + "/" + year);
} else {
Serial.println("Invalid NMEA string");
}
}
//timer
boolean runEvery(unsigned long interval)
{
static unsigned long previousMillis = 0;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
return true;
}
return false;
}