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

  • Data Science

    Mengulik Digitalisasi data statistik dengan bantuan python untuk pemanfaatan di bidang transportasi, kesehatan, keuangan dan masih banyak lagi

  • Artificial Intelligence - Pengenalan Object

    Menghadirkan pemanfaatan AI dengan praktek-praktek yang mudah diikuti - cocok untuk mahasiswa yang mencari ide tugas akhir

  • 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

Tampilkan postingan dengan label esp8266. Tampilkan semua postingan
Tampilkan postingan dengan label esp8266. Tampilkan semua postingan

Selasa, 02 September 2025

[ESP32-A2DP] Talkie Suara Digital dari Era 80-an: Pelaporan Suhu DHT11 Lewat Audio

Bayangkan sebuah suara robotik yang khas, kaku, namun jelas – seperti suara dari film fiksi ilmiah tahun 1980-an. Suara itu bukan hasil rekaman manusia, bukan pula hasil AI modern seperti Google Assistant atau Siri. Suara itu adalah hasil dari sintesis vokal berbasis Linear Predictive Coding (LPC), teknologi canggih di masanya yang diusung oleh chip legendaris: TMS5100 dari Texas Instruments.



Hari ini, dengan ESP32 dan library modern seperti TalkiePCM ( kreasi Phill Schatzmann baca disini ), kita bisa menghidupkan kembali suara ikonik itu – bukan hanya untuk nostalgia, tapi untuk aplikasi nyata. Dalam proyek ini, saya berhasil membuat sistem pelaporan suhu dan kelembaban otomatis menggunakan sensor DHT11, yang kemudian "dibacakan" oleh suara digital vintage dan dikirim ke speaker Bluetooth. Mari kita telusuri sejarah menakjubkan di balik teknologi ini, dan bagaimana ia masih relevan di era IoT.


Dari Lab TI ke Mainstream: Kelahiran TMS5100 dan Revolusi Speech Synthesis

Pada akhir 1970-an, Texas Instruments (TI) merilis sebuah chip revolusioner: TMS5100. Ini bukan sekadar chip audio biasa. TMS5100 adalah prosesor LPC (Linear Predictive Coding) pertama yang dikomersialkan, mampu mensintesis suara manusia dari data terkompresi dengan sangat efisien – bahkan dengan memori yang sangat terbatas.

Teknologi LPC bekerja dengan memodelkan saluran vokal manusia secara matematis, bukan merekam suara asli. Alih-alih menyimpan audio mentah, chip ini menyimpan parameter seperti frekuensi fonasi, resonansi (formant), dan energi suara. Saat diaktifkan, ia "membangun" suara dari parameter ini, menghasilkan ucapan yang terdengar mekanis, tetapi cukup jelas untuk dipahami.


TMS5100 menjadi jantung dari berbagai produk populer:




  • Speak & Spell (1978): Mainan edukasi TI yang mengajarkan ejaan dengan suara robotik yang ikonik.
  • TI-99/4A Home Computer: Dengan modul ekspansi speech, komputer rumahan ini bisa berbicara.
  • Game Arcade Atari: Suara Darth Vader yang mengintimidasi di Star Wars Arcade (1983) dihasilkan oleh TMS5220, penerus TMS5100.
  • BBC Micro dan Apple Echo II: Menunjukkan bahwa teknologi ini diadopsi luas di dunia komputasi awal.
  • Suara dari chip-chip ini menjadi simbol era digital awal – suara dari masa depan yang dibayangkan di masa lalu.


Talkie: Membangkitkan Kembali Warisan TI di Abad 21

Talkie adalah proyek open-source yang menghidupkan kembali arsitektur TMS5100 secara perangkat lunak. Dikembangkan oleh Peter Knight dan dikembangkan lebih lanjut oleh Phil Schatzmann (pschatzmann), Talkie bukan emulator perangkat keras, melainkan reimplementasi algoritma LPC yang digunakan oleh TMS5100, dikompilasi menjadi kode C++ yang ringan dan platform-independent.


Yang membuat Talkie sangat menarik:

  • Header-only dan ringan: Cocok untuk mikrokontroler seperti ESP32.
  • Menghasilkan PCM 16-bit @ 8 kHz: Format dasar yang bisa diproses lebih lanjut.
  • Vokabular siap pakai: Termasuk Vocab_US_Large.h yang berisi ratusan kata seperti "temperature", "is", "degrees", "button", dan angka.
  • Kemampuan baca angka dan float: Bisa mengucapkan angka desimal secara otomatis.

Contoh vocabulary yang paling lengkap dan bisa kamu kreasikan sendiri  jika ada waktu : https://github.com/ptwz/python_wizard



Dengan Talkie, kita tidak hanya mendapatkan suara vintage – kita mendapatkan mesin TTS (Text-to-Speech) analog digital yang bisa digunakan untuk sistem informasi otomatis, tanpa bergantung pada cloud atau AI besar.


Proyek: Sistem Pelaporan Suhu Otomatis dengan ESP32 dan Bluetooth

Saya memanfaatkan kekuatan Talkie untuk membuat sistem yang fungsional dan futuristik-nostalgik sekaligus:


ESP32 membaca data dari sensor DHT11, lalu "mengucapkan" suhu dan kelembaban melalui speaker Bluetooth dengan suara robotik khas tahun 80-an. Jika tidak ada aktivitas selama 10 detik, sistem secara otomatis melaporkan kondisi terkini. 


Arsitektur Sistem

  • Sensor: DHT11 membaca suhu & kelembaban.
  • Sintesis Suara: Library TalkiePCM menghasilkan ucapan dari vokabular TI.
  • Konversi Format: Audio 8 kHz dari Talkie di-upsample ke 44.1 kHz agar kompatibel dengan Bluetooth A2DP.
  • Output Nirkabel: Data dikirim via A2DPStream ke speaker Bluetooth.
  • Interaksi: Dua tombol fisik untuk memicu pesan khusus ("Button one is press").
  • Integrasi dengan AudioTools


Karena Talkie hanya menghasilkan data PCM mentah, saya menggunakan AudioTools oleh pschatzmann untuk menghubungkannya ke dunia nyata:

  1. FormatConverterStream: Mengubah 8kHz → 44.1kHz.
  2. BufferedStream: Menjamin aliran data halus.
  3. A2DPStream: Mengirim audio ke Bluetooth dengan nama perangkat "K1".


FormatConverterStream out(a2dp);

BufferedStream bs(1024, out);

TalkiePCM voice(bs, 2); // Output ke stream dengan stereo



Setiap kali saya memanggil:


voice.say(sp3_TEMPERATURE);

voice.sayFloat(t, 2, 0, 1);

voice.say(sp3_CELCIUS);

bs.flush();


...ESP32 secara real-time menghasilkan suara "temperature is twenty three point five degrees" – dengan aksen robotik yang khas, lalu mengirimkannya ke speaker.


Berikut adalah contoh kode lengkap, sesuaikan dengan pin esp32 yg kamu pakai:

#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "AudioTools/AudioLibs/A2DPStream.h"
#include "TalkiePCM.h"
#include "Vocab_US_Large.h"
#include "DHT.h"

// Definisikan pin tombol
const int PIN_BUTTON_1 = 22; // Tombol untuk memutar audio 1
const int PIN_BUTTON_2 = 21; // Tombol untuk memutar audio 2

// === Bluetooth Name Sesuaikan dengan nama speaker kamu ===
const char* BT_NAME = "K1";

// === Audio Info seperti di contoh ===
AudioInfo from(8000, 2, 16);  // TTS (input: 8kHz, stereo - meski mono, kita isi 2)
AudioInfo to(44100, 2, 16);   // A2DP (output: 44.1kHz, stereo)

// === Stream ===
A2DPStream a2dp;
FormatConverterStream out(a2dp);
BufferedStream bs(1024, out);  // Buffer untuk smooth playback
TalkiePCM voice(bs, from.channels);

#define DHTTYPE DHT11  //Sensor DHT11
const int dhtPin = 23; //PIN DHT11 di ESP32


DHT dht(dhtPin, DHTTYPE);

// === Timer Idle ===
unsigned long lastActivity = 0;
const unsigned long IDLE_TIMEOUT = 10000; // 30 detik

void setup() {
  Serial.begin(115200);

  pinMode(PIN_BUTTON_1, INPUT_PULLUP);
  pinMode(PIN_BUTTON_2, INPUT_PULLUP);
    
  AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
  Serial.println(" TalkiePCM to A2DP - Dengan Idle Timer 30 Detik");
   
  // Setup konversi format: 8kHz → 44.1kHz, mono → stereo
  out.begin(from, to);
  
  // Setup Bluetooth
  auto cfg = a2dp.defaultConfig(TX_MODE);
  cfg.name = BT_NAME;
  cfg.silence_on_nodata = true;
  a2dp.begin(cfg);
  a2dp.setVolume(0.8);

  delay(2000); // Beri waktu Bluetooth stabil

  Serial.println(" Siap. Play Suhu dan Tekan tombol kapan saja...");

  dht.begin();

  playTemp();
  // Simpan waktu terakhir aktivitas
  lastActivity = millis();
}


void playTemp(){
   
 
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    bool fail = false;

    if (isnan(h) || isnan(t)) {
      Serial.println("Failed to read from DHT sensor!");
      fail =true;
    }
    
    Serial.print("Humidity: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature: "); 
    Serial.print(t);
    Serial.println(" °C ");
  
    //ngomong temp
    voice.say(sp4_THE);
    voice.say(sp3_TEMPERATURE);
    voice.say(sp2_IS);
    if(!fail){
       voice.sayFloat(t,2,0,1);
       voice.say(sp3_CELCIUS);
      }
     else{
       voice.say(sp2_DEVICE);
       voice.say(sp3_ERROR); 
     }
    delay(200);
    voice.say(sp4_PLEASE);
    voice.say(sp2_PRESS);
    voice.say(sp2_A);
    voice.say(sp2_BUTTON);
    bs.flush();
  }

void loop() {
  unsigned long now = millis();

  // Cek tombol 1
  if (digitalRead(PIN_BUTTON_1) == LOW) {
    Serial.println(" Tekan 1: Button one is press");
    delay(50);
    if (digitalRead(PIN_BUTTON_1) == LOW) {
      voice.say(sp2_BUTTON);
      voice.say(sp2_ONE);
      voice.say(sp2_IS);
      voice.say(sp2_PRESS);
      bs.flush();
      lastActivity = now; // Reset timer
    }
    while (digitalRead(PIN_BUTTON_1) == LOW) delay(10);
  }

  // Cek tombol 2
  if (digitalRead(PIN_BUTTON_2) == LOW) {
    Serial.println(" Tekan 2: Button two is press");
    delay(50);
    if (digitalRead(PIN_BUTTON_2) == LOW) {
      voice.say(sp2_BUTTON);
      voice.say(sp2_TWO);
      voice.say(sp2_IS);
      voice.say(sp2_PRESS);
      bs.flush();
      lastActivity = now; // Reset timer
    }
    while (digitalRead(PIN_BUTTON_2) == LOW) delay(10);
  }

  // Cek idle: jika tidak ada aktivitas selama 10 detik
  if (now - lastActivity > IDLE_TIMEOUT) {
    Serial.println(" Idle 10 detik: memutar suhu");

    playTemp();
    lastActivity = now; // Reset timer setelah main
  }

  delay(10); // Stabilisasi loop
}





Fitur Cerdas: Mode Idle Otomatis

Salah satu fitur menarik adalah mode idle. Jika tidak ada tombol yang ditekan selama 10 detik, sistem otomatis memutar pembacaan suhu terbaru:


Ini membuat sistem sangat cocok untuk:


  • Panel informasi cuaca mini di rumah.
  • Sistem monitoring IoT yang memberi laporan berkala.
  • Instalasi seni interaktif dengan nuansa retro-futuristik.


Mengapa Ini Penting di Era Modern?

Di tengah dominasi AI dan cloud, proyek seperti ini mengingatkan kita bahwa:


  • Komputasi edge bisa tetap cerdas tanpa internet.
  • Efisiensi dan kemandirian lebih penting daripada kompleksitas.
  • Nostalgia teknologi bisa menjadi inspirasi desain.
  • Talkie tidak hanya tentang suara – ia adalah penghormatan terhadap inovasi masa lalu yang masih bisa digunakan hari ini untuk membuat sesuatu yang unik, fungsional, dan menyenangkan.


Kesimpulan: Masa Lalu yang Masih Bisa Berbicara

Dengan kombinasi:

  • Sejarah chip TMS5100,
  • Rekayasa ulang oleh TalkiePCM,
  • Kemudahan integrasi melalui AudioTools,
  • Dan kreativitas dalam aplikasi IoT,

Saya berhasil membuat sistem yang bukan hanya bekerja, tapi berkarakter. Suara robotik yang keluar dari speaker Bluetooth bukan sekadar informasi – ia adalah nada waktu, pengingat bahwa teknologi lama tetap bisa bernyawa di tangan pembuat yang kreatif.

Jika Anda ingin membuat proyek IoT yang tidak hanya pintar, tapi juga punya jiwa, cobalah hidupkan kembali suara dari masa lalu. Karena terkadang, masa depan terdengar seperti tahun 1982.








Proyek ini menggunakan: ESP32, DHT11, TalkiePCM, Vocab_US_Large, AudioTools, dan A2DP. Semua kode open-source dan tersedia untuk dikembangkan lebih lanjut.

Share:

[ESP32-A2DP] MP3 player menggunakan SD Card dan kirim ke speaker Bluetooth

Pada artikel sebelumnya, kita telah membahas cara membuat suara sirene digital menggunakan ESP32 dan mengirimkannya melalui Bluetooth A2DP dengan bantuan library AudioTools oleh pschatzmann. Kali ini, kita akan melangkah lebih jauh dengan memanfaatkan kemampuan ESP32 untuk memutar file audio MP3 dari kartu SD dan mengalirkannya ke perangkat speaker Bluetooth secara nirkabel menggunakan protokol A2DP.



Namun, ada satu hal penting yang perlu ditekankan: dengan konfigurasi dan library tertentu — khususnya kombinasi AudioSourceSDFAT, CodecMP3Helix, dan A2DPStream — sistem ini hanya mampu memutar semua file MP3 pada scdard secara otomatis dan berulang (loop), tanpa kemampuan memilih file lain secara dinamis. Artikel ini akan menjelaskan mengapa hal ini terjadi, bagaimana sistem bekerja, serta batasan-batasan teknis yang perlu dipahami oleh pengembang.


1. Arsitektur Sistem: Alur Pemutaran Audio

Untuk memutar file MP3 dari SD card ke speaker Bluetooth, kita membutuhkan beberapa komponen utama:


  1. ESP32 – Sebagai mikrokontroler utama.
  2. Kartu SD – Menyimpan file audio MP3.
  3. Library AudioTools – Framework audio modular oleh pschatzmann.
  4. Codec MP3Helix – Untuk mendekode file MP3.
  5. A2DPStream – Untuk mengirim audio ke perangkat Bluetooth.
  6. AudioSourceSDFAT – Untuk membaca data dari SD card.






Alur kerjanya adalah:


  1. ESP32 membaca file MP3 dari SD card.
  2. File tersebut didekode menggunakan codec MP3Helix.
  3. Data audio dalam format PCM dikirim ke A2DPStream.
  4. A2DPStream mengirimkan data ke perangkat Bluetooth (seperti speaker atau headphone).
  5. Proses ini berjalan terus-menerus (loop) selama perangkat menyala.


2. Kode Dasar untuk Pemutaran MP3 via A2DP

Berikut adalah contoh kode minimal menggunakan library yang disebutkan:

#define HELIX_LOGGING_ACTIVE true

#include "AudioTools.h"
#include "AudioTools/AudioLibs/A2DPStream.h"
#include "AudioTools/Disk/AudioSourceSDFAT.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"

// === SD Card Pins ===
#define SD_CS   5
#define SD_MOSI 23
#define SD_MISO 19
#define SD_SCK  18

// === Konfigurasi ===
const char *startFilePath = "/"; //pindahkan file mp3 ke sdcard 
const char* ext = "mp3";

// === Objek Audio ===
AudioSourceSDFAT source(startFilePath, ext);
A2DPStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);

void setup() {
  Serial.begin(115200);
  AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);

  // Setup SPI dengan pin kamu
  SPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);

  // Setup player
  player.setVolume(0.8);  // 0.0 - 1.0
  player.begin();

  // Setup Bluetooth
  auto cfg = out.defaultConfig(TX_MODE);
  cfg.silence_on_nodata = true;
  cfg.name = "K1";  //  Ganti jadi nama speaker BT kamu
  out.begin(cfg);

  Serial.println(" Siap memutar MP3 ke K1...");
}

void loop() {
  player.copy();  // Otomatis mainkan file .mp3 dari SD
}



3. Bagaimana Pemilihan File Dilakukan?

Perhatikan bahwa dalam kode di atas, tidak ada perintah eksplisit untuk memilih nama file tertentu. Ini karena AudioSourceSDFAT — dalam konfigurasi default — secara otomatis membuka file pertama yang ditemukan di root direktori SD card dengan ekstensi .mp3.


Artinya:


  • Jika Anda memiliki file alarm.mp3, music.mp3, dan siren.mp3 di SD card, maka satu persatu fileakan diputar — biasanya yang pertama menurut urutan pembacaan sistem file (FAT).
  • Tidak ada fungsi seperti source.open("siren.mp3") yang tersedia secara langsung dalam implementasi dasar AudioSourceSDFAT saat digunakan dengan AudioPlayer dan A2DPStream.


4. Mengapa Tidak Bisa Memilih File Secara Bebas?

Ada beberapa alasan teknis mengapa pemilihan file dinamis tidak didukung secara langsung dalam pendekatan ini:


a. Abstraksi Tingkat Tinggi

Library AudioTools dirancang untuk mempermudah alur data audio, bukan sebagai sistem manajemen file. AudioSourceSDFAT bertindak sebagai source pasif yang hanya menyediakan data mentah, dan AudioPlayer mengharapkan sumber yang siap digunakan tanpa intervensi pengguna.


b. Keterbatasan A2DPStream

A2DPStream adalah output stream yang bersifat continuous. Begitu koneksi Bluetooth terbentuk, ia mengharapkan aliran data tanpa henti. Jika kita ingin berganti file, kita harus:


  • Menghentikan stream,
  • Mengganti file di sumber,
  • Memulai ulang koneksi A2DP.
  • Proses ini tidak stabil dan sering menyebabkan koneksi Bluetooth terputus atau speaker gagal reconnect.


c. Tidak Ada Event Callback untuk Akhir File

Dalam konfigurasi ini, saat file MP3 selesai diputar, AudioPlayer secara otomatis melakukan loop ke awal file yang sama, bukan melanjutkan ke file berikutnya. Tidak ada event seperti onFinish() yang bisa digunakan untuk beralih ke file lain.


5. Sistem Ini Dirancang untuk Pemutaran Loop Otomatis

Justru karena keterbatasan ini, sistem ini sangat cocok untuk aplikasi yang membutuhkan pemutaran terus-menerus dari satu file, seperti:


  • Alarm atau sirene darurat yang harus berbunyi terus hingga dimatikan.
  • Panduan suara di pameran yang memutar satu rekaman berulang-ulang.
  • Speaker background music di toko kecil dengan satu playlist statis.


Keuntungannya:


  • Stabil: Tidak ada perpindahan file yang bisa merusak koneksi Bluetooth.
  • Sederhana: Tidak perlu logika kompleks untuk manajemen file.
  • Hemat sumber daya: Cocok untuk sistem embedded dengan memori terbatas.


6. Solusi Alternatif (Jika Ingin Pemilihan File)

Jika Anda benar-benar membutuhkan kemampuan memilih file, beberapa opsi yang bisa dipertimbangkan:


  • Gunakan SPIFFS atau SD card dengan daftar file statis dan buat logika manual untuk mengganti sumber.
  • Restart seluruh pipeline audio (player.end(), ganti file, player.begin()).
  • Gunakan Bluetooth Sink + ESP32 sebagai penerima, lalu keluarkan ke DAC/I2S — lebih stabil untuk kontrol file.
  • Gunakan platform lain seperti ESP32-S3 dengan dukungan RTOS dan codec lebih baik.
  • Namun, semua ini keluar dari lingkup kemudahan yang ditawarkan oleh AudioSourceSDFAT + A2DPStream secara default.


Silahkan baca bagaimana memutar file Mp3 untuk pembacaan LM35 disini : https://www.aisi555.com/2024/05/esp8266-kirim-data-suhu-lm35-lewat.html


7. Kesimpulan: Simplicity with a Trade-off

Kombinasi library:


#include "AudioTools.h"

#include "AudioTools/AudioLibs/A2DPStream.h"

#include "AudioTools/Disk/AudioSourceSDFAT.h"

#include "AudioTools/AudioCodecs/CodecMP3Helix.h"


menawarkan solusi cepat, mudah, dan stabil untuk memutar satu file MP3 dari SD card secara loop terus-menerus melalui Bluetooth A2DP.

Namun, pengguna harus menerima kenyataan bahwa sistem ini tidak dirancang untuk navigasi file atau pemilihan lagu dinamis. Ini bukan kelemahan, melainkan konsekuensi dari desain yang mengutamakan kesederhanaan dan keandalan.

Jika proyek Anda membutuhkan pemutaran otomatis tanpa intervensi, seperti sistem alarm, panduan otomatis, atau iklan berulang, maka pendekatan ini sangat ideal. Tapi jika Anda ingin membuat music player dengan playlist dan kontrol, Anda perlu mempertimbangkan arsitektur yang lebih kompleks.

Dengan memahami batasan ini sejak awal, Anda bisa merancang sistem yang realistis, efisien, dan sesuai kebutuhan — tanpa frustrasi karena ekspektasi yang salah terhadap kemampuan library.

AudioTools tetap menjadi salah satu library paling powerful untuk proyek audio di ESP32, selama kita tahu apa yang bisa dan tidak bisa dilakukannya.

Share:

[ESP32-A2DP] Berkenalan dengan library siap pakai AudioTools - Membuat Sirene

Dalam era Internet of Things (IoT), mikrokontroler seperti ESP32 telah menjadi pilihan populer karena kemampuannya yang luar biasa, termasuk konektivitas Wi-Fi dan Bluetooth. Salah satu fitur menarik dari ESP32 adalah dukungan terhadap Bluetooth A2DP (Advanced Audio Distribution Profile), yang memungkinkan perangkat mengirimkan aliran audio berkualitas tinggi ke perangkat audio nirkabel seperti speaker Bluetooth. Dengan memanfaatkan library AudioTools yang dikembangkan oleh pschatzmann, proses pengiriman audio melalui A2DP menjadi jauh lebih mudah dan terstruktur.





Pada artikel ini, kita akan membahas bagaimana ESP32 dapat digunakan untuk menghasilkan suara sirene digital dan mengirimkannya ke speaker Bluetooth menggunakan protokol A2DP dengan bantuan library AudioTools. Pembahasan akan mencakup dasar teori, arsitektur sistem, serta konsep pembangkitan sinyal audio secara real-time.


1. Apa itu A2DP dan Mengapa ESP32 Cocok untuk Ini?

A2DP adalah protokol Bluetooth yang memungkinkan transmisi data audio secara nirkabel dari sumber (seperti smartphone atau mikrokontroler) ke perangkat penerima (seperti headphone atau speaker). ESP32 dilengkapi dengan modul Bluetooth dual-mode (Classic Bluetooth dan BLE), sehingga mampu bertindak sebagai sumber audio A2DP (Source).

Dengan fitur ini, ESP32 bisa digunakan sebagai pemutar musik, interkom nirkabel, atau bahkan sistem peringatan seperti sirene darurat yang dikirim ke speaker Bluetooth di lokasi tertentu.


2. Peran Library AudioTools oleh pschatzmann

Salah satu tantangan dalam pengembangan aplikasi audio di ESP32 adalah kompleksitas manajemen buffer, codec, dan sinkronisasi aliran data. Di sinilah library AudioTools oleh pschatzmann sangat membantu. Library ini menyediakan abstraksi tingkat tinggi yang mempermudah proses pembuatan, manipulasi, dan transmisi audio.


Beberapa keunggulan AudioTools:

  1. Mendukung berbagai format audio (WAV, MP3, RAW, dll).
  2. Memiliki antarmuka modular untuk input, output, dan prosesor audio.
  3. Integrasi langsung dengan A2DP melalui BluetoothA2DPSink atau BluetoothA2DPSource.
  4. Mudah digunakan dengan sintaks yang intuitif.


Untuk proyek ini, kita akan menggunakan BluetoothA2DPSink agar ESP32 dapat menerima audio, tetapi karena tujuan kita adalah mengirim audio sirene, maka kita sebenarnya membutuhkan mode A2DP Source. Namun, perlu dicatat bahwa implementasi A2DP Source di ESP32 masih terbatas dibandingkan Sink. Oleh karena itu, alternatifnya adalah menghasilkan audio secara internal dan mengirimkannya melalui koneksi Bluetooth sebagai sumber virtual, atau menggunakan pendekatan lain seperti mengirim audio melalui DAC atau I2S, lalu memancarkannya via Bluetooth dari perangkat lain.

Namun, dengan perkembangan library AudioTools, pschatzmann telah menyediakan dukungan eksperimental untuk A2DP Source, yang memungkinkan ESP32 berperan sebagai pengirim audio. Ini adalah kunci dari proyek kita.


3. Konsep Pembangkitan Suara Sirene Digital

Suara sirene umumnya merupakan gelombang suara dengan frekuensi yang berubah-ubah secara periodik, seperti naik-turun (wailing) atau berdenyut (yelp). Untuk menghasilkan efek ini secara digital, kita bisa menggunakan osilator gelombang sinus dengan frekuensi yang dimodulasi terhadap waktu.


Secara matematis, sinyal sirene dapat direpresentasikan sebagai:


s(t)=A⋅sin(2Ï€⋅f(t)⋅t)

di mana:

A adalah amplitudo (volume),

f(t) adalah frekuensi yang berubah terhadap waktu (misalnya, dari 500 Hz ke 1500 Hz secara linier).


Modulasi frekuensi ini bisa diimplementasikan dengan memvariasikan nilai frekuensi osilator dalam loop utama program. ESP32 cukup cepat untuk menghitung nilai sinus ini secara real-time menggunakan fungsi sin() dari C++ (dengan optimasi jika perlu).


4. Arsitektur Sistem

Berikut adalah komponen utama sistem:


  1. ESP32 – Sebagai otak sistem, menghasilkan sinyal audio dan mengelola koneksi Bluetooth.
  2. Library AudioTools – Menyediakan framework untuk pembangkitan dan transmisi audio.
  3. Bluetooth A2DP Source – Mengirimkan aliran audio ke perangkat penerima.
  4. Speaker Bluetooth – Perangkat yang menerima dan memutar suara sirene.


Alur kerja sistem:


  1. ESP32 menginisialisasi modul Bluetooth dalam mode A2DP Source.
  2. Secara berkala, ia menghasilkan sampel audio sirene (dalam bentuk array nilai 16-bit signed).
  3. Sampel ini dikirim ke buffer audio yang dikelola oleh AudioTools.
  4. Library AudioTools menangani enkapsulasi data ke format A2DP dan transmisi nirkabel.
  5. Speaker Bluetooth menerima data, mendekode, dan memutar suaranya.


5. Implementasi Teoritis - Praktek dengan AudioTools

Untuk membuat sirene, kita bisa membuat kelas atau fungsi yang menghasilkan sampel audio setiap waktu. Berikut adalah gambaran kode teoritis:

Silahkan download library yang sesuai di googling saja dengan kata kunci :  AudioTools - Pschatzmann dan ESP32-A2DP

Test 1 - Tone Generator : 

#include "BluetoothA2DPSource.h"
#include <math.h>

// Objek utama untuk mengelola koneksi Bluetooth
BluetoothA2DPSource a2dp_source;

// Frekuensi nada yang akan dihasilkan dalam Hz (A4)
#define A4_FREQUENCY 1000.0

// Variabel untuk melacak waktu/fase agar gelombang sinyal kontinu
static float m_time = 0.0;

// Fungsi callback untuk menghasilkan data audio
int32_t get_data_frames(Frame *frame, int32_t frame_count) {
  // Volume, dari -32,768 hingga 32,767
  float m_amplitude = 10000.0;
  // Interval waktu antar sampel, untuk sample rate 44.1 kHz
  float m_deltaTime = 1.0 / 44100.0;
  float pi_2 = PI * 2.0;

  // Isi buffer frame dengan data sinyal
  for (int sample = 0; sample < frame_count; ++sample) {
    // Hitung sudut untuk gelombang sinus
    float angle = pi_2 * A4_FREQUENCY * m_time;
    // Hasilkan sinyal
    int16_t signal = (int16_t)(m_amplitude * sin(angle));
    
    // Isi data ke kedua saluran (stereo)
    frame[sample].channel1 = signal;
    frame[sample].channel2 = signal;
    
    // Majukan waktu
    m_time += m_deltaTime;
  }
  return frame_count;
}

// Callback untuk melacak status koneksi
void connection_state_changed(esp_a2d_connection_state_t state, void *ptr){
  Serial.println(a2dp_source.to_str(state));
}

void setup() {
  Serial.begin(115200);
  a2dp_source.set_auto_reconnect(false);
  a2dp_source.set_on_connection_state_changed(connection_state_changed);
  a2dp_source.set_data_callback_in_frames(get_data_frames);
  a2dp_source.set_volume(100);
  a2dp_source.start("K1"); // Ubah K1 dengan nama speaker BT anda
}

void loop() {
  // Hanya jeda untuk mencegah watchdog timer
  delay(10);
}


Test 2- Sirene Generator : 

#include "AudioTools.h"
#include "BluetoothA2DPSource.h"

using namespace audio_tools;

// Generator dan Bluetooth
SineWaveGenerator<int16_t> sine_gen;
BluetoothA2DPSource a2dp_source;

// Konfigurasi sirene
#define SIRENE_FREQ_1 800.0f
#define SIRENE_FREQ_2 1000.0f
#define DURATION_S_TONE 0.5f  // 0.5 detik

// State
float current_frequency = SIRENE_FREQ_1;
float time_of_last_switch = 0.0f;
float m_time = 0.0f;
const float sample_rate = 44100.0f;

// Buffer
const int FRAME_COUNT = 1024;
int16_t buffer_mono[FRAME_COUNT];
Frame buffer_stereo[FRAME_COUNT];  // channel1 & channel2

// Callback: generate audio data
int32_t get_data_frames(Frame *frame, int32_t frame_count) {
  // Ganti frekuensi setiap DURATION_S_TONE detik
  if (m_time - time_of_last_switch >= DURATION_S_TONE) {
    current_frequency = (current_frequency == SIRENE_FREQ_1) ? SIRENE_FREQ_2 : SIRENE_FREQ_1;
    time_of_last_switch = m_time;
    sine_gen.setFrequency(current_frequency);
  }

  // Gunakan readBytes() — memindah byte audio
  size_t bytes_to_read = frame_count * sizeof(int16_t);
  size_t bytes_read = sine_gen.readBytes((uint8_t*)buffer_mono, bytes_to_read);
  size_t samples_read = bytes_read / sizeof(int16_t);

  // Konversi mono → stereo
  for (int i = 0; i < samples_read; i++) {
    frame[i].channel1 = buffer_mono[i];
    frame[i].channel2 = buffer_mono[i];
  }

  // Update waktu
  m_time += samples_read / sample_rate;

  return samples_read;
}

// Callback: status koneksi Bluetooth
void connection_state_changed(esp_a2d_connection_state_t state, void *ptr) {
  Serial.println(a2dp_source.to_str(state));
}

void setup() {
  Serial.begin(115200);
  while (!Serial);

  // Konfigurasi audio
  AudioInfo cfg;
  cfg.sample_rate = 44100;
  cfg.channels = 1;
  cfg.bits_per_sample = 16;

  // Inisialisasi generator
  sine_gen.begin(cfg);
  sine_gen.setFrequency(SIRENE_FREQ_1);      // Mulai dari 800 Hz
  sine_gen.setAmplitude(10000.0f);           // Amplitude ~30% dari max

  // Setup Bluetooth
  
  a2dp_source.set_auto_reconnect(true);
  a2dp_source.set_volume(100);  // 0–100
  a2dp_source.set_on_connection_state_changed(connection_state_changed);
  a2dp_source.set_data_callback_in_frames(get_data_frames);

  Serial.println("Mencoba terhubung ke speaker 'K1'...");
  a2dp_source.start("K1"); //Sesuaikan dengan nama speaker BT anda
}

void loop() {
  delay(10);
}


6. Tantangan dan Solusi

Keterbatasan A2DP Source di ESP32: ESP-IDF lebih mendukung A2DP Sink. Implementasi Source masih dalam pengembangan. Solusi: Gunakan versi terbaru library AudioTools yang telah mengatasi sebagian besar isu ini.

  1. Kualitas Audio: Penggunaan fungsi sin() secara real-time bisa memberatkan CPU. Optimasi dengan lookup table (LUT) sinus dapat mengurangi beban prosesor.
  2. Latensi dan Buffering: Pastikan buffer audio cukup besar untuk menghindari crackling atau putus-putus suara.
  3. Daya Baterai: Transmisi Bluetooth konstan memakan daya. Untuk aplikasi portabel, pertimbangkan untuk mengaktifkan sirene hanya saat diperlukan.


7. Aplikasi Nyata

Sistem ini bisa digunakan dalam berbagai skenario:


  1. Alarm rumah pintar yang memicu sirene melalui speaker Bluetooth di seluruh rumah.
  2. Sistem peringatan bencana berbasis IoT.
  3. Proyek edukasi tentang pemrosesan sinyal digital dan komunikasi nirkabel.


Kesimpulan

Dengan kombinasi ESP32, library AudioTools oleh pschatzmann, dan protokol Bluetooth A2DP, kita dapat membangun sistem audio yang canggih namun tetap sederhana secara konseptual. Pembuatan suara sirene digital adalah contoh nyata bagaimana mikrokontroler modern bisa digunakan tidak hanya untuk kontrol logika, tetapi juga untuk pemrosesan sinyal waktu-nyata.

Meskipun ada tantangan teknis, terutama terkait dukungan A2DP Source, perkembangan library open-source seperti AudioTools terus mempermudah pengembangan aplikasi audio di platform ESP32. Dengan sedikit pemahaman tentang dasar-dasar gelombang suara dan modulasi frekuensi, siapa pun bisa membuat sistem audio nirkabel yang fungsional dan kreatif.

Proyek ini membuka pintu bagi eksplorasi lebih lanjut, seperti menambahkan kontrol jarak jauh via Wi-Fi, menyimpan audio dalam SD card, atau bahkan menggabungkan dengan sensor untuk sistem alarm otomatis. Dunia audio di IoT menanti untuk dieksplorasi!

Share:

Sabtu, 28 Juni 2025

Modifikasi Mobil - Mobilan RC 27Mhz Menjadi Berbasis WiFi

 



Tulisan kali ini sebenarnya masih ada hubungannya dengan kerjasama saya Bersama Fakultas Vokasi Unesa dalam hal penelitian dibidang Transportasi. Gaya nya sih pengen membuat robot AGV seperti di gudang nya amazon sana yang banyak bertebaran videonya di medsos, namun seperti biasa kita mainan yang kecil-kecilan dulu. Yuk kita utak-atik mobil-mobilan RC Truk seharga 100ribuan dulu. Sekalian mau melihat bagaimana sih kontrol yang didalamnya ?



Truk mainan Remote diatas sangat umum di toko mainan bahkan mungkin hampir dimiliki oleh semua anak kecil (cowok) karena hemat saya lebih mengajarkan kegiatan outdoor ke anak daripada berkutat dengan smartphone melulu. Namun setelah saya perhatikan jangkauan kendali remotenya cukup pendek sehingga otomatis ketika diatas 5 meter gak akan nyampai tuh kendalinya. Menjadi keheranan saya kenapa sependek itu? Apakah agar anak ikutan berlari dibelakang mobil-mobilan? Namun secara teknis ini terjadi karena ketika saya bongkar antena di dalam kontroler disisi mobil-mobilannya hanya berupa kabel saja. Harus dikasi tambahan apa ya agar jauh?



Gambar diatas merupakan modul kontroler yang dapat dibeli di berbagai onlineshop (bahkan sepaket dengan motor DC nya) hanya 25 ribu saja (dapet untung dari mana coba produsennya ...ckckckckc). Dari berbagai tulisan dan video di internet rata-rata yang dilakukan adalah menambahkan antena dan jangkauannya gak jauh juga. Nah bagaimana dengan memanfaatkan wifi dirumah yang lumayan jauh jangkauannya ? Kita bisa gunakan bantuan mikrokontrolelr ber wifi macam ESP8266/ESP32.

Prinsipnya sebagai berikut:

1. Gunakan relay 5V atau transistor sebagai penyambung tombol yang ada pada modul remote, kemudian di kontrol oleh pin dari ESP32. Saya lebih memilih transistor kebetulan ada banyak transistor PNP 2N3906 di gudang sehingga hanya butuh tambahan resistor 1K ke basis dan kontrol logika aktif LOW untuk menyambungkan tombol switch. Ilustrasinya seperti berikut : 




2. ESP32 terhubung dengan wifi rumah dan menggunakan komunikasi UDP / TCP sebagai protokol pengiriman data. Perintah yg dikirim berupa teks "1" untuk maju, "2" untuk mundur, "3" belok kiri, "4" belok kanan dan "0" untuk stop.
3. Untuk kontrol jarak jauh (kanan kiri maju mundur) bisa menggunakan aplikasi smartphone android UDP terminal, seperti pada gambar paling atas. Saya ada juga koding APK sendiri namun saya bahas lain waktu. 
4. Remote dapat di bongkar menyisakan PCB nya saja dan taruh  bersama ESP32 diatas / didalam back truk. Jangan lupa menambahkan Power suply berupa powerbank agar dapat mensuply ESP32 dan juga modul remote. 
5. Saya menyambungkan pin 3.3 Volt dari ESP32 ke pin baterai + dari modul remote. 










Koding di ESP32 :


#include <WiFi.h>
#include <WiFiUdp.h>

// Konfigurasi WiFi sesuaikan
const char* ssid = "nama wifi";
const char* password = "paswordnya";

// IP statis sesuaikan, agar tidak berubah ubah
IPAddress ip(192, 168, 1, 177);
IPAddress gateway(192, 168, 1, 1);     // samakan IP router yg digunakan
IPAddress subnet(255, 255, 255, 0);

// Port UDP nya bebas
unsigned int udpPort = 4210;
WiFiUDP udp;

// Sinyal ke Transistor sesuaikan dengan pin di ESP32 yg kamu pakai
#define MAJU_PIN    15
#define MUNDUR_PIN   2
#define KANAN_PIN    4
#define KIRI_PIN     5

void setup() {
  // Inisialisasi pin sebagai output
  pinMode(MAJU_PIN, OUTPUT);
  pinMode(MUNDUR_PIN, OUTPUT);
  pinMode(KANAN_PIN, OUTPUT);
  pinMode(KIRI_PIN, OUTPUT);

  // Set awal semua pin HIGH (non-aktif karena aktif LOW)
  digitalWrite(MAJU_PIN, HIGH);
  digitalWrite(MUNDUR_PIN, HIGH);
  digitalWrite(KANAN_PIN, HIGH);
  digitalWrite(KIRI_PIN, HIGH);

  // Mulai koneksi WiFi dengan IP statis
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(ssid, password);

  Serial.begin(115200);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nKoneksi WiFi berhasil");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Mulai UDP
  udp.begin(udpPort);
  Serial.printf("Listening on UDP port %d\n", udpPort);
}

void maju()
{

    digitalWrite(MAJU_PIN, LOW );
    digitalWrite(MUNDUR_PIN, HIGH);
    digitalWrite(KANAN_PIN, HIGH);
    digitalWrite(KIRI_PIN, HIGH);

  Serial.println("ini maju ...");
}

void mundur()
{

    digitalWrite(MAJU_PIN, HIGH );
    digitalWrite(MUNDUR_PIN, LOW);
    digitalWrite(KANAN_PIN, HIGH);
    digitalWrite(KIRI_PIN, HIGH);

    Serial.println("ini mundur ...");

}

void kiri()
{

    digitalWrite(MAJU_PIN, LOW);
    digitalWrite(MUNDUR_PIN, HIGH);
    digitalWrite(KANAN_PIN, HIGH);
    digitalWrite(KIRI_PIN, LOW);


    Serial.println("ini ngiri ...");
  
}

void kanan()
{

    digitalWrite(MAJU_PIN, LOW );
    digitalWrite(MUNDUR_PIN, HIGH);
    digitalWrite(KANAN_PIN, LOW);
    digitalWrite(KIRI_PIN, HIGH);

    Serial.println("ini nganan ...");
}

void mandek()
{

    digitalWrite(MAJU_PIN, HIGH);
    digitalWrite(MUNDUR_PIN, HIGH);
    digitalWrite(KANAN_PIN, HIGH);
    digitalWrite(KIRI_PIN, HIGH);

    Serial.println("ini mandek ...");
}

void loop() {


  char packetBuffer[255]; // Buffer untuk menerima data UDP

  // Cek apakah ada paket UDP masuk
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(packetBuffer, 255);
    if (len > 0) {
      packetBuffer[len] = '\0'; // Null-terminate string
    }

    Serial.printf("Pesan diterima: %s\n", packetBuffer);

    // Proses perintah 1=maju 2=mundur 3=kiri 4=kanan 0=stop , silahkan berkreasi untuk model pergerakan sesuai yg anda mau
    if (strcmp(packetBuffer, "1") == 0) {
      maju();
      delay(600);
      mundur();
      delay(200);
      mandek();
      
    } else if (strcmp(packetBuffer, "2") == 0) {
      mundur();
      delay(600);
      maju();
      delay(200);
      mandek();
    } else if (strcmp(packetBuffer, "3") == 0) {
      kiri();
      delay(700);
      mundur();
      delay(150);   
      mandek();
    } else if (strcmp(packetBuffer, "4") == 0) {
      kanan();
      delay(700);
      mundur();
      delay(150);   
      mandek();
    } else if (strcmp(packetBuffer, "0") == 0) {
      // Semua tetap HIGH → berhenti total
      mandek();
      Serial.println("Perintah BERHENTI diterima");
    }

    
  }
  
}





SELAMAT MENCOBA !

Share:

Rabu, 08 Mei 2024

[AI] Kirim Data Suhu LM35 Lewat Radio Amatir / HT


 
Disclaimer : " Penggunaan frekuensi radio amatir merupakan domain dari Kemkominfo, ORARI dan RAPI. Jangan sekali-kali menggunakan frekuensi amatir untuk tujuan lain daripada Amatir Radio yang tunduk pada aturan-aturan baku yang mengikat. Jika perangkat kamu akan memancarkan pembacaan data, silahkan berkomunikasi lanjut dengan pengelola lokal orari setempat agar tidak terjadi masalah dikemudian hari. Tulisan ini hanya sebagai pembuktian teknologi radio untuk pelaporan data jaman sekarang"

Suatu hari ketika saya scanning frekuensi maritim (150Mhz-170Mhz) menggunakan RTL-SDR yang beberapa bulan ini sedang saya gandrungi, terdengar sayup-sayup suara operator yang membacakan laporan cuaca di wilayah syahbandar pelabuhan tanjung perak Surabaya. Suaranya begitu "kurang bersemangat", mungkin karena dia sadar data cuaca untuk kapal shiping line, sudah lengkap dan mudah didapatkan melalui internet atau radar mereka, jadi operator ini hanya menjalankan tugas rutinitas yang harus dia lakukan sesuai perintah instansi. 

Tahukah kamu, di belahan bumi Amerika utara terdapat stasiun radio amateur WWV yang bekerja pada 2,5Mhz, 5Mhz, 10 Mhz, 15 Mhz dan 20Mhz, dimana secara berkala akan memberikan informasi waktu yang akurat berdasarkan jam atom. Yang berbicara di radio adalah suara mesin/komputer. Sempat juga saya melihat youtuber asal kanada yg scanning radio di dekat pelabuhan, laporan cuaca nya dibacakan juga oleh komputer, seperti halnya suara google map ketika kita berkendara dan menggunakan panduan arah.

Lalu teringat lah saya pada suatu kejadian letusan gunung merapi dan gunung agung beberapa tahun yg lalu, dimana laporan kegempaan seismik ditautkan pada sebuah frekuensi radio amatir VHF dengan tone yang berayun jika ada guncangan / letusan di puncak. Dan di wilayah rural seperti pegunungan, radio amatir / HT menjadi sarana komunikasi handalan. Jadi penggunaan radio HT untuk pengiriman data serta warning lumayan masih diperlukan.

Lalu bagaiman sih caranya menggantikan operator manusia menjadi suara text to speech (TTS) layaknya suara google ? Ayo saya share caranya... Pertama-tama tentunya kamu butuh memiliki sepasang radio HT / Walkie Talkie untuk melakukan experimen ini. Dan saya pilihkan merek baofeng yang sedang laris manis digunakan satpam dimana-mana.



Saya akan menggunakan mikrocontroller ESP8266 - Wemos yang berfungsi sebagai :

  1. Pembaca Sensor Suhu LM35
  2. Penerjemah pembacaan suhu menjadi urutan file mp3
  3. Pemutar audio mp3
  4. Pemutus - sambung transmit PTT (Push To Talk) sesuai kebutuhan


Dengan komponen beserta rangkaian secara lengkap seperti ini :



Lalu pertanyaannya apakah ESP8266 dapat memutar file audio langsung tanpa memerlukan player lainnya semacan DF player ? Ooooo itu sudah lama saya tau, dengan memanfaatkan I2S dari generasi ESP8266 atau ESP32 maka memutar file audio sangatlah mudah. Silahkan meluncur ke : https://github.com/earlephilhower/ESP8266Audio untuk belajar bagaimana cara memutar beberapa jenis file audio secara langsung, tanpa atau dengan DAC.

Untuk melakukan generate audio robot, maka gunakan layanan TTS (text to speech) yang versi bahasa Indonesia secara gratis dapat di googling seperti BOTIKA. Kalau punya teman atau saudara yg suaranya renyah dapat juga direkam beberapa file pembacaan angka "satu", "dua", "sebelas" dan lainnya. Seperti contoh yang saya buat dan letakkan di folder /data dibawah directory project arduino saya.



Untuk melakukan upload file suara mp3 ke ESP8266 maka diperlukan library SPIFFS yang memungkinkan ESP memiliki penyimpanan file tersendiri. Langkah pengaturan dan cara upload nya bisa dibaca disini : https://randomnerdtutorials.com/install-esp8266-filesystem-uploader-arduino-ide/.


Sedangkan script lengkap yang bisa kalian coba, saya bagikan secara FREE alias GRATEEESS 


/* Pengubah pembacaan Suhu LM35 ke suara dan dikirim via HT Baofeng  
   www.aisi555.com  08155737755 nyoman yudi kurniawan 2024
   
   Pada IDE gunakan mode clock ESP8266 160MHz, serta mode SPIFFS sebesar 1MB atau lebih
   Pelajari cara "Tools->ESP8266/ESP32 Sketch Data Upload" untuk menyimpan MP3 di SPIFFS
   Baca disini : https://randomnerdtutorials.com/install-esp8266-filesystem-uploader-arduino-ide/
   File mp3 simpan di folder /data pada direktori sketch arduino, dengan nama file berikut
   rekam sendiri di HP/PC atau gunakan layanan text to speech untuk membuat tiap file

   0.mp3  
   1.mp3
   2.mp3
   3.mp3
   4.mp3
   5.mp3
   6.mp3
   7.mp3
   8.mp3
   9.mp3
   10.mp3 ==> sepuluh
   11.mp3 ==> sebelas
   1_1.mp3 ==> belas
   p.mp3  ==> puluh
   d.mp3  ==> derajat
   k.mp3  ==> koma
   s.mp3  ==> ucapan pertama /salam
  
  
  Selanjutnya bisa baca di www.aisi555.com
  Gunakan secara gratis dan bertanggung jawab
  semua aturan penggunaan frekuensi radio amatir mengacu pada ORARI/RAPI/KEMKOMINFO/BALMON
*/

#include <Arduino.h>
#include <ESP8266WiFi.h>

#include "AudioFileSourceSPIFFS.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"

//sesuaikan pin PTT 
#define ptt 5
#define lm35 A0

int16_t suhu = 337;
uint8_t proses = 0;


AudioGeneratorMP3 *mp3;
AudioFileSourceSPIFFS *file;
AudioOutputI2SNoDAC *out;



void setup()
{
  pinMode(ptt, OUTPUT);
  digitalWrite(ptt, LOW);
  WiFi.mode(WIFI_OFF); 
  Serial.begin(115200);
  delay(1000);
  SPIFFS.begin();
  Serial.println("======= www.aisi555.com ========");
  Serial.println("  kirim Suhu LM 35 ke HT yukkkkk");
  Serial.println("================================");
  
  audioLogger = &Serial;

  //baca suhu LM35 di A0, Vin = 3.3 volt

    int16_t analogValue = analogRead(lm35);
    
    suhu = analogValue  * (3300/1024);
    Serial.println(analogValue);
    Serial.print("Suhu : ");
    Serial.println(suhu);
  

  //awal mulai pertama
  proses=1;
  //kirim PTT dan kirim suara ke HT
  digitalWrite(ptt, HIGH);
  delay(500);
  
  file = new AudioFileSourceSPIFFS("/s.mp3");
  out = new AudioOutputI2SNoDAC();  
  mp3 = new AudioGeneratorMP3();
  mp3->begin(file, out);
}


void tts(int16_t angka) //disini proses memilih angka jadi file mp3
{   
    uint8_t pilih;
    if (proses == 2) {
       pilih = angka/100;

      if ( angka <200 && angka >=120 ) pilih = (angka /10) % 10;
      else if (angka <120 && angka >=110) pilih =11; 
      else if (angka <110 && angka >=100) pilih =10;
      else if (angka <100 && angka >=0) pilih = angka /10;
    
    }
    else if (proses == 4){  
      pilih = (angka /10) % 10;
      if (pilih == 0) return;
      if (angka <200 && angka >=0) return;  
    
    }
    else if (proses == 6) pilih = angka % 10;

   switch (pilih) {
     case 0 : file = new AudioFileSourceSPIFFS("/0.mp3");
        break;
     case 1 : file = new AudioFileSourceSPIFFS("/1.mp3");
        break;
     case 2 : file = new AudioFileSourceSPIFFS("/2.mp3");
        break;
     case 3 : file = new AudioFileSourceSPIFFS("/3.mp3");
        break;   
     case 4 : file = new AudioFileSourceSPIFFS("/4.mp3");
        break;
     case 5 : file = new AudioFileSourceSPIFFS("/5.mp3");
        break;
     case 6 : file = new AudioFileSourceSPIFFS("/6.mp3");
        break;
     case 7 : file = new AudioFileSourceSPIFFS("/7.mp3");
        break;
     case 8 : file = new AudioFileSourceSPIFFS("/8.mp3");
        break;
     case 9 : file = new AudioFileSourceSPIFFS("/9.mp3");
        break; 
     case 10 : file = new AudioFileSourceSPIFFS("/10.mp3");
        break;        
     case 11 : file = new AudioFileSourceSPIFFS("/11.mp3");
        break;
   }

  out = new AudioOutputI2SNoDAC();  
  mp3 = new AudioGeneratorMP3();
  mp3->begin(file, out);
   
}




void loop()
{
  if (mp3->isRunning()) {
    if (!mp3->loop()) mp3->stop();
  } else {
    
    if(proses == 7){
      
    proses =1;
    Serial.printf("MP3 play selesai\n");
    //matikan PTT
    digitalWrite(ptt, LOW);


    
    delay(5000);
    //ulang reset ESP agar fresh terus 
    ESP.reset();
 
    }

   else if ( proses == 1){ //puluhan
    proses=2;
    tts(suhu);
   }
   else if ( proses == 2){ //puluh, belas atau lewat
    proses=3;
     
     if( suhu <200 && suhu >=120 ) file = new AudioFileSourceSPIFFS("/1_1.mp3");
     else if( suhu <120 && suhu >=0 ) file = new AudioFileSourceSPIFFS(NULL);
     else file = new AudioFileSourceSPIFFS("/p.mp3");
    
     out = new AudioOutputI2SNoDAC();  
     mp3 = new AudioGeneratorMP3();
     mp3->begin(file, out);
   }   
   else if ( proses == 3){ //satuan
    proses=4;
    tts(suhu);
   }    
   else if ( proses == 4){ // koma
     proses=5;
     file = new AudioFileSourceSPIFFS("/k.mp3"); 
     out = new AudioOutputI2SNoDAC();  
     mp3 = new AudioGeneratorMP3();
     mp3->begin(file, out);
   }  
   else if ( proses == 5){ //desimal
    proses=6;
    tts(suhu);
   } 

   else if ( proses == 6){ //derajat
     proses=7;
     file = new AudioFileSourceSPIFFS("/d.mp3");
     out = new AudioOutputI2SNoDAC();  
     mp3 = new AudioGeneratorMP3();
     mp3->begin(file, out);
   }
     
  }
}

Dan jika kalian sukses dan beruntung (karena setiap kali compile hasilnya bisa error mulu, jadi kesabaran adalah kunci), akan kira-kira seperti video dibawah ini :








Share:

Senin, 13 September 2021

ESP8266 - @Telegram_Bot - Part 4 : Kirim Grafik Real Time ke Bot

 


Seperti yang sudah kita praktekkan sebelumnya, Modul ESP8266 sekelas Wemos D1 memiliki kemampuan untuk berkomunikasi dengan API Telegram BOT secara pooling. Namun dikarenakan limitasi dari resource dan syarat dari API yang menggunakan SSL terenkripsi maka akan menyebabkan lambatnya respon yg didapat. Untuk itu butuh perantara di mesin yg lebih cepat seperti raspberry pi atau komputasi cloud yg dapat melayani kebutuhan kapan saja. 

Setelah berhasil menyimpan data dan menggambarkan grafiknya, kita sampai pada pembahasan terakhir yaitu mengirimkan grafik realtime melalui bot telegram. Secara garis besar alur cerita dari logika yg saya buat seperti berikut :


  • ESP8266 membaca sensor DHT 11 dan mengirimkan secara PUB ke MQTT Broker.
  • MQTT broker meneruskan data ke pihak yg melakukan SUB, dalam hal ini PC+python di rumah saya.
  • Python melakukan penyimpanan data suhu dan kelembaban ke SQLite secara berkala setiap ada pesan MQTT yg sampai ke PC.
  • Python mengontrol komunikasi ke API telegram bot dan kemudian mengirimkan grafik jika diminta.


Library atau modul python yg digunakan untuk mengotrol telegram bot adalah python-telegram-bot, jangan lupa untuk meginstalnya di komputer kamu.


$ pip install python-telegram-bot 


Sedangkan module lainnya sudah dibahas pada praktek sebelumnya, tanpa berpanjang lebar lagi, berikut ini script python sebagai jembatan ESP8266 dengan telegram bot


from telegram import Bot, ReplyKeyboardMarkup, ReplyKeyboardRemove
from telegram.ext import Updater, MessageHandler, Filters
import random, json, sqlite3, datetime, time
from paho.mqtt import client as mqtt_client
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from dateutil import parser
from matplotlib import style

# server/broker mqtt
broker = 'broker.hivemq.com'
port = 1883
topicdht= "/aisi555/dht" # sesuaikan topic di praktek sebelumnya


# client id random karena gak boleh sama antar client
client_id = f'python-mqtt-{random.randint(0, 100)}'
username = ''
password = ''

bot = Bot('XXXXXXXX:XXXXXXXXXXXXXXXXXX') #isi sesuai Token BOT anda


#Masukkan ke database dan tambahkan kolom date
def sql_insert(s,h):
db = sqlite3.connect("humitemp.db") #buat dulu databasenya
cursor = db.cursor()
now = int(time.time())
tgl = str(datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S'))
cursor.execute("insert into dht11(suhu, humi, date) values(?,?,?)",(s,h,tgl))
db.commit()
cursor.close()
db.close()

def graph_data(uid):
# Connect to database
sqlite_file = 'humitemp.db'
conn = sqlite3.connect(sqlite_file)
c = conn.cursor()
style.use('seaborn')
    #pilih 30 data terakhir
c.execute('SELECT * FROM dht11 ORDER BY date DESC LIMIT 30')
data = c.fetchall()

temperature = []
humidity = []
timenow = []

for row in data:
temperature.append(row[0])
humidity.append(row[1])
timenow.append(parser.parse(row[2]))

dates = [mdates.date2num(t) for t in timenow]

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.set_title("Kondisi Lingkungan")

# Configure x-ticks
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m %H:%M'))

# Plot temperature data on left Y axis
ax1.set_ylabel("Suhu [°C]")
ax1.plot_date(dates, temperature, '-', label="Suhu", color='r')

# Plot humidity data on right Y axis
ax2 = ax1.twinx()
ax2.set_ylabel("Kelembaban [% RH]")
ax2.plot(dates, humidity, '-', label="Kelembaban", color='g')

# Format the x-axis for dates (label formatting, rotation)
fig.autofmt_xdate(rotation=60)
fig.tight_layout()

# Show grids and legends
ax1.grid(True)
ax1.legend(loc='center left', framealpha=0.5)
ax2.legend(loc='center right', framealpha=0.5)

plt.savefig("figure.png") #simpan dalam file gambar

c.close()
conn.close()
bot.send_photo(uid, photo=open('figure.png', 'rb')) #kirim ke bot telegram berupa gambar

def reply(uid, teks, tombolList): #biar tampil tombol bot keren
if len(tombolList) == 0:
tombol = ReplyKeyboardRemove()
else:
tombol = ReplyKeyboardMarkup([tombolList], resize_keyboard=True)
bot.send_message(uid, parse_mode="HTML", text='<b>' + teks + '</b>', reply_markup=tombol)


def respond(data, update): #membaca dan membalas respon dari pengguna bot
message = data.message
teks = message.text
uid = message.chat.id # user id

if teks == 'suhu':
bot.send_message(uid, parse_mode="HTML", text="<b>Suhu</b> : " + str(suhu) + " °C")
elif teks == 'humi':
bot.send_message(uid, parse_mode="HTML", text="<b>Kelembaban</b> : " + str(humi) + " % rH")
elif teks == 'grafik':
graph_data(uid) #kirim grafik

if teks.find('suhu') != -1 or teks.find('humi') != -1 or teks.find('/start') != -1:
reply(uid, "ESP8266 ULO by Aisi555.com", ["suhu", "humi", "grafik"])
else:
reply(uid, "Silahkan Pilih Tombol", ["suhu", "humi", "grafik"])
return "ok"


# koneksi Mqtt
def connect_mqtt() -> mqtt_client:
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)

client = mqtt_client.Client(client_id)
client.username_pw_set(username, password)
client.on_connect = on_connect
client.connect(broker, port)
return client


# subscribe ke topik mqtt broker
def subscribe(client: mqtt_client):
def on_message(client, userdata, msg):
global suhu, humi, indeks
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
if (msg.topic == topicdht):
data= json.loads(msg.payload.decode()) #parsing json
suhu = data["suhu"]
humi = data["humi"]
sql_insert(suhu,humi) #kirim ke SQLite
# daftar pada topic
client.subscribe(topicdht)
client.on_message = on_message


def run():
global klien
klien = connect_mqtt()
subscribe(klien)
klien.loop_forever()


if __name__ == '__main__':
updater = Updater(bot=bot)
dispatcher = updater.dispatcher

dispatcher.add_handler(MessageHandler(Filters.text, respond))

print("@" + bot.username + " siap.")
updater.start_polling()
run()
updater.idle()


Berikut ini penjelasan secara detail, dimulai dari Module Python yg digunakan :


from telegram import Bot, ReplyKeyboardMarkup, ReplyKeyboardRemove
from telegram.ext import Updater, MessageHandler, Filters
import random, json, sqlite3, datetime, time
from paho.mqtt import client as mqtt_client
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from dateutil import parser
from matplotlib import style


Yang dibawah ini merupakan inilisalisasi broker MQTT dan token BOT Telegram.


# server/broker mqtt
broker = 'broker.hivemq.com'
port = 1883
topicdht= "/aisi555/dht" # sesuaikan topic di praktek sebelumnya


# client id random karena gak boleh sama antar client
client_id = f'python-mqtt-{random.randint(0, 100)}'
username = ''
password = ''

bot = Bot('XXXXXXXX:XXXXXXXXXXXXXXXXXX') #isi sesuai Token BOT anda


Selanjutnya routine / function untuk mengatur koneksi MQTT dan Subcribe topiknya. Setiap ada message yg masuk dari MQTT langsung disimpan pada database SQLite.


# koneksi Mqtt
def connect_mqtt() -> mqtt_client:
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)

client = mqtt_client.Client(client_id)
client.username_pw_set(username, password)
client.on_connect = on_connect
client.connect(broker, port)
return client


# subscribe ke topik mqtt broker
def subscribe(client: mqtt_client):
def on_message(client, userdata, msg):
global suhu, humi, indeks
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
if (msg.topic == topicdht):
data= json.loads(msg.payload.decode()) #parsing json
suhu = data["suhu"]
humi = data["humi"]
sql_insert(suhu,humi) #kirim ke SQLite
# daftar pada topic
client.subscribe(topicdht)
client.on_message = on_message


Setelah data tersedia dari broker MQTT, maka dilakukan penyimpanan database oleh function berikut :


def sql_insert(s,h):
db = sqlite3.connect("humitemp.db") #buat dulu databasenya di SQLite
cursor = db.cursor()
now = int(time.time())
tgl = str(datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S'))
cursor.execute("insert into dht11(suhu, humi, date) values(?,?,?)",(s,h,tgl))
db.commit()
cursor.close()
db.close()


Respon dari Telegram Bot dilakukan di function berikut ini :


def respond(data, update): #membaca dan membalas respon dari pengguna bot
message = data.message
teks = message.text
uid = message.chat.id # user id

if teks == 'suhu':
bot.send_message(uid, parse_mode="HTML", text="<b>Suhu</b> : " + str(suhu) + " °C")
elif teks == 'humi':
bot.send_message(uid, parse_mode="HTML", text="<b>Kelembaban</b> : " + str(humi) + " % rH")
elif teks == 'grafik':
graph_data(uid) #kirim grafik

if teks.find('suhu') != -1 or teks.find('humi') != -1 or teks.find('/start') != -1:
reply(uid, "ESP8266 ULO by Aisi555.com", ["suhu", "humi", "grafik"])
else:
reply(uid, "Silahkan Pilih Tombol", ["suhu", "humi", "grafik"])
return "ok"


Biar tambah keren ditambahkan reply keyboard "markup" seperti berikut :


def reply(uid, teks, tombolList): #biar tampil tombol bot keren
if len(tombolList) == 0:
tombol = ReplyKeyboardRemove()
else:
tombol = ReplyKeyboardMarkup([tombolList], resize_keyboard=True)
bot.send_message(uid, parse_mode="HTML", text='<b>' + teks + '</b>', reply_markup=tombol)




Untuk menampilkan grafik digunakan module Matplotlib, persis dengan pembahasan sebelumnya.


def graph_data(uid):
# Connect to database
sqlite_file = 'humitemp.db'
conn = sqlite3.connect(sqlite_file)
c = conn.cursor()
style.use('seaborn')
    #pilih 30 data terakhir
c.execute('SELECT * FROM dht11 ORDER BY date DESC LIMIT 30')
data = c.fetchall()

temperature = []
humidity = []
timenow = []

for row in data:
temperature.append(row[0])
humidity.append(row[1])
timenow.append(parser.parse(row[2]))

dates = [mdates.date2num(t) for t in timenow]

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.set_title("Kondisi Lingkungan")

# Configure x-ticks
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m %H:%M'))

# Plot temperature data on left Y axis
ax1.set_ylabel("Suhu [°C]")
ax1.plot_date(dates, temperature, '-', label="Suhu", color='r')

# Plot humidity data on right Y axis
ax2 = ax1.twinx()
ax2.set_ylabel("Kelembaban [% RH]")
ax2.plot(dates, humidity, '-', label="Kelembaban", color='g')

# Format the x-axis for dates (label formatting, rotation)
fig.autofmt_xdate(rotation=60)
fig.tight_layout()

# Show grids and legends
ax1.grid(True)
ax1.legend(loc='center left', framealpha=0.5)
ax2.legend(loc='center right', framealpha=0.5)

plt.savefig("figure.png") #simpan dalam file gambar

c.close()
conn.close()
bot.send_photo(uid, photo=open('figure.png', 'rb')) #kirim ke bot telegram berupa gambar


Hasil pada BOT Telegram seperti berikut ini :




Grafik lebih jelasnya seperti pada gambar berikut :




Keren bukan ? Selamat mencoba ! 

Share:

Kontak Penulis



12179018.png (60×60)
+6281331339072

Mail : ahocool@gmail.com

Site View

Categories

555 (8) 7 segmen (3) adc (4) amplifier (2) analog (19) android (14) antares (11) arduino (29) artikel (11) attiny (3) attiny2313 (19) audio (8) baterai (5) blog (1) bluetooth (1) chatgpt (2) cmos (2) crypto (2) dasar (46) digital (11) dimmer (5) display (3) esp32 (3) esp8266 (30) euro2020 (13) gcc (1) gis (3) gsm (1) iklan (1) infrared (2) Input Output (3) iot (78) 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 (2) mikrokontroller (14) mikrotik (5) modbus (9) mqtt (3) ninmedia (5) ntp (1) openwrt (3) paket belajar (19) palang pintu otomatis (1) parabola (88) pcb (2) power (1) praktek (2) project (37) proyek (1) python (11) radio (31) raspberry pi (9) remote (1) revisi (1) rfid (1) robot (1) rpm (2) rs232 (1) script break down (3) sdcard (3) SDR (3) sensor (2) sharing (3) signage (1) sinyal (1) sms (6) software (18) solar (1) solusi (1) statistik (5) tachometer (2) technology (1) teknologi (2) telegram (2) telepon (9) televisi (167) television (28) telkomiot (5) transistor (2) troubleshoot (3) tulisan (96) tutorial (108) tv digital (6) tvri (2) unesa (8) vu meter (2) vumeter (2) wav player (3) wayang (1) wifi (6) yolo (9)

Arsip Blog

Diskusi


kaskus
Forum Hobby Elektronika