Semua Tentang Belajar Teknologi Digital Dalam Kehidupan Sehari - Hari

  • IC Timer 555 yang Multifungsi

    IC timer 555 adalah sirkuit terpadu (chip) yang digunakan dalam berbagai pembangkit timer, pulsa dan aplikasi osilator. Komponen ini digunakan secara luas, berkat kemudahan dalam penggunaan, harga rendah dan stabilitas yang baik

  • Ayo Migrasi TV Digital

    Kami bantu anda untuk memahami lebih jelas mengenai migrasi tv digital, apa sebabnya dan bagaimana efek terhadap kehidupan. Jasa teknisi juga tersedia dan siap membantu instalasi - setting perangkat - pengaturan antena dan distribusi televisi digital ke kamar kos / hotel

  • Bermain DOT Matrix - LOVEHURT

    Project Sederhana dengan Dot Matrix dan Attiny2313. Bisa menjadi hadiah buat teman atau pacarmu yang ulang tahun dengan tulisan dan animasi yang dapat dibuat sendiri.

  • JAM DIGITAL 6 DIGIT TANPA MICRO FULL CMOS

    Jika anda pencinta IC TTL datau CMOS maka project jam digital ini akan menunjukkan bahwa tidak ada salahnya balik kembali ke dasar elektronika digital , sebab semuanya BISA dibuat dengan teknologi jadul

  • Node Red - Kontrol Industri 4.0

    Teknologi kontrol sudah melampaui ekspektasi semua orang dan dengan kemajuan dunia elektronika, kini semakin leluasa berkreasi melalui Node Red

Minggu, 14 Juli 2024

[raspi - yolo] Google Colab & Ngrok - Mengolah Gambar Untuk Raspi Zero W

 


Cloud computing adalah hal yang cukup lumrah kita temui untuk membantu semua pekerjaan komputasi kita secara jarak jauh dan dimanapun. Saya sering menggunakan vps - google cloud - aws untuk membatu banyak pekerjaan saya dan kali ini saya akan gunakan google colab yang memiliki kemampuan menjalankan script python pada cloud. Permasalahannya adalah jika kita ingin mengakses google colab secara http API /Post /Get maka tidak akan ada IP public yang diberikan oleh google. Sehingga kita membutuhkan fasilitas tunneling "Gratis" bernama NGROK

Ngrok adalah alat yang memungkinkan pengembang untuk mengekspos server lokal ke internet dengan cara yang aman. Ini sangat berguna untuk pengembangan dan pengujian aplikasi web, terutama ketika perlu memeriksa bagaimana aplikasi tersebut berinteraksi dengan layanan eksternal atau perangkat lain yang tidak berada di jaringan lokal. 

Silahkan daftar dengan akun google kamu dan jangan lupa dapatkan authtoken tunneling untuk praktek yang aakan saya contohkan dibawah.



Pada google colab, setelah terkoneksi dengan benar, maka dibutuhkan mengunduh file konfigurasi yolo v3 seperti berikut ini 

!wget https://pjreddie.com/media/files/yolov3.weights
!wget https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
!wget https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names

Setellah itu perlu menginstall library python untuk  pyngrok sedangkan flask sudah tersedia secara default

!pip install pyngrok

Dan response nya seperti berikut

Collecting pyngrok Downloading pyngrok-7.1.6-py3-none-any.whl (22 kB) Requirement already satisfied: PyYAML>=5.1 in /usr/local/lib/python3.10/dist-packages (from pyngrok) (6.0.1) Installing collected packages: pyngrok Successfully installed pyngrok-7.1.6

Brikut ini adalah script python untuk mengolah gambar via flask yang mirip dengan tulisan sebelumnya disini . Jangan lupa mengambil authtoken tunnel di dashbor ngrok


from flask import Flask, request, jsonify
import cv2
import numpy as np
import time
from pyngrok import ngrok
import threading



# Replace 'your_authtoken' with your actual authtoken from ngrok dashboard
ngrok.set_auth_token('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')

app = Flask(__name__)


yolo = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
classes = []

with open("coco.names", "r") as file:
    classes = [line.strip() for line in file.readlines()]


layer_names = yolo.getLayerNames()
output_layers = [layer_names[i - 1] for i in yolo.getUnconnectedOutLayers()]

colorRed = (0,0,255)
colorGreen = (0,255,0)

# #Loading Images
def detect_objects(img):
    starting_time = time.time()
    height, width, channels = img.shape

    # # Detecting objects
    blob = cv2.dnn.blobFromImage(img, 0.00392, (255, 255), (0, 0, 0), True, crop=False)

    yolo.setInput(blob)
    outputs = yolo.forward(output_layers)

    class_ids = []
    confidences = []
    boxes = []
    for output in outputs:
        for detection in output:
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.4 and class_id == 0 : # 0 = person
                center_x = int(detection[0] * width)
                center_y = int(detection[1] * height)
                w = int(detection[2] * width)
                h = int(detection[3] * height)

                x = int(center_x - w / 2)
                y = int(center_y - h / 2)

                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)


    indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
    org = 0
    mob = 0
    mot = 0

    for i in range(len(boxes)):
        if i in indexes:
            if class_ids[i] == 0:
                org = org + 1
                label = 'orang : ' + repr(org)
            elif class_ids[i] == 2:
                mob = mob + 1
                label = 'mobil : ' + repr(mob)
            elif class_ids[i] == 3:
                mot = mot + 1
                label = 'motor : ' + repr(mot)
            x, y, w, h = boxes[i]
            #label = str(classes[class_ids[i]]) + ', x=' +repr(x) + ',y=' +repr(y)
            #label = str(classes[class_ids[i]]) + '- no: ' + repr(i+1)
            cv2.rectangle(img, (x, y), (x + w, y + h), colorGreen, 3)
            cv2.putText(img, label, (x, y -5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255) )




    print("{")
    print(f"\"Jumlah Orang\": {org},")
    print(f"\"Jumlah Mobil\": {mob},")
    print(f"\"Jumlah Motor\": {mot},")

    elapsed_time = time.time() - starting_time
    print("\"processing time\":" + repr(elapsed_time)  )
    print("}")
    #cv2.imshow("Image", img)
    cv2.imwrite("output.jpg",img)
    #cv2.waitKey(0)
    #cv2.destroyAllWindows()
    return {"Orang": org, "Mobil": mob, "Motor": mot, "time": elapsed_time }

@app.route("/detect", methods=["POST"])
def detect():
    file = request.files["image"]
    npimg = np.fromfile(file, np.uint8)
    image = cv2.imdecode(npimg, cv2.IMREAD_COLOR)

    # Measure start time
    start_time = time.time()

    result = detect_objects(image)

    # Measure end time
    end_time = time.time()

    # Calculate processing time
    processing_time = end_time - start_time
    result['processing_time'] = float(processing_time)  # Ensure it's a float

    return jsonify(result)


# Function to run Flask server
def run_flask():
    app.run(port=5000)

# Start Flask server in a separate thread
flask_thread = threading.Thread(target=run_flask)
flask_thread.start()

# Set up ngrok tunnel
public_url = ngrok.connect(5000)
print("Flask server is running at:", public_url)

Sedangkan script pada raspberry pi zero tetap sama dengan tulisan sebelumnya. Jalankan script pada google colab sampai muncul alamat public dari ngrok tunneling.


 * Serving Flask app '__main__'
 * Debug mode: off
INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
INFO:werkzeug:Press CTRL+C to quit
Flask server is running at: NgrokTunnel: "https://029a-34-105-62-129.ngrok-free.app" -> "http://localhost:5000"


Hasilnya luar biasa cepat dan gratis pula !




Dikarenakan alamat publik dari tunneling ngrok berubah setiap kali script dijalankan pada google colab, maka saya sudah berhasil mengakalinya dengan memanfaatkan MQTT sebagai pengirim alamat tunnel nya dan dapat digunakan oleh banyak pengguna semisal banyak raspberry pi zero w + cam di beberapa titik lampu. Ini akan saya bahas jika ada yang tertarik dengan tulisan saya ini (by request).

SELAMAT BELAJAR !
Share:

[raspi - yolo] Memanfaatkan Komputer Lain Untuk Membantu Raspi Zero W Mengolah Gambar

 


Judulnya sedikit membuat ambigu, ngapain sih pake raspberry segala kalau juga digunakan PC lain untuk mengolah yolo ? Eiittsss ..saya pingin mensimulasikan raspberry pi zero w saya sebagai pengambil gambar melalui camera raspi ov5647, lalu alat saya ini akan dipasang pada banyak titik untuk medeteksi jumlah orang di bawah lampu jalan. Jadi harga raspi zero w yg cukup murah ditambah camera yg terjangkau menjadi pilihan yang cukup bagus dibanding meng-upgrade ke versi raspi 4 yang jauh harganya walau dapat mengolah gambar dengan lebih bagus (akan di bahas belakangan). Cukup 1 komputer yang processor kelas i5 sudah bisa menjadi server pengolah gambar.




Kali ini saya akan membahas membuat "server" pengolah gambar pada pc saya yang terhubung dengan wifi yang sama dengan raspi zero w yang saya gunakan sebagai simulator pengambil gambar. Untuk itu sebaiknya baca penjelasan sebelumnya untuk memahami yolo-python untuk mendeteksi objek disini:

  • Yolo dan python untuk deteksi objek :  Videonya dilihat disini
  • Deteksi object pada cctv menggunakan python pada node-red : Klik disini

Yang kita gunakan sebagai server lokal adalah flask yang merupakan pilihan favorit untuk mengetest sistem server berbasis python.  Flask adalah sebuah mikroframework web untuk Python yang dikembangkan oleh Armin Ronacher sebagai bagian dari proyek Pocoo. Flask sangat populer karena kesederhanaannya dan fleksibilitasnya, sehingga memungkinkan pengembang untuk membuat aplikasi web dengan cepat dan efisien.

Fitur Utama Flask

  • Ringan dan Fleksibel: Flask tidak memiliki lapisan abstraksi database atau validasi form bawaan, tetapi memungkinkan integrasi dengan banyak pustaka pihak ketiga.
  • WSGI: Flask berbasis pada Werkzeug WSGI toolkit dan Jinja2 templating engine.
  • Routing: Flask menyediakan sistem routing yang mudah digunakan untuk mengarahkan URL ke fungsi Python.
  • Templating: Dengan menggunakan Jinja2, Flask memungkinkan penggunaan template untuk memisahkan logika aplikasi dari tampilan.
  • Blueprints: Flask mendukung modularisasi aplikasi melalui blueprints, yang memungkinkan pengembangan aplikasi besar menjadi lebih terstruktur.
  • Ekstensi: Banyak ekstensi yang tersedia untuk Flask, seperti Flask-SQLAlchemy untuk ORM, Flask-WTF untuk validasi form, dan Flask-Login untuk otentikasi pengguna.

Contoh Aplikasi Sederhana dengan Flask

Berikut adalah contoh dasar aplikasi Flask:
from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

@app.route('/')
def home():
    return 'Hello, Flask!'

@app.route('/hello/<name>')
def hello(name):
    return f'Hello, {name}!'

@app.route('/form', methods=['GET', 'POST'])
def form():
    if request.method == 'POST':
        name = request.form['name']
        return redirect(url_for('hello', name=name))
    return render_template('form.html')

if __name__ == '__main__':
    app.run(debug=True)

Kalau kita test pada python, hasilnya seperti ini pada browser :



Penjelasan Contoh:

  • Importing Flask: Mengimpor Flask dan modul terkait lainnya.
  • Inisialisasi Aplikasi: app = Flask(__name__) menginisialisasi aplikasi Flask.
  • Route and View Functions: @app.route('/') mendefinisikan route untuk halaman utama. Fungsi home akan mengembalikan string "Hello, Flask!".
  • Dynamic Route: @app.route('/hello/<name>') mendefinisikan route dinamis yang menerima parameter name.
  • Form Handling: Route /form menerima metode GET dan POST. Jika metode POST, data dari form akan diambil dan pengguna akan diarahkan ke halaman hello.

Kelebihan Flask

  • Kesederhanaan: Mudah dipelajari dan digunakan, cocok untuk pemula maupun pengembang berpengalaman.
  • Fleksibilitas: Dapat dengan mudah disesuaikan sesuai kebutuhan aplikasi.
  • Komunitas: Komunitas yang besar dan aktif, menyediakan banyak tutorial dan dukungan.

Kelemahan Flask

  • Kurangnya Fitur Bawaan: Tidak memiliki banyak fitur bawaan seperti framework web lain yang lebih besar (misalnya, Django).
  • Pengaturan Manual: Banyak fitur yang harus diatur dan diintegrasikan secara manual.

Flask sangat cocok untuk aplikasi web kecil hingga menengah, API, dan proyek yang membutuhkan fleksibilitas tinggi.


Selanjutnya pada raspi zero w akan saya buat script python untuk mengirimkan gambar ke server saya :

import sys
import requests
import cv2
import time

def send_image(image_path, server_url):
    # Read image from file
    image = cv2.imread(image_path)
    if image is None:
        print(f"Failed to load image {image_path}")
        return

    # Encode image as JPEG
    _, img_encoded = cv2.imencode('.jpg', image)

    # Measure the start time for the network request
    start_time = time.time()

    # Send the image to the Flask server
    response = requests.post(f"{server_url}/detect", files={"image": img_encoded.tobytes()})

    # Measure the end time for the network request
    end_time = time.time()

    # Calculate network time
    network_time = end_time - start_time

    # Check and parse the response
    response_data = response.json()
    server_processing_time_str = response_data.get('processing_time', None)

    if server_processing_time_str is not None:
        try:
            server_processing_time = float(server_processing_time_str)
            print(f"Server processing time: {server_processing_time:.2f} seconds")
        except ValueError:
            print("Error: Server processing time is not a valid float.")
            server_processing_time = None
    else:
        print("Server processing time not found in response.")
        server_processing_time = None

    print(response_data)
    print(f"Network time: {network_time:.2f} seconds")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python myscript.py <image_path> <server_url>")
    else:
        image_path = sys.argv[1]
        server_url = sys.argv[2]
        send_image(image_path, server_url)

Script diatas akan menggunakan cara panggil : 

python flask.py <nama_gambar.jpg> <ip_server_flask:port>


Sedangkan pada sisi server, seperti yang sudah saya bahas pada cara pengolahan yolo sebelumnya  disini, maka script yang saya buat sebagai berikut :


from flask import Flask, request, jsonify
import cv2
import numpy as np
import time


app = Flask(__name__)


yolo = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
classes = []

with open("coco.names", "r") as file:
    classes = [line.strip() for line in file.readlines()]


layer_names = yolo.getLayerNames()
output_layers = [layer_names[i - 1] for i in yolo.getUnconnectedOutLayers()]

colorRed = (0,0,255)
colorGreen = (0,255,0)

# #Loading Images
def detect_objects(img):
    starting_time = time.time()
    height, width, channels = img.shape

    # # Detecting objects
    blob = cv2.dnn.blobFromImage(img, 0.00392, (255, 255), (0, 0, 0), True, crop=False)

    yolo.setInput(blob)
    outputs = yolo.forward(output_layers)

    class_ids = []
    confidences = []
    boxes = []
    for output in outputs:
        for detection in output:
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.4 and ( class_id == 0 or class_id == 2 or class_id == 3 ): # 0 = orang, 2 = motor , 3 = mobil 
                center_x = int(detection[0] * width)
                center_y = int(detection[1] * height)
                w = int(detection[2] * width)
                h = int(detection[3] * height)

                x = int(center_x - w / 2)
                y = int(center_y - h / 2)

                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)


    indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
    org = 0
    mob = 0
    mot = 0

    for i in range(len(boxes)):
        if i in indexes:
            if class_ids[i] == 0:
                org = org + 1
                label = 'orang : ' + repr(org)
            elif class_ids[i] == 2:
                mob = mob + 1
                label = 'mobil : ' + repr(mob)
            elif class_ids[i] == 3:
                mot = mot + 1
                label = 'motor : ' + repr(mot)
            x, y, w, h = boxes[i]
            #label = str(classes[class_ids[i]]) + ', x=' +repr(x) + ',y=' +repr(y)
            #label = str(classes[class_ids[i]]) + '- no: ' + repr(i+1)
            cv2.rectangle(img, (x, y), (x + w, y + h), colorGreen, 3)
            cv2.putText(img, label, (x, y -5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255) )




    print("{")
    print(f"\"Jumlah Orang\": {org},")
    print(f"\"Jumlah Mobil\": {mob},")
    print(f"\"Jumlah Motor\": {mot},")

    elapsed_time = time.time() - starting_time
    print("\"processing time\":" + repr(elapsed_time)  )
    print("}")
    cv2.imwrite("output.jpg",img) 
    return {"Orang": org, "Mobil": mob, "Motor": mot, "time": elapsed_time }
    

@app.route("/detect", methods=["POST"])
def detect():
    file = request.files["image"]
    npimg = np.fromfile(file, np.uint8)
    image = cv2.imdecode(npimg, cv2.IMREAD_COLOR)
    result = detect_objects(image)
    return jsonify(result)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Dan hasilnya sebagai berikut :



Nah...bisa dan cepat bukan ? Jadi file gambar yang saya tampilkan output.jpg diatas merupakan hasil pengolahan gambar pada pc saya sebagai sever dan kemudian dikembalikan ke raspberry pi zero w sebagai json hasil perhitungan jumlah orang. Jadi cukup 1 server bisa melayani banyak alat pengambil gambar yang sama menggunakan raspberry pi zero w yang harganya lumayan murah.

Bagaimana jika PC nya tidak tersedia dan hanya ada jaringan wifi dilokasi ? Kita kan bahas selanjutnya memanfaatkan komputasi cloud dari google lanjut disini.
Share:

[raspi - yolo] Apakah raspberry pi w zero dapat menjalankan yolo ?

 



Wah gak kerasa sudah lama sekali saya tidak menyentuh raspberry pi zero saya hampir 4 tahun setelahnya baru sempat menyentuhnya lagi. Jadi pembaca yang ingin mengetahui bagaimana mengenal seri raspberry pi paling minim dapat kembali ke : https://www.aisi555.com/2020/07/solder-pi-bermain-dengan-gpio-dan.html dan bisa mengenal dasar-dasar raspberry pi dalam konteks GPIO dan padanannya ketika dimanfaatkan layaknya mikrokontroller. 



Kali ini saya yang sedang berkutat dengan dunia image processing berbasis yolo ingin menge-test apakah dengan processor dual core 1Ghz dan ram 512 Kb dapat menjalankan yolo ? Dari beberapa literatur di internet (dan chatgpt tentunya) kita akan menggunakan opencv dan yolo v3 tiny. Tidak lupa saya memformat ulang raspberry pi zero w saya dengan OS terbaru mengunakan software windows - Raspberry Pi Imager dan saya pilihkan Os  lite-32 tanpa desktop agar menghemat space. Jadi saya mengandalkan putty untuk meng-edit semua raspberry saya.



Raspberry Pi Zero W, dengan kekuatan pemrosesan dan memorinya yang terbatas, paling cocok untuk versi ringan YOLO (You Only Look Once) yang dioptimalkan untuk lingkungan dengan sumber daya rendah. Model YOLO umumnya intensif secara komputasi, jadi untuk Raspberry Pi Zero W, Anda harus fokus pada versi yang lebih ringkas yang dirancang untuk efisiensi. Berikut adalah versi yang lebih mungkin kompatibel:

  • YOLOv3 Tiny: Ini adalah versi sederhana dari YOLOv3 yang dirancang agar lebih cepat dan memerlukan lebih sedikit daya komputasi. Ini adalah pilihan bagus untuk perangkat dengan sumber daya terbatas.
  • YOLOv4 Tiny: Mirip dengan YOLOv3 Tiny, ini adalah versi YOLOv4 yang dioptimalkan dan dirancang untuk berjalan pada perangkat dengan kemampuan komputasi lebih rendah.
  • YOLO-Nano: Ini adalah model ringan lainnya yang bertujuan untuk memberikan keseimbangan yang baik antara kinerja dan penggunaan sumber daya.
  • Tiny YOLOv2: Versi ini juga terkenal dengan efisiensinya dan dapat menjadi pilihan yang cocok untuk Raspberry Pi Zero W.

Instalasi dan Pengaturan:

Berikut panduan umum tentang cara menjalankan salah satu model ini di Raspberry Pi Zero W Anda:

1. Instal dependencies:

Pastikan Anda telah menginstal Python (disarankan Python 3).
Instal perpustakaan yang diperlukan seperti NumPy, OpenCV, dan TensorFlow Lite atau PyTorch bergantung pada model dan kerangka kerja yang Anda pilih.

sudo apt-get update
sudo apt-get install python3-pip
sudo pip3 install numpy opencv-python



2. Unduh weight dan Konfigurasi pre-trained:

Unduh file konfigurasi Tiny YOLO dan file bobot. Misalnya, Anda bisa mendapatkan bobot dan konfigurasi YOLOv3 Tiny dari situs web resmi YOLO atau repositori GitHub.
Konversikan ke TensorFlow Lite (Opsional):


wget https://pjreddie.com/media/files/yolov3-tiny.weights
wget https://github.com/pjreddie/darknet/blob/master/cfg/yolov3-tiny.cfg -O yolov3-tiny.cfg
wget https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names


3.Jika menggunakan TensorFlow Lite, konversikan model ke format TensorFlow Lite, yang lebih dioptimalkan untuk perangkat tersemat.

pip3 install tensorflow
Lanjutkan instalasi converter tensorflow lite, jika ini terasa susah skip saja karena saya tidak menggunakannya

4.Jalankan Modelnya:

Tulis skrip untuk memuat model dan menjalankan inferensi. Berikut ini contoh sederhana menggunakan OpenCV dan model YOLOv3 Tiny:

import cv2
import numpy as np

net = cv2.dnn.readNet("yolov3-tiny.weights", "yolov3-tiny.cfg")
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)

def load_image(img_path):
    image = cv2.imread(img_path)
    blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)
    net.setInput(blob)
    return image

def get_outputs():
    layer_names = net.getLayerNames()
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    return output_layers

def detect_objects(image):
    detections = net.forward(get_outputs())
    for detection in detections:
        for obj in detection:
            scores = obj[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.5:
                center_x = int(obj[0] * image.shape[1])
                center_y = int(obj[1] * image.shape[0])
                w = int(obj[2] * image.shape[1])
                h = int(obj[3] * image.shape[0])
                x = int(center_x - w / 2)
                y = int(center_y - h / 2)
                cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
    return image

image = load_image("your_image.jpg")
output_image = detect_objects(image)
#cv2.imshow("Output", output_image)
#cv2.waitKey(0)
#cv2.destroyAllWindows()


Hasilnya ?


Gagal bokk...ternyata hasil nya sangat  tergantung pada kecerahan gambar dan sangat lambat jika menggunakan gamabr yang lebih besar resolusinya.


wah saya padahal punya proyek untuk mengatur nyala lampu dengan perbandingan jumlah orang dibawah lampu... lalu bagaimana ? Pada bagian selanjutnya saya akan memanfaatkan "komputer lain " yang akan dipinjam kekuatan komputasinya. Apakah google cloud bisa? Silahkan lanjut baca disini.. 

Share:

Sabtu, 15 Juni 2024

IOT - GPS dengan bantuan apps smartphone netGPS

 


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;
}

Dan jika script kamu benar dapat mengirimkan data GPS menggunakan lorawan seperti berikut :
 


Dan kemudian pengolahan data GIS nya kita dapat manfaatkan google maps atau yang secara online yaitu kepler.gl (lihat gambar paling atas). Keberhasilan pengaplikasian trik saya ini sangat bergantung pada sensitifitas GPS receiver pada smartphone, sehingga syarat mutlak adalah HP kamu berada pada lokasi yg open space / langit terbuka.

SELAMAT MENCOBA
Share:

Kontak Penulis



12179018.png (60×60)
+628155737755

Mail : ahocool@gmail.com

Site View

Categories

555 (8) 7 segmen (3) adc (4) amplifier (2) analog (19) android (12) antares (11) arduino (27) artikel (11) attiny (3) attiny2313 (19) audio (5) baterai (5) blog (1) bluetooth (1) chatgpt (2) cmos (2) crypto (2) dasar (46) digital (11) dimmer (5) display (3) esp8266 (26) euro2020 (13) gcc (1) gsm (1) iklan (1) infrared (2) Input Output (3) iot (73) jam (7) jualan (12) kereta api (1) keyboard (1) keypad (3) kios pulsa (2) kit (6) komponen (17) komputer (3) komunikasi (1) kontrol (8) lain-lain (8) lcd (2) led (14) led matrix (6) line tracer (1) lm35 (1) lora (11) lorawan (2) MATV (1) memory (1) metal detector (4) microcontroller (70) micropython (6) mikrokontroler (1) mikrokontroller (14) mikrotik (5) modbus (9) mqtt (3) ninmedia (5) ntp (1) paket belajar (19) palang pintu otomatis (1) parabola (88) pcb (2) power (1) praktek (2) project (33) proyek (1) python (8) radio (28) raspberry pi (9) remote (1) revisi (1) rfid (1) robot (1) rpm (2) rs232 (1) script break down (3) sdcard (3) sensor (2) sharing (3) signage (1) sinyal (1) sms (6) software (18) solar (1) solusi (1) tachometer (2) technology (1) teknologi (2) telegram (2) telepon (9) televisi (167) television (28) telkomiot (4) transistor (2) troubleshoot (3) tulisan (93) tutorial (108) tv digital (6) tvri (2) vu meter (2) vumeter (2) wav player (3) wayang (1) wifi (3) yolo (7)

Arsip Blog

Diskusi


kaskus
Forum Hobby Elektronika