Sebelum melanjutkan membaca lebih kebawah, ada baiknya prolog nya dibaca terlebih dahulu agar tidak kehilangan arah ya! Ini urutan membacanya :
- Raspbery Pi Sebagai MQTT Broker Publik
- Geopy Library Python Untuk GIS
- Bermain GPS Jadul - Part 1
Titik kulminasi dari beberapa tulisan saya setelah gatel meng"oprek" raspi zero w yg di berikan seorang teman, saya curahkan pada tulisan ini. Jadi dapat dibilang lengkap end to end yang sangat bermanfaat bagi pembaca yg kemungkinan sedang mencari judul tugas atau skripsi. Memang saya sarankan jangan di jiplak langsung karena dapat membunuh kreatifitas, tapi cobalah baca skema paling atas ! Modifikasi di bagian sensor dan output dengan apapun yg kamu inginkan maka ratusan ide judul skripsi dapat kamu kreasikan.
kalau kita ingat pada 2 tulisan sebelumnya, dapat dilihat keterkaitan kedua gambar yaitu informasi koordinat GPS bisa diberikan oleh arduino ke Raspberry dan kemudian python akan melakukan Reverse Geopy untuk mendapatkan nama titik koordinat tadi. Ujungnya informasi nama titik yg sudah didapatkan dikembalikan oleh python menuju ke arduino dan ditampilkan pada LCD. Bingung ? Begini kalau dijabarkan secara sederhana.
- GPS receiver menerima data dari satelit berupa koordinat berformat "Lattitude , Longitude" dan dikirimkan melalui serial/UART ke arduino
- Arduino mengirimkan informasi (PUBLISH) ke topic "Lattitude , Longitude" ke broker MQTT melalui Wifi
- Karena alat akan mobile/bergerak maka butuh Smartphone sebagai Hotspot yg akan ikut dibawa berkeliling selain juga berfungsi sebagai internet gateway melalui 3G/4G
- Raspberry Pi dirumah berfungsi sebagai broker MQTT (Mosquitto) yang terhubungkan ke modem WAN/Internet.
- Script python pada Raspi melakukan SUBSCRIBE pada topic mqtt dan menunggu informasi koordinat , setelah data tersedia akan melakukan Geopy Reverse Nominatim untuk mendapatkan nama titik GPS yg dimaksud.
- Python juga akan melakukan pengiriman data nama titik tadi secara PUBLISH ke topic mqtt sekaligus melakukan penyimpanan data log dalam format csv.
- Arduino menunggu kiriman data secara SUB pada topik yg sama dengan yg di PUB oleh python dan selanjutnya ditampilkan di LCD 4 x 20.
Selanjutnya saya akan berbaik hati membahas satu per satu bagian dari script nya, akan saya bahas berselingan antara script arduino dan python agar seperti cerita bersambung.
- Insialisasi Arduino
#include <TinyGPS++.h> // library parsing data gps
#include <LiquidCrystal.h> // library LCD
#include <SoftwareSerial.h> // library serial port tambahan
#include <ESP8266WiFi.h> // library wifi
#include <PubSubClient.h> // library MQTT dari knoleary
#include "DHT.h" // Librasi sensor DHT
static const int RXPin = 0, TXPin = 15; //pake RX aja hanya baca data !
static const uint32_t GPSBaud = 4800; // kecepatan serial GPS
#define DHTPIN 2 // pin sensor DHT
#define DHTTYPE DHT11 // Saya pake DHT11
//inisialisasi DHT
DHT dht(DHTPIN, DHTTYPE);
// The TinyGPS++ object
TinyGPSPlus gps;
// Koneksi serial GPS
SoftwareSerial ss(RXPin, TXPin);
//PIN LCD
LiquidCrystal lcd(16, 5, 4, 14, 12, 13);
//milis agar timer lebih jitu
unsigned long previousMillis = 0;
//interval pembacaan dht 11
const long interval = 5000;
const char *ssid = "Nama wifi mu";
const char *pass = "Passwordnya";
//inisialisasi client dan server MQTT
WiFiClient wclient;
PubSubClient client(wclient);
const char *mqtt_server = "sesuaikan_nama_server.hopto.org";
const int mqtt_port = 1883;
const char *mqtt_user = "nama user";
const char *mqtt_pass = "password mqtt";
const char *mqtt_client_name = "ahocool123";
String latlong = "invalid"; // variabel koordinat
- Inisialisasi Python (raspberry pi)
import paho.mqtt.client as mqtt #import modul mqtt client
from geopy.geocoders import Nominatim #import nominatim sebagai geocoder
from datetime import datetime #waktu
humitemp=[""] #list global suhu kelembaban
#inisialisasi geolocator nominatim gratisan
geolocator = Nominatim(user_agent="my-application")
- Setup Arduino
void setup()
{
Serial.begin(9600); // monitor serial
ss.begin(GPSBaud); // hidupkan software serial u/ gps
lcd.begin(20, 4); //hidupkan LCD 4 x 20
dht.begin(); //hidupkan sensor dht 11
lcd.setCursor(0,0); // awal lcd
lcd.print("--Network Startup--");
//Setup MQTT server
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqtt_callback);
delay(10);
Serial.println();
Serial.println();
//konek ke wifi
if (WiFi.status() != WL_CONNECTED) {
Serial.print("Connecting to Wifi: ");
Serial.print(ssid);
Serial.println("...");
WiFi.begin(ssid, pass);
//tunggu sampe terkoneksi dengan wifi..
if (WiFi.waitForConnectResult() != WL_CONNECTED)
return;
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
}
- Setup Python (Raspberry Pi)
broker_address="localhost" #lokal aja di raspi
broker_port=1883
broker_user="username"
broker_pass="password"
print("creating new instance")
client = mqtt.Client("P1") #create new instance
client.on_message=on_message #attach function to callback
client.username_pw_set(broker_user,broker_pass)
print("connecting to broker")
client.connect(broker_address,broker_port) #connect to broker
#start the loop
print("Subscribing to topic...")
client.subscribe("/gps") #subscribe ke topik /gps
client.subscribe("/dht") #subscribe ke topik /dht
client.loop_forever()
#muter terusss
- Function pada Arduino
Function berikut ini memiliki tujuan untuk menunggu koneksi ke broker mqtt, jika sukses terhubung lanjut subscribe pada topik "/lokasi".
void reconnectmqtt()
{
Serial.println("Connecting to MQTT server..");
if (client.connect(mqtt_client_name,mqtt_user, mqtt_pass)) {
Serial.println("Connected to MQTT server");
} else {
Serial.println("Could not connect to MQTT server");
}
if (client.connected()){
Serial.println("subscribe to topic: ");
client.subscribe("/lokasi"); //subscribe ke topic lokasi point
}
}
Yang dibawah ini merupakan rutin untuk mengambil data GPS dan kemudian menuliskannya ke LCD. Sudah pernah dibahas pada tulisan sebelumnya. Selain outputnya dapat dilihat pada LCD, juga di output monitor melalui serial port/console.
void displayInfo()
{
Serial.print(F("Location: ")); //kirim ke console serial
if (gps.location.isValid()) // jika data GPS lengkap dan valid
{
Serial.print(gps.location.lat(), 6); //nulis ke serial
Serial.print(F(","));
Serial.print(gps.location.lng(), 6);
lcd.setCursor(0,0);
lcd.print(F("LAT : "));
lcd.print(gps.location.lat(), 6); //nulis ke LCD
lcd.print(F(" "));
lcd.setCursor(0,1);
lcd.print(F("LONG: "));
lcd.print(gps.location.lng(), 6);
//jangan lupa informasi koordinat disimpan dalam variabel
latlong = String(gps.location.lat(), 6) + "," + String(gps.location.lng(), 6) ;
}
else //jika GPS nya error
{
Serial.print(F("INVALID"));
lcd.setCursor(0,0);
lcd.print(" ---GPS SEARCHING---");
latlong = "invalid";
}
// yang dibawah ini untuk menulis tanggal dan jam
Serial.print(F(" Date/Time: "));
if (gps.date.isValid())
{
Serial.print(gps.date.month());
Serial.print(F("/"));
Serial.print(gps.date.day());
Serial.print(F("/"));
Serial.print(gps.date.year());
lcd.setCursor(0,2);
lcd.print(gps.date.month());
lcd.print(F("/"));
lcd.print(gps.date.day());
lcd.print(F("/"));
lcd.print(gps.date.year());
}
else
{
Serial.print(F("INVALID"));
lcd.setCursor(0,2);
lcd.print(F("Date Err"));
}
Serial.print(F(" "));
if (gps.time.isValid())
{
if (gps.time.hour() < 10) Serial.print(F("0"));
Serial.print(gps.time.hour());
Serial.print(F(":"));
if (gps.time.minute() < 10) Serial.print(F("0"));
Serial.print(gps.time.minute());
Serial.print(F(":"));
if (gps.time.second() < 10) Serial.print(F("0"));
Serial.print(gps.time.second());
Serial.print(F("."));
if (gps.time.centisecond() < 10) Serial.print(F("0"));
Serial.print(gps.time.centisecond());
lcd.setCursor(10,2);
if (gps.time.hour() < 10) lcd.print(F("0"));
lcd.print(gps.time.hour());
lcd.print(F(":"));
if (gps.time.minute() < 10) lcd.print(F("0"));
lcd.print(gps.time.minute());
lcd.print(F(":"));
if (gps.time.second() < 10) lcd.print(F("0"));
lcd.print(gps.time.second());
}
else //jam error
{
Serial.print(F("INVALID"));
lcd.setCursor(10,2);
lcd.print(F("Time Err"));
}
Serial.println();
}
Yang selanjutnya adalah function untuk pengolahan data sensor DHT11 sekaligus sebagai timer untuk pengiriman data MQTT secara PUBLISH
void baca_dht(){
//timer milis digunakan agar tidak mengganggu proses subscribe mqtt
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
//baca sensor dan simpan waktu terakhir membaca
previousMillis = currentMillis;
float h = dht.readHumidity();
float t = dht.readTemperature();
// jika sensor ngaco
if (isnan(h) || isnan(t)) {
lcd.setCursor(0,3);
lcd.print(F("..DHT Sensor Err...."));
return; //This will ensure that data is always sent
}
else{ //jika bener nulis LCD
lcd.setCursor(0,3);
lcd.print(F("H="));
lcd.print(h);
lcd.print(F(" % "));
lcd.setCursor(10,3);
lcd.print(F("T="));
lcd.print(t);
lcd.print(F(" C "));
//publish data suhu kelembaban ke topik /dht
String pubString = String(t)+ "," + String(h);
Serial.println("publish to topic: /dht : " );
Serial.println(pubString);
//serialisasi data mqtt
char message_buff[pubString.length() + 1];
pubString.toCharArray(message_buff, pubString.length() + 1);
client.publish("/dht",message_buff );
//sekalian barengan ngirim data GPS jika ada ke topik /gps
if(latlong != "invalid")
{
Serial.println("publish to topic: /gps : " );
Serial.println(latlong);
char message_buffgps[latlong.length() + 1];
latlong.toCharArray(message_buffgps, latlong.length() + 1);
client.publish("/gps",message_buffgps );
}
}
}
}
Lanjut nih kita mau bikin function untuk callback / menunggu pesan yg telah di SUBSCRIBE yaitu pada topik "/lokasi". Jika data lokasi telah diterima maka lanjut ditulis ke LCD.
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
//jika data masuk
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
//Tulis di serial console
String message;
for (int i = 0; i < length; i++) {
message = message + (char)payload[i]; //Convert *byte to String
}
Serial.print(message);
//biar ringkas saya bikin function baru
tulis_lokasi(message);
}
//function khusus menulis ke LCD hasil reply nama lokasi dari MQTT
void tulis_lokasi(String lokasi)
{
//sedikit tricky untuk membaagi nama yg panjang ke LCD 4x20
//yg ini menentukan berapa bagian baris yg akan ditampilkan
//trick nya jika dibagi 20 gak genap baris tambahkan 1
int bagian = lokasi.length() / 20;
if( (lokasi.length() % 20) != 0 ) bagian+=1;
//yg ini menentukan berapa kali layar full akan ditampilkan
//trick nya jika dibagi 4 gak genap layar tambah 1 lagi
int layar = bagian / 4;
if( (bagian % 4) != 0 ) layar+=1;
String tulisan[bagian]; //array yg berisikan bagian (20 karakter)
//loop untuk mengepaskan tampilan LCD sesuai panjang tulisan
for(int a =0 ; a< bagian ; a++)
{
tulisan[a]= lokasi.substring(a*20,(a*20)+20);
}
// ini ketemu by trial dan error..jangan mumet ya...
int ujung =0;
for(int a = 0 ; a < layar ;a++)
{ lcd.clear();
for( int b = 0 ; b < 4 ; b++)
{
ujung++;
lcd.setCursor(0,b);
if(ujung <= bagian) lcd.print( tulisan[ (a*4) + b]);
}
delay(5000);
}
lcd.clear();
unsigned long currentMillis = millis();
previousMillis = currentMillis; //biar 5 detiknya dht lancar
}
Untuk loop utama seperti ini :
void loop()
{
// Jika ada koordinat GPS yg selesai di parsing
while (ss.available() > 0)
if (gps.encode(ss.read()))
displayInfo();
// Jika parsing salah mulu
if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println(F("GPS Mu Matek Bozz..."));
while(true);
}
// Yang ini loop untuk ngecek MQTT nyambung apa tidak
if (!client.connected())
{
reconnectmqtt(); // Jika mqtt putus sambungkan lagi
}
else client.loop(); //cek terus jaga2 kalau ada data masuk
baca_dht(); // baca sensor suhu dan kirim2 data
}
##CATATAN##
Ubah panjang maksimum teks dari library MQTT pada file PubSubClient.h
// MQTT_MAX_PACKET_SIZE : Maximum packet size
#ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 256
#endif
- Function pada script Python (raspberry pi)
Function yg dibawah ini bertujuan untuk me Reverse GeoLocator dari koordinat menjadi nama titik atau alamat yg diwakilkan. Data API yang digunakan adalah Nominatim dari OpenStreetMap yang bersifat gratisan, jadi tidak seakurat Google Map API yg berbayar.
def on_message(client, userdata, message): #routine pesan mqtt masuk
print("message received " ,str(message.payload.decode("utf-8")))
print("message topic=",message.topic)
if (message.topic == '/gps'): #jika topik /gps
outF = open("log.csv", "a")
dateTimeObj = datetime.now()
kor=str(message.payload.decode("utf-8"))
location = geolocator.reverse(kor)
loc = (location.address).split(',') #ambil nama geo
lokasi = ''.join(loc)
print(lokasi)
client.publish("/lokasi",lokasi) #kirim ke topik /lokasi
# dibawah ini untuk menulis log.csv
outF.write(str(dateTimeObj))
outF.write(",")
outF.write(humitemp[0])
outF.write(",")
outF.write(kor)
outF.write(",")
outF.write(lokasi)
outF.write("\n")
outF.close()
elif (message.topic == '/dht'): #jika topik /dht
humitemp[0]=str(message.payload.decode("utf-8")) #simpan di var
That's it...telah saya tulis satu persatu dan semoga penjelasannya dirasa cukup jelas. Oh iya untuk menjalankan Script tanpa membuka layar terminal terus menerus, gunakan cara NOHUP seperti dibawah ini :
pi@raspberry:~ $ nohup python3 coba_geopy.py &
Dan selanjutnya biarkan raspberry pi nya menyala sedangkan alat akan kita bawa berkeliling mencari Landmark / Titik terkenal diseputaran rumah. Mau tahu hasil yang saya dapatkan ? Maaf kalau gambarnya kurang jelas tapi berusahalah membaca (pakai kaca pembesar kalau bisa ! wkwkwkw)
Dan script python saya juga memiliki fasilitas untuk menyimpan ke log.csv, jadi bikin dulu header kolomnya dengan notepad seperti halnya membikin file csv pada umumnya. hasil keliling saya seperi ini nih.
Untuk lebih kerennya, file CSV ini dapat juga diolah menjadi informasi GIS pada software Google EARTH ataupun yg paling gampang dengan meng import CSV nya ke google "MY MAPS". Ini fasilitas GRATIS baru dari google dan sangat menarik untuk dicoba.
Jika mau melihat kemana saja kemarin saya jalan-jalan (walau PSBB) saat lebaran hari ke-2 bisa meng klik LINK DISINI.
SELAMAT MENCOBA