Sinkronisasi Waktu Dengan Network Time Protocol (NTP) pada Mikrokontroler ESP32

NTP (Network Time Protocol) adalah protokol komunikasi di jaringan komputer untuk sinkronisasi waktu melalui jaringan komputer. Sinkronisasi waktu ini penting pada aplikasi-aplikasi yang berbasis pewaktuan yang tepat, misalnya pencatatan ataupun untuk koordinasi aktivitas di suatu sistem berbasis IoT (Internet of Things).

ESP32 adalah mikrokontroler yang memiliki fitur Wi-Fi yang memungkinkan untuk tersambung ke jaringan komputer, termasuk ke suatu server NTP. ESP32 dapat menggunakan protokol NTP untuk sinkronisasi waktu dengan server NTP. Proses ini menyederhanakan kalibrasi waktu di ESP32. Alternatif lainnya adalah menggunakan RTC (real time clock) yaitu jam digital yang dibackup dengan baterai, ataupun dengan setting waktu secara manual. Teknik RTC perlu komponen tambahan, sedangkan setting manual memerlukan waktu dan tenaga. Selain itu sumber waktu di ESP32 juga tidak sepresisi sumber waktu di server NTP, sehingga setelah beberapa waktu maka jam di ESP32 akan bergeser dari setting awal.

Untuk mengakses NTP dari ESP32 dapat menggunakan library yang sudah ada, jadi tidak perlu lagi koding protokol secara dari nol. Library yang umum dipakai ada 2 macam yaitu:

  • Arduino Library NTPClient untuk Arduino. Library ini dapat dipakai di semua mikrokontroler Arduino.
  • Arduino ESP32 NTP untuk ESP32. Library ini hanya dapat dipakai untuk ESP32.

Persiapan

Sebelum menjalankan program NTP ini, ada beberapa hal yang perlu diperhatikan:

  • Arduino IDE sudah diinstal di desktop/laptop, dan libraray Arduino-ESP32 sudah diaktifkan
  • Konfigurasi ESP32 sudah diujicoba dengan program sederhana, misalnya lampu kedip (Blink)
  • Wi-Fi akses point dapat mengakses internet tersedia, dan juga sudah disiapkan nama akses point dan passwordnya.
  • Pahami zona waktu tempat anda. NTP memberikan waktu dengan standar GMT+0, jadi perlu disesuaikan jika anda memerlukan waktu lokal.

Arduino NTPClient

Cara pertama adalah menggunakan library NTPClient Arduino. Library ini dicantumkan di laman NTPClient di situs Arduino, namun tidak ada penjelasan sama sekali di situs tersebut. Untuk cara penggunaannya kita perlu melihat repository NTPClient di github. Contoh penggunaan ada di direktori /examples di repository tersebut.

Contoh kode juga dapat diakses dari Arduino IDE, dari menu File -> Examples -> NTPClient. Namun sedikit repot, karena daftar Examples cukup banyak dan urutannya tidak tepat abjad. Berikut ini tampilan menunya di Arduino IDE. (klik untuk gambar yang lebih besar).

Kode yang mudah dipakai adalah yang ‘Basic’ , namun kode ini perlu diubah sedikit seperti pada listing kode berikut ini.

#include <NTPClient.h>
// change next line to use with another board/shield
//#include <ESP8266WiFi.h>
#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid = "AP_NAME";
const char *password = "AP_PASSWORD";

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  timeClient.begin();
}
void loop() {
  timeClient.update();
  Serial.println(timeClient.getFormattedTime());
  delay(1000);
}

Perubahan yang dilakukan adalah sebagai berikut:

  • Kode ini awalnya untuk ESP8266. Untuk dipakai di ESP32 maka header file mesti diganti, dari “ESP8266WiFi.h” menjadi “WiFi.h”
  • Sesuaikan nama SSID untuk akses point
  • Sesuaikan password untuk mengakses akses point. Perhatikan bahwa program NTP ini hanya dapat berfungsi jika ESP32 tersambung ke internet. Jadi pastikan Wi-Fi access point tersambung ke internet atau bisa juga menggunakan tethering ke smartphone

Berikut ini tampilan output ESP32 di serial monitor Arduino. Waktu adalah GMT+0, sedangkan di Indonesia adalah GMT+7, sehingga jam yang ditampilkan berbeda 7 jam.

Program di atas menampilkan waktu dalam UTC atau GMT+0. Untuk menampilkan dalam waktu lokal seperti WIB (Waktu Indonesia bagian Barat), gunakan fungsi setTimeOffset() untuk menambah/mengurangi waktu yang ditampilkan. Untuk WIB, waktu yang ditambah dalam detik adalah 7 x 60 x 60. Programnya menjadi sebagai berikut:

#include <NTPClient.h>
// change next line to use with another board/shield
//#include <ESP8266WiFi.h>
#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid = "AP_NAME";
const char *password = "AP_PASSWORD";

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

void setup() {
  Serial.begin(115200); 
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  timeClient.begin();
  timeClient.setTimeOffset(7*60*60);
}
void loop() {
  timeClient.update();
  Serial.println(timeClient.getFormattedTime());
  delay(1000);
}

Beberapa hal yang dapat diambil/diubah dengan member function di di class NTPClient :

  • SetPoolServerName() : mengubah NTP server dari default pool.ntp.org
  • isTimeSet() : apakah berhasil mengambil waktu dari NTP server
  • getDay() : ambil hari dalam minggu (0 = minggu)
  • getHours() : ambil waktu jam
  • getMinutes() : ambil waktu menit
  • getSeconds() : ambil waktu detik
  • setTimeOffset() : menambah/mengurangi offset waktu yang ditampilkan dalam detik
  • getFormattedTime() : waktu terformat dalam bentuk string
  • getEpochTime() : jumlah detik sejak 1 Januari 1970

Arduino ESP32 NTP Library

Library ini adalah bagian dari Arduino ESP32, jadi hanya dapat dipakai di ESP32.

Fungsi-fungsi penting adalah:

  • getLocalTime() untuk mendapatkan waktu
  • configTime() untuk setting NTP
  • configTzTime() untuk set timezone

Contoh kode diambil dari contoh program SimpleTime.ino di repository arduino-esp32 .

Daftar timezone yang dapat dipakai dapat dilihat di https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h

Berikut ini adalah kode yang sudah dimodifikasi:

// sumber: libraries/ESP32/examples/Time/SimpleTime/SimpleTime.ino
// URL: https://github.com/espressif/arduino-esp32/blob/496b8411773243e1ad88a68652d6982ba2366d6b/libraries/ESP32/examples/Time/SimpleTime/SimpleTime.ino
// dimodifikasi untuk WIB (GMT+7)

#include <WiFi.h>
#include "time.h"
#include "esp_sntp.h"

const char *ssid = "AP_NAME";
const char *password = "AP_PASSWORD";

const char *ntpServer1 = "pool.ntp.org";
const char *ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;

//const char *time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)
const char *time_zone = "WIB-7";  // Indonesia WIB

void printLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("No time available (yet)");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

// Callback function (gets called when time adjusts via NTP)
void timeavailable(struct timeval *t) {
  Serial.println("Got time adjustment from NTP!");
  printLocalTime();
}

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

  // First step is to configure WiFi STA and connect in order to get the current time and date.
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);

  /**
   * NTP server address could be acquired via DHCP,
   *
   * NOTE: This call should be made BEFORE esp32 acquires IP address via DHCP,
   * otherwise SNTP option 42 would be rejected by default.
   * NOTE: configTime() function call if made AFTER DHCP-client run
   * will OVERRIDE acquired NTP server address
   */
  esp_sntp_servermode_dhcp(1);  // (optional)

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

  // set notification call-back function
  sntp_set_time_sync_notification_cb(timeavailable);

  /**
   * This will set configured ntp servers and constant TimeZone/daylightOffset
   * should be OK if your time zone does not need to adjust daylightOffset twice a year,
   * in such a case time adjustment won't be handled automagically.
   */
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);

  /**
   * A more convenient approach to handle TimeZones with daylightOffset
   * would be to specify a environment variable with TimeZone definition including daylight adjustmnet rules.
   * A list of rules for your zone could be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
   */
  configTzTime(time_zone, ntpServer1, ntpServer2);
}

void loop() {
  delay(5000);
  printLocalTime();  // it will take some time to sync time :)
}

Penyesuaian yang dilakukan adalah sebagai berikut:

  • Timezone diganti menjadi Waktu Indonesia bagian Barat (WIB-7)

Berikut ini output program di serial terminal

Kedua library tersebut fungsinya mirip-mirip. Library ESP32 memiliki fitur lebih banyak dan lebih cocok untuk dijalankan di ESP32.

Saran & Penyempurnaan

Contoh program NTP yang disajikan adalah program yang sangat minimalis untuk menunjukkan konsep NTP. Pada kondisi sesungguhnya, sistem mikrokontroler IoT ini akan menghadapi berbagai masalah, sehingga perlu ditambahkan penanganan beberapa kasus berikut:

  • Koneksi ESP32 ke server NTP tidak stabil, baik di local network maupun di internetnya.
  • Pada program perlu ditambahkan error handling jika terjadi koneksi internet putus ataupun koneksi Wi-Fi putus. Jika koneksi Wi-Fi putus, maka perlu ada prosedur untuk melakukan rekoneksi ulang ke akses point Wi-Fi. Jadi tidak perlu reset manual.
  • Pengubahan konfigurasi nama akses point dan password akses point secara software dengan interface tertentu. Pada contoh ini nama dan password dikodekan langsung di source code sehingga tidak dapat diubah. Pada kondisi nyata seharusnya dapat diubah oleh pemakai tanpa perlu mengubah source code.

Literatur

Frekuensi DAC MCP4725 pada ESP32 C3

Pengukuran Frekuensi DAC MCP4725 pada ESP32

Foto perangkat

Kode Pengujian


#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include <Wire.h>

#define I2C_SDA 15
#define I2C_SCL 13

Adafruit_MCP4725 dac;

void setup(void) {
  int address = 0;
  int hasil;
  uint32_t speed = 400000L;
  int status;
  status = Wire.begin(I2C_SDA, I2C_SCL, speed);
  Serial.begin(115200);
  Serial.print("Hello! Wire Init status:");
  Serial.println(status);
  Serial.println(speed);

  address = 0x62;
  dac.begin(address);
  Serial.println("Search I2C");
  SearchI2C();
  Serial.println("benchmark");
}
int start_sawtooth = 0;
int current_time;
int duration;
int time_begin;
int time_now;
uint32_t counter;
void loop(void) {
  dac.setVoltage(0, false);
  dac.setVoltage(4095, false);
  counter = counter + 1;
  if (counter >= 10000) {
    float period;
    int time_now = millis();
    counter = 0;
    duration = time_now - time_begin;
    period = duration / 10000.0 / 2;
    Serial.print("period (ms): ");
    Serial.println(period);
    //  prepare next round
    time_begin = time_now;
  }
}

void SearchI2C() {
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for (address = 1; address < 127; address++) {
    //    Serial.println(address, HEX);
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    } else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
}

Pengukuran

Hasil pengukuran perioda

benchmark
period (ms): 0.18
period (ms): 0.17
period (ms): 0.17

Hasil pengukuran sinyal output DAC

Frekuensi DAC adalah 2,867 kHz x 2 = 5,734 kHz

Kesimpulan

Perioda : 0.17 ms

Frekuensi: 5,734 kHz

Referensi

Perancangan dan Implementasi Low Pass Filter Digital Dengan ESP32

Perancangan Filter

Perancangan filter menggunakan tools dari https://fiiir.com/

Parameter yang digunakan:

  • Sampling rate: 20000 Hz
  • Cut off frequency 800 Hz
  • Transition bandwidth 1800 Hz
  • Window type : Blackman

Berikut ini karakteristik filter yang dapat dilihat di laman yang sama

Kode dalam bahasa Python untuk menghitung parameter filter dan konvolusinya juga dihasilkan dari laman yang sama, sebagai berikut:

from __future__ import print_function
from __future__ import division

import numpy as np

# Example code, computes the coefficients of a low-pass windowed-sinc filter.

# Configuration.
fS = 20000 # Sampling rate.
fL = 800 # Cutoff frequency.
N = 53 # Filter length, must be odd.

# Compute sinc filter.
h = np.sinc(2 * fL / fS * (np.arange(N) - (N - 1) / 2))

# Apply window.
h *= np.blackman(N)

# Normalize to get unity gain.
h /= np.sum(h)

print(h)

# Applying the filter to a signal s can be as simple as writing
# s = np.convolve(s, h)

Parameter filter yang dihasilkan adalah sebagai berikut  (sebagai Python array)

[-4.29317419e-20 -4.18549678e-21 -1.79819385e-05 -8.38178513e-05
 -2.28616892e-04 -4.79528638e-04 -8.51847897e-04 -1.33864303e-03
 -1.89910369e-03 -2.44776137e-03 -2.84740649e-03 -2.90866942e-03
 -2.39870072e-03 -1.06016948e-03  1.35995551e-03  5.07437980e-03
  1.02166209e-02  1.68050560e-02  2.47160945e-02  3.36720917e-02
  4.32479500e-02  5.28978134e-02  6.20002342e-02  6.99171799e-02
  7.60597872e-02  7.99523091e-02  8.12855501e-02  7.99523091e-02
  7.60597872e-02  6.99171799e-02  6.20002342e-02  5.28978134e-02
  4.32479500e-02  3.36720917e-02  2.47160945e-02  1.68050560e-02
  1.02166209e-02  5.07437980e-03  1.35995551e-03 -1.06016948e-03
 -2.39870072e-03 -2.90866942e-03 -2.84740649e-03 -2.44776137e-03
 -1.89910369e-03 -1.33864303e-03 -8.51847897e-04 -4.79528638e-04
 -2.28616892e-04 -8.38178513e-05 -1.79819385e-05 -4.18549678e-21
 -4.29317419e-20]

Simulasi dengan Python di Desktop

Berikut ini kode untuk simulasi filter tersebut di Jupyter Notebook dengan bahasa Python.

from __future__ import print_function
from __future__ import division

import numpy as np
import math
import matplotlib.pyplot as plt

# Example code, computes the coefficients of a low-pass windowed-sinc filter.

# Configuration.
fS = 20000 # Sampling rate.
fL = 800 # Cutoff frequency.
N = 53 # Filter length, must be odd.

# Compute sinc filter.
h = np.sinc(2 * fL / fS * (np.arange(N) - (N - 1) / 2))

# Apply window.
h *= np.blackman(N)

# Normalize to get unity gain.
h /= np.sum(h)

print(h)

# Applying the filter to a signal s can be as simple as writing
# s = np.convolve(s, h)

x = np.linspace(0,1, 20001)

# filter digital
signal_frequency=8000 # diubah-ubah sesuai keperluan
y=np.sin(x*2*math.pi * signal_frequency)
# Applying the filter to a signal s can be as simple as writing
y_out = np.convolve(y, h)
plt.figure(figsize=(16,6))
plt.plot(x, y_out[0:20001],label="output")
plt.plot(x, y[0:20001],label="input")
plt.title("signal:{} Hz".format(signal_frequency))
plt.legend()
plt.axis('tight')
plt.xlim([0, 0.01])
plt.savefig("simulasi-signal-{}.jpg".format(signal_frequency))

Hasil simulasi untuk frekuensi 8 Hz

Hasil simulasi untuk frekuensi 80 HzHasil simulasi untuk frekuensi 800 Hz

Hasil simulasi untuk frekuensi 8000 Hz

Simulasi dengan C di Desktop

Pada percobaan ini filter disimulasikan di Desktop PC untuk mengecek ketepatan perhitungan dibandingkan dengan versi Python.

Kode untuk simulasi C sebagai berikut:

#include <stdio.h>
#include <stdlib.h>
#include "math.h"

int main()
{

#define ORDE_FILTER 53
int i,j,k;
FILE * fp;
char filename[100];
printf("Hello world!\n");
// simulasi 1 detik saja

float signal_frequency=8; // frekuensi input signal
float filter_coefficients[]= {-4.29317419e-20, -4.18549678e-21, -1.79819385e-05, -8.38178513e-05,
-2.28616892e-04, -4.79528638e-04, -8.51847897e-04, -1.33864303e-03,
-1.89910369e-03, -2.44776137e-03, -2.84740649e-03, -2.90866942e-03,
-2.39870072e-03, -1.06016948e-03, 1.35995551e-03, 5.07437980e-03,
1.02166209e-02, 1.68050560e-02, 2.47160945e-02, 3.36720917e-02,
4.32479500e-02, 5.28978134e-02, 6.20002342e-02, 6.99171799e-02,
7.60597872e-02, 7.99523091e-02, 8.12855501e-02, 7.99523091e-02,
7.60597872e-02, 6.99171799e-02, 6.20002342e-02, 5.28978134e-02,
4.32479500e-02, 3.36720917e-02, 2.47160945e-02, 1.68050560e-02,
1.02166209e-02, 5.07437980e-03, 1.35995551e-03, -1.06016948e-03,
-2.39870072e-03, -2.90866942e-03, -2.84740649e-03, -2.44776137e-03,
-1.89910369e-03, -1.33864303e-03, -8.51847897e-04, -4.79528638e-04,
-2.28616892e-04, -8.38178513e-05, -1.79819385e-05, -4.18549678e-21,
-4.29317419e-20
};
int orde_filter=ORDE_FILTER;
float y_history[ORDE_FILTER];
signal_frequency=8000;
sprintf(filename,"simulasi-%d.csv",(int)signal_frequency);
fp = fopen (filename, "w");
fprintf(fp,"time,y,y_out\n");

// zeros y_history
for (i=0; i<orde_filter; i++)
{
y_history[i]=0;
}
for (i=0; i<20000; i++)
{
printf(".");
float t,y;
t=(float)i/(float)20000; // time axis
y=sin(2 * M_PI *signal_frequency* t);
// fprintf(fp,"%f,%f\n",t,y);

// hitung history signal
for (j=orde_filter-1; j>=1; j--)
{
int src=j-1;
int dst=j;
y_history[dst]=y_history[src];
// printf("%d %d\n",src,dst);
}
y_history[0]=y;

// hitung konvolusi
float y_out=0;
for (k=0; k<orde_filter; k++)
{
// fprintf(fp,"%5.3f ",y_history[k]);
y_out=y_out+y_history[k]*filter_coefficients[k];
}
// fprintf(fp,"\n");
fprintf(fp,"%f,%f,%f\n",t,y,y_out);
}
fclose(fp);
return 0;
}



// https://www.tutorialspoint.com/c_standard_library/c_function_fprintf.htm

Output kode C tersebut berupa file *.csv

Visualisasi data di file *.csv dilakukan menggunakan Jupyter Notebook dengan kode sebagai berikut:

import pandas as pd
import math
import matplotlib.pyplot as plt

name="simulasi-8"
name="simulasi-80"
name="simulasi-800"
name="simulasi-8000"
df = pd.read_csv('../filter-simulasi-c/{}.csv'.format(name))

plt.figure(figsize=(16,6))
plt.plot(df["time"], df["y"],marker='o',label="input")
plt.plot(df["time"], df["y_out"],marker='o',label="output")
plt.legend()
plt.axis('tight')
plt.xlim([0, 0.001])
plt.savefig("visualisasi-{}.jpg".format(name))

Tampilan Visualisasi untuk sinyal 8 Hz

nampak sinyal 8 Hz tidak teredam

Tampilan Visualisasi untuk sinyal 80 Hz

sinyal 80 Hz nampak teredam sedikit sekali dan ada lag

Tampilan Visualisasi untuk sinyal 800 Hz

nampak sinyal 800 Hz teredam sekitar 50%

Tampilan Visualisasi untuk sinyal 8000 Hz

nampak sinyal 8000 Hz teredam hampir semuanya

Simulasi dengan C di ESP32

pada percobaan ini kode dijalankan di ESP32 namun input adalah simulasi bukan dari generator sinyal. Output direkam ke port serial, bukan ke osiloskop.

Kode ESP32 adalah sebagai berikut

#define ORDE_FILTER 53

void setup() {

  Serial.begin(115200);

  //

  int i, j, k;

  char filename[100];

  // simulasi 1 detik saja

  float signal_frequency = 8;  // frekuensi input signal

  float filter_coefficients[] = { -4.29317419e-20, -4.18549678e-21, -1.79819385e-05, -8.38178513e-05,

                                  -2.28616892e-04, -4.79528638e-04, -8.51847897e-04, -1.33864303e-03,

                                  -1.89910369e-03, -2.44776137e-03, -2.84740649e-03, -2.90866942e-03,

                                  -2.39870072e-03, -1.06016948e-03, 1.35995551e-03, 5.07437980e-03,

                                  1.02166209e-02, 1.68050560e-02, 2.47160945e-02, 3.36720917e-02,

                                  4.32479500e-02, 5.28978134e-02, 6.20002342e-02, 6.99171799e-02,

                                  7.60597872e-02, 7.99523091e-02, 8.12855501e-02, 7.99523091e-02,

                                  7.60597872e-02, 6.99171799e-02, 6.20002342e-02, 5.28978134e-02,

                                  4.32479500e-02, 3.36720917e-02, 2.47160945e-02, 1.68050560e-02,

                                  1.02166209e-02, 5.07437980e-03, 1.35995551e-03, -1.06016948e-03,

                                  -2.39870072e-03, -2.90866942e-03, -2.84740649e-03, -2.44776137e-03,

                                  -1.89910369e-03, -1.33864303e-03, -8.51847897e-04, -4.79528638e-04,

                                  -2.28616892e-04, -8.38178513e-05, -1.79819385e-05, -4.18549678e-21,

                                  -4.29317419e-20 };

  int orde_filter = ORDE_FILTER;

  float y_history[ORDE_FILTER];

  signal_frequency = 800;

//  signal_frequency = 80;

//  signal_frequency = 8;

  sprintf(filename, "simulasi-%d.csv", (int)signal_frequency);

  printf("time,y,y_out\n");

  // zeros y_history

  for (i = 0; i < orde_filter; i++) {

    y_history[i] = 0;

  }

  for (i = 0; i < 20000; i++) {

    float t, y;

    t = (float)i / (float)20000;  //  time axis

    y = sin(2 * M_PI * signal_frequency * t);

    //        fprintf(fp,"%f,%f\n",t,y);

    // hitung history signal

    for (j = orde_filter - 1; j >= 1; j--) {

      int src = j - 1;

      int dst = j;

      y_history[dst] = y_history[src];

      //            printf("%d %d\n",src,dst);

    }
  y_history[0] = y;
    // hitung konvolusi
    float y_out = 0;
    for (k = 0; k < orde_filter; k++) {
      y_out = y_out + y_history[k] * filter_coefficients[k];
    }

    //        fprintf(fp,"\n");

    printf("%f,%f,%f\n", t, y, y_out);

  }

}

void loop() {

  // put your main code here, to run repeatedly:

}

Output dari ESP32 direkam dengan software RealTerm ke sebuah file *.txt

File *.txt tersebut dirapikan dan disimpan sebagai *.csv

File *.csv ini kemudian divisualisasikan dengan Jupyter Notebook

Kode visualisasi sebagai berikut

# graph simulasi C filter
import pandas as pd
import math
import matplotlib.pyplot as plt

name="esp32-8.csv"
name="esp32-80.csv"
#name="esp32-800.csv"
#name="esp32-8000.csv"
df = pd.read_csv('{}'.format(name))

plt.figure(figsize=(16,6))
plt.plot(df["time"], df["y"],label="input")
plt.plot(df["time"], df["y_out"],label="output")
plt.legend()
plt.axis('tight')
plt.xlim([0, 0.1])
plt.title(name)
plt.savefig("visualisasi-{}.jpg".format(name))

Sinyal 8 Hz:

Sinyal 80 Hz

Sinyal 800 Hz

Sinyal 8000 Hz

Kesimpulan: output dari software LPF di ESP32 sama dengan output dari software C di Desktop

Profiling di ESP32

Pada percobaan ini waktu yang diperlukan untuk menghitung filter diukur di ESP32.

Pewaktuan menggunakan fungsi millis(). Fungsi millis() tidak 100% akurat. Untuk lebih tepat perlu interupsi timer, atau dengan osiloskop.

Kode profiling:

#define ORDE_FILTER 53
float buffer[100];
void setup() {
  int buffer_index = 0;
  int durasi;
  Serial.begin(115200);
  //
  long int i;
  int j, k;
  char filename[100];
  // simulasi 1 detik saja

  float signal_frequency = 8;  // frekuensi input signal
  float filter_coefficients[] = { -4.29317419e-20, -4.18549678e-21, -1.79819385e-05, -8.38178513e-05,
                                  -2.28616892e-04, -4.79528638e-04, -8.51847897e-04, -1.33864303e-03,
                                  -1.89910369e-03, -2.44776137e-03, -2.84740649e-03, -2.90866942e-03,
                                  -2.39870072e-03, -1.06016948e-03, 1.35995551e-03, 5.07437980e-03,
                                  1.02166209e-02, 1.68050560e-02, 2.47160945e-02, 3.36720917e-02,
                                  4.32479500e-02, 5.28978134e-02, 6.20002342e-02, 6.99171799e-02,
                                  7.60597872e-02, 7.99523091e-02, 8.12855501e-02, 7.99523091e-02,
                                  7.60597872e-02, 6.99171799e-02, 6.20002342e-02, 5.28978134e-02,
                                  4.32479500e-02, 3.36720917e-02, 2.47160945e-02, 1.68050560e-02,
                                  1.02166209e-02, 5.07437980e-03, 1.35995551e-03, -1.06016948e-03,
                                  -2.39870072e-03, -2.90866942e-03, -2.84740649e-03, -2.44776137e-03,
                                  -1.89910369e-03, -1.33864303e-03, -8.51847897e-04, -4.79528638e-04,
                                  -2.28616892e-04, -8.38178513e-05, -1.79819385e-05, -4.18549678e-21,
                                  -4.29317419e-20 };
  int orde_filter = ORDE_FILTER;
  float y_history[ORDE_FILTER];
  signal_frequency = 800;
  //  signal_frequency = 80;
  //  signal_frequency = 8;
  printf(“time,y,y_out\n”);

  // zeros y_history
  for (i = 0; i < orde_filter; i++) {
    y_history[i] = 0;
  }
  int time_start = millis();

  for (i = 0; i < 1000000L; i++) {
    float t, y;
    t = (float)i / (float)20000;  //  time axis
    y = sin(2 * M_PI * signal_frequency * t);
    //        fprintf(fp,”%f,%f\n”,t,y);

    // hitung history signal
    for (j = orde_filter – 1; j >= 1; j–) {
      int src = j – 1;
      int dst = j;
      y_history[dst] = y_history[src];
      //            printf(“%d %d\n”,src,dst);
    }
    y_history[0] = y;

    // hitung konvolusi
    float y_out = 0;
    for (k = 0; k < orde_filter; k++) {
      y_out = y_out + y_history[k] * filter_coefficients[k];
    }
    //        fprintf(fp,”\n”);
    //    Serial.print(“.”);
    buffer[buffer_index] = y_out;
    buffer_index++;
    if (buffer_index>100){
      buffer_index=0;
    }
  }
  int time_end = millis();
  durasi = time_end – time_start;
  Serial.println(“durasi”);
  Serial.println(durasi);
}

void loop() {
  // put your main code here, to run repeatedly:
}

Hasil profiling: kode memerlukan 16479 ms untuk menjalankan 1000000 kali perhitungan. jadi setiap perhitungan memerlukan 16.479 us

Konversi DAC di ESP32 memerlukan sekitar 20 us (pesimis), jadi jika kode sekuensial maka didapatkan perioda 36.479 us, atau frekuensi = 27413 Hz.

Frekuensi filter yang dirancang adalah 20000 Hz, jadi masih di bawah batas 27413 Hz.

Pengujian Dengan Sinyal Sesungguhnya

Pada percobaan ini kode dijalankan di ESP32 dengan input dari generator sinyal dan output diamati di osiloskop

Berikut ini implementasi rangkaian:

Implementasi filter digital dengan ESP32, MCP3008 dan level converter
Implementasi filter digital dengan ESP32, MCP3008 dan level converter

Implementasi software dapat dilihat di https://github.com/waskita/embedded/tree/master/esp32_mcp3008_baker_adc_dac_filter-timer

Sinual input didapat dari  generator sinyal GW Instek AFG-2012

Pengukuran output menggunakan osiloskop GW Instek-1152A-U

Berikut ini pengukuran pada frekuensi cut off = 100 Hz jauh di bawah frekuensi cut off.

Berikut ini pengukuran pada frekuensi cut off = 800 Hz. Amplitudo sinyal output sekitar 50% sinyal input.

Berikut ini pengukuran pada frekuensi 1600 Hz, jauh di atas cut off. Amplitudo sinyal output kecil sekali hampir tidak ada.

Berikut ini pengukuran pada frekuensi input 19200 Hz. Terjadi aliasing karena pada sistem ini tidak ada filter anti aliasing.

Referensi

Komunikasi Nirkabel untuk Mikrokontroler

Komunikasi Nirkabel (wireless) untuk mikrokontroler/mikroprosesor pada pita frekuensi ISM (Industrial Scientific Medical).

Frekuensi 433 MHz

Modul CC1101

Chip CC1101 ini adalah buatan Texas Instruments. Berikut ini contoh modul wireless yang menggunakan chip CC1101

Modul CC1101
Modul CC1101

Datasheet CC1101: https://www.ti.com/product/CC1101

Modul HC-12

Datasheet HC-12: http://statics3.seeedstudio.com/assets/file/bazaar/product/HC-12_english_datasheets.pdf

Frekuensi 915 MHz

LORA

Modul LORA

Referensi: https://en.wikipedia.org/wiki/LoRa

Frekuensi 2,4 GHz

Protokol WiFi (IEEE 802.11)

Contoh perangkat dengan WiFi yang populer adalah ESP32 dan ESP8266

ESP8266 NodeMCU (kiri), ESP32 DevkitC (tengah), ESP32 Lolin32 Lite (kanan)
ESP8266 NodeMCU (kiri), ESP32 DevkitC (tengah), ESP32 Lolin32 Lite (kanan)

Protokol Bluetooth

Perangkat mikrokontroler dengan protokol bluetooth misalnya adalah ESP32

Protokol lain

Contoh protokol di frekuensi 2,4 GHz lainnya adalah NRF24L01 buatan Nordic Semiconductor

Datasheet: https://www.mouser.com/datasheet/2/297/nRF24L01_Product_Specification_v2_0-9199.pdf

Situs resmi NRF24: https://www.nordicsemi.com/Products/nRF24-series

 

 

Luatos ESP32-C3-Core

Spesifikasi

  • Based on the ESP32-C3 WIFI & Bluetooth LE RISC-V Single-Core CPU
  • Type-C USB
  • Castellated pads
  • 4MB Flash
  • Clock speed: 160 Mhz
  • 15x Digital IO
  • ADC(5 channel, 12-bit), I2C, SPI, UARTx2
  • Size: 21mm x 51mm
  • Default firmware: LuatOS
  • 2 red status LEDs

Pinout

Deskripsi Pin

Numbering

name

Default function after reset

multiplexing function

power domain

Pull-up and pull-down ability

32

GND

grounding

 

 

 

31

5V

5V power interface, connected to VBUS of USB

 

 

 

30

BOOT

GPIO09, input

BOOTMODE

VDD3P3_CPU

UP/DOWN

29

IO08

GPIO08, input, output, high impedance

 

VDD3P3_CPU

UP/DOWN

28

IO04

GPIO04, input, output, high impedance

I2C_SDA/ADC_4

VDD3P3_RTC

UP/DOWN

27

IO05

GPIO05, input, output, high impedance

I2C_SCL/ADC_5

VDD3P3_RTC

UP/DOWN

26

3.3V

Chip power supply, 3.3V

 

 

 

25

GND

grounding

 

 

 

twenty four

PB_11

GPIO11, input, output, high impedance

VDD_SPI

VDD3P3_CPU

UP/DOWN

twenty three

IO07

GPIO07, input, output, high impedance

SPI2_CS

VDD3P3_CPU

UP/DOWN

twenty two

IO06

GPIO06, input, output, high impedance

 

VDD3P3_CPU

UP/DOWN

twenty one

IO10

GPIO10, input, output, high impedance

SPI2_MISO

VDD3P3_CPU

UP/DOWN

20

IO03

GPIO03, input, output, high impedance

SPI2_MOSI/ADC_3

VDD3P3_RTC

UP/DOWN

19

IO02

GPIO02, input, output, high impedance

SPI2_CK/ADC_2

VDD3P3_CPU

UP/DOWN

18

3.3V

Chip power supply, 3.3V

 

 

 

17

GND

grounding

 

 

 

16

5V

5V power interface, connected to VBUS of USB

 

 

 

15

PWB

Chip 3.3V power supply control, high level is effective, can be suspended when not in use

 

 

 

14

GND

grounding

 

 

 

13

3.3V

Chip power supply, 3.3V

 

 

 

12

RESET

chip reset

 

VDD3P3_RTC

 

11

NC

 

 

 

 

10

IO13

GPIO13, input, output, high impedance

 

VDD3P3_CPU

UP/DOWN

09

U0_RX

GPIO20, input, output, high impedance

UART0_RX

VDD3P3_CPU

UP/DOWN

08

U0_TX

GPIO21, input, output, high impedance

UART0_TX

VDD3P3_CPU

UP/DOWN

07

GND

grounding

 

 

 

06

IO19

GPIO19, input, output, high impedance

USB_D+

VDD3P3_CPU

UP/DOWN

05

IO18

GPIO18, input, output, high impedance

USB_D-

VDD3P3_CPU

UP/DOWN

04

IO12

GPIO12, input, output, high impedance

SPIHD

VDD3P3_CPU

UP/DOWN

03

IO01

GPIO1, input, output, high impedance

UART1_RX/ADC_1

VDD3P3_CPU

UP/DOWN

02

IO00

GPIO0, input, output, high impedance

UART1_TX/ADC_0

VDD3P3_CPU

UP/DOWN

01

GND

grounding

 

 

 

Fungsi

Power Supply

Ada 3 cara memasang power supply:

  • USB-C (default)
  • 5V & GND header
  • 3V3 & GND header

LED

Ada 2 buah LED pada board ini.

LED number

Corresponding to GPIO

pin function

describe

D4

IO12

GPIO12 configuration

active high

D5

IO13

GPIO13 configuration

active high

Tombol

Ada 2 buah tombol, yaitu tombol BOOT dan RST

key number

pin function

describe

BOOT/GPIO9

When the button is pressed, the chip enters the download mode

active low

RST

When the button is pressed, the chip resets

active low

 

Schematic

ESP32 C3 Luatos-schematic flash section
Memori Flash
ESP32 C3 Luatos-schematic mikroprosesor
Skema bagian mikroprosesor
ESP32 C3 Luatos-schematic switch and LEDs
Sakelar dan LEDs
ESP32 C3 Luatos-schematic regulator section
Regulator
ESP32 C3 Luatos-schematic USB section
USB circuit

Teknik Pemrograman

di Arduino IDE, pilih board ESP32C3 Dev Module, kemudian pilih flashmode “DIO”

Tips dari https://github.com/mboehmerm/Luatos_C3_Core_DIO_SSD1309_I2C_128x64

Referensi

 

Perbedaan ESP32 dan ESP8266

Perbedaan ESP32 dan ESP8266 adalah sebagai berikut

Parameter ESP32 ESP8266
Prosesor / CPU Xtensa :LX6 dual core / single core Xtensa L:106 single core
clock processor 160 MHz 80 MHz
Memori Flash ada versi dengan built in flash tidak ada built in flash
Memori SRAM    
Memori EEPROM di flash memory  
Wifi HT40 HT20
Bluetooth tidak ada Bluetooth 4.2 dan BLE
SPI 4 2
I2C 2 1
I2S 2 2
UART 2 2
ADC 12 bit 10 bit
CAN ada tidak ada
Built in sensor hall sensor, temperature sensor tidak ada

 

ESP8266 NodeMCU (kiri), ESP32 DevkitC (tengah), ESP32 Lolin32 Lite (kanan)
ESP8266 NodeMCU (kiri), ESP32 DevkitC (tengah), ESP32 Lolin32 Lite (kanan)

Penjelasan Ringkas ESP32

ESP 32 adalah mikrokontroler yang memiliki banyak fitur pada prosesornya, dan dilengkapi dengan konektivitas Wi-Fi dan Bluetooth terintegrasi untuk berbagai macam aplikasi.

ESP32 mampu berfungsi dengan andal di lingkungan industri, dengan suhu pengoperasian mulai dari –40°C hingga +125°C. ESP32 dapat secara dinamis menghilangkan ketidaksempurnaan sirkuit eksternal dan beradaptasi dengan perubahan kondisi eksternal dengan dukungan rangkaian kalibrasi di dalamnya. Rangkaian kalibrasi ini memungkinkan WiFi pada ESP32 berfungsi tanpa perlu kalibrasi ekstra.

ESP32 dirancang untuk perangkat bergerak (mobile), perangkat elektronik yang dapat dikenakan (wearable electronics), dan aplikasi IoT. Konsumsi daya ESP32 dapat dibuat sangat rendah dengan kombinasi beberapa macam perangkat lunak khusus. ESP32 juga mencakup fitur-fitur canggih, seperti ‘clock gating‘ untuk mereduksi konsumsi daya, berbagai mode daya, dan penskalaan daya secara dinamis.

ESP32 dibuat sangat terintegrasi dengan sakelar pemilih antena di dalamnya, RF balun, power amplifier untuk radio, amplifier penerima radio dengan kebisingan rendah (low noise), filter, dan modul manajemen daya. ESP32 menambahkan fungsionalitas dan keserbagunaan yang banyak aplikasi dengan hanya memerlukan ruang kecil di PCB (Printed Circuit Board)

ESP32 dapat berfungsi sebagai sistem mandiri yang lengkap atau sebagai perangkat pembantu ke mikrokontroler lain, mengurangi overhead tumpukan komunikasi pada prosesor aplikasi utama. ESP32 dapat berinteraksi dengan sistem lain untuk menyediakan fungsionalitas Wi-Fi dan Bluetooth melalui antarmuka SPI/SDIO atau I2C/UART.

Modul ESP32 tersedia dalam berbagai versi, di antaranya adalah Lolin32 Lite dan DevkitC serta clone nya.

Penjelasan Ringkas ESP8266

ESP8266 adalah prosesor yang dibuat oleh Espressif. ESP8266 adalah pendahulu dari ESP32.

Referensi

 

UDP Client di ESP32

Berikut ini contoh pembuatan aplikasi UDP client di ESP32 dengan compiler Arduino

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

/* WiFi network name and password */
const char * ssid = "dd-wrt";
const char * pwd = "0000000000";

// IP address to send UDP data to.
// it can be ip address of the server or 
// a network broadcast address
// here is broadcast address
const char * udpAddress = "192.168.1.100";
const int udpPort = 44444;

//create UDP instance
WiFiUDP udp;

void setup(){
  Serial.begin(115200);
  
  //Connect to the WiFi network
   WiFi.begin(ssid, pwd);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop(){
  //data will be sent to server
  uint8_t buffer[50] = "hello world";
  //This initializes udp and transfer buffer
  udp.beginPacket(udpAddress, udpPort);
  udp.write(buffer, 11);
  udp.endPacket();
  memset(buffer, 0, 50);
  //processing incoming packet, must be called before reading the buffer
  udp.parsePacket();
  //receive response from server, it will be HELLO WORLD
  if(udp.read(buffer, 50) > 0){
    Serial.print("Server to client: ");
    Serial.println((char *)buffer);
  }
  //Wait for 1 second
  delay(1000);
}

Referensi

Perbedaan Arduino UNO dan ESP32

Berikut ini perbedaan utama antara Arduino UNO dan ESP32:

Parameter Arduino Uno ESP32
Prosesor ATmega328 Xtensa dual core / single core
Clock prosesor 16 MHz 160 MHz / 240 MHz
Ukuran 68,6 mm x 53,4 mm tergantung board
Analog Input 6 kanal, 10 bit ADC 18 kanal, 12 bit ADC
Tegangan catu daya 6 sampai 20 volt DC, namun disarankan hanya 7 sampai 12 volt DC 5 volt (USB)
Pin untuk Digital Input/Output 14 (6 dengan fitur Pulse Width Modulation) 34 (maksimum)
Berat 25 g tergantung board
Ukuran lebih kecil. shield Arduino Mega tidak dapat dipakai di Arduino UNO lebih besar. shield Arduino UNO dapat dipakai di Arduino Mega
Kapasitas flash memory 32 kilobyte 4 MB (eksternal denganm antar muka SDIO/SPI)
SRAM 2 kilobyte 320 kilobyte
EEPROM 1 kilobyte tidak ada secara khusus, namun dapat menggunakan sebagian memory Flash
Tegangan kerja 5 volt 3 volt
Konektor USB USB type B USB mini
Konektivitas Jaringan tidak ada WiFi 802.11, bluetooth
Port serial (UART) 1 (hardware) 3 (hardware)
I2C 1 kanal 2 kanal
Output analog tidak ada DAC (Digital to Analog Converter) ada DAC 2 kanal, 8 bit
SPI (Serial Peripheral Interface) 1 kanal 4 kanal

 

Arduino UNO R3, ESP32 Devkit-C  , ESP32 Lolin32 Lite
Arduino UNO R3, ESP32 Devkit-C , ESP32 Lolin32 Lite

Arduino UNO

Arduino UNO R3 asli
Arduino UNO R3 asli

Arduino UNO adalah rangkaian mikrokontroler berbasis ATmega328P. Arduino UNO memiliki 14 pin input/output digital (6 di antaranya dapat digunakan sebagai output PWM), 6 input analog, resonator keramik 16 MHz sebagai sumber clock, koneksi USB untuk catu daya dan komunikasi serial, colokan listrik DC, header ICSP (In Circuit Serial Programming), dan tombol reset. Papan rangkaian Arduino UNO berisi semua hal yang diperlukan untuk mendukung mikrokontroler. Untuk menggunakan Arduino UNO, cukup dengan disambung ke komputer dengan kabel USB atau sambungkan dengan adaptor AC-ke-DC atau baterai untuk memulai. Anda dapat mengotak-atik UNO Anda tanpa terlalu khawatir melakukan kesalahan. Pada kondisi terburuk, chip ATmega328 dengan kemasan DIP (Dual In line Package) dapat diganti jika rusak.

ESP32

ESP 32 adalah mikrokontroler yang memiliki banyak fitur pada prosesornya, dan dilengkapi dengan konektivitas Wi-Fi dan Bluetooth terintegrasi untuk berbagai macam aplikasi.

ESP32 mampu berfungsi dengan andal di lingkungan industri, dengan suhu pengoperasian mulai dari –40°C hingga +125°C. ESP32 dapat secara dinamis menghilangkan ketidaksempurnaan sirkuit eksternal dan beradaptasi dengan perubahan kondisi eksternal dengan dukungan rangkaian kalibrasi di dalamnya. Rangkaian kalibrasi ini memungkinkan WiFi pada ESP32 berfungsi tanpa perlu kalibrasi ekstra.

ESP32 dirancang untuk perangkat bergerak (mobile), perangkat elektronik yang dapat dikenakan (wearable electronics), dan aplikasi IoT. Konsumsi daya ESP32 dapat dibuat sangat rendah dengan kombinasi beberapa macam perangkat lunak khusus. ESP32 juga mencakup fitur-fitur canggih, seperti ‘clock gating‘ untuk mereduksi konsumsi daya, berbagai mode daya, dan penskalaan daya secara dinamis.

ESP32 dibuat sangat terintegrasi dengan sakelar pemilih antena di dalamnya, RF balun, power amplifier untuk radio, amplifier penerima radio dengan kebisingan rendah (low noise), filter, dan modul manajemen daya. ESP32 menambahkan fungsionalitas dan keserbagunaan yang banyak aplikasi dengan hanya memerlukan ruang kecil di PCB (Printed Circuit Board)

ESP32 dapat berfungsi sebagai sistem mandiri yang lengkap atau sebagai perangkat pembantu ke mikrokontroler lain, mengurangi overhead tumpukan komunikasi pada prosesor aplikasi utama. ESP32 dapat berinteraksi dengan sistem lain untuk menyediakan fungsionalitas Wi-Fi dan Bluetooth melalui antarmuka SPI/SDIO atau I2C/UART.

Modul ESP32 tersedia dalam berbagai versi, di antaranya adalah Lolin32 Lite dan DevkitC serta clone nya.

ESP32 memiliki beberapa kelebihan dibandingkan Arduino. Di antaranya sebagai berikut:

  • Prosesor di ESP32 dari keluarga Xtensa, lebih cepat dibandingkan prosesor ATmega dengan arsitektur AVR. ESP32 clocknya adalah 240 MHz, sedangkan Arduino UNO menggunakan ATmega328 dengan kecepatan maksimum 16 MHz
  • Jumlah core pada prosesornya lebih banyak. ESP32 mempunyai 2 core, sedangkan ATmega328 hanya 1 core saja.
  • Memori Flash yang terhubung lebih banyak. ESP32 memiliki Flash Memory sebesar 1 MB sampai 16 MB. Arduino UNO memiliki memori flash sebesar 32 KB. Model Arduino lain ada yang memiliki flash lebih besar, seperti ATmega2560 yang memiliki flash sebesar 256 KB
  • Memori RAM lebih besar. ESP32 memiliki SRAM 520 KiB, sedangkan Arduino UNO memiliki SRAM hanya 2 KB. ATmega2560 memiliki SRAM sebesar 8 kilobyte
  • ESP32 memiliki kemampuan nirkabel WiFi 802.11 dan Bluetooth. Fitur ini di Arduino UNO tidak ada, harus menambah modul/shield.

Referensi

Telemetri Data Sakelar Dengan Thingsboard

Pada percobaan ini dilakukan pengiriman data telemetri dari ESP32 ke Thingsboard. Sensor yang dipakai adalah sakelar on-off. Percobaan ini adalah bagian dari pembuatan sistem IoT (Internet of Things) yang terdiri dari beberapa ESP32 dan software ThingsBoard.

Arsitektur ThingsBoard adalah sebagai berikut:

Arsitektur ThingsBoard
Arsitektur ThingsBoard

Pada percobaan ini , tidak semua komponen ThingsBoard dipakai. Yang dipakai adalah sebagai berikut

  • Devices: menggunakan ESP32 dan sakelar sebagas sensor 
  • Protokol HTTP sebagai ThingsBoard Transports
  • ThingsBoard Core
  • SQL Database
  • External Systems

Aliran data pada sistem yang dibuat adalah sebagai berikut:

Aliran data pada sistem. Sensor berupa sakelar. Kondisi sakelar dibaca oleh ESP32, kemudian dikirim secara teratur ke ThingsBoard.

Sakelar bertindak sebagai sensor. Besaran yang dihasilkan oleh sakelar adalah 0 dan 1. ESP32 membaca status sensor setiap interval tertentu, dan kemudian mengirimkannya ke ThingsBoard menggunakan protokol HTTP. Data ini kemudian disimpan di Database.

Data dari database ini dapat ditampilkan menggunakan Dashboard pada Thingsboard, dan juga dapat diambil oleh aplikasi eksternal menggunakan REST API (REpresentational State Transfer Application Program Interface)

Hardware yang dipakai adalah sebagai berikut:

  • ESP32 Lolin Wemos
  • 2 buah sakelar. Pada percobaan ini digunakan sakelar geser SPDT dan push button.
ESP32 Lolin Wemos
ESP32 Lolin Wemos

Daftar pin ESP32 yang dipakai adalah sebagai berikut

  • pin 13 sebagai input digital, diberi nama SW0. Input active low.
  • pin 15 sebagai input digital, diberi nama SW1. Input active low.

Berikut ini foto rangkaian setelah dirakit

Perangkat lunak yang dipakai

  • Arduino versi 1.8.19 (Windows)
  • ThingsBoard versi 3.3.4.1
  • Ubuntu Linux 20.04.04 LTS
  • Wireshark versi 3.02

Setting ThingsBoard

[under construction]

Perangkat Lunak ESP32

Perangkat lunak ESP32 dibuat dengan Arduino

Library yang dipakai di Arduino adalah sebagai berikut

Prosedur instalasi dapat dilihat di artikel “ESP32 Pico Kit GPIO Control and DHT22 sensor monitor using ThingBoard Arduino SDK

// demo switch telemetry to Thingsboard
// adapted from https://thingsboard.io/docs/samples/esp32/gpio-control-pico-kit-dht22-sensor/

#include <WiFi.h>           // WiFi control for ESP32
#include <ThingsBoard.h>    // ThingsBoard SDK

// onboard LED
#define BUILTIN_LED 22
// switch pin definition
#define SW0 13
#define SW1 15

// Helper macro to calculate array size
// #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

// WiFi access point
#define WIFI_AP_NAME        "ACCESS_POINT_NAME"
// WiFi password
#define WIFI_PASSWORD       "ACCESS_POINT_PASSWORD"

// See https://thingsboard.io/docs/getting-started-guides/helloworld/
// to understand how to obtain an access token
#define TOKEN               "QdqSrxBAjBvxAoJyeoXN"
// ThingsBoard server instance.
#define THINGSBOARD_SERVER  "192.168.0.90"

// Baud rate for debug serial
#define SERIAL_DEBUG_BAUD    115200

// Initialize ThingsBoard client
WiFiClient espClient;
// Initialize ThingsBoard instance
ThingsBoard tb(espClient);
// the Wifi radio's status
int status = WL_IDLE_STATUS;

// main application loop delay (ms)
int quant = 20;

// Period of sending a temperature/humidity data.
int send_delay = 2000;

// Time passed after telemetry data was sent, milliseconds.
int send_passed = 0;

char mac_str[20]; // storing MAC address string
byte mac_byte[6]; // storing MAC address bytes

int led_counter = 0; //blinking built int led

// Setup an application
void setup() {
  pinMode(SW0, INPUT_PULLUP); // input switch
  pinMode(SW1, INPUT_PULLUP);

  pinMode(BUILTIN_LED , OUTPUT);


  // Initialize serial for debugging
  Serial.begin(SERIAL_DEBUG_BAUD);
  WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
  InitWiFi();

  WiFi.macAddress(mac_byte);
  sprintf(mac_str, "%02x%02x%02x%02x%02x%02x", mac_byte[0], mac_byte[1], mac_byte[2], mac_byte[3], mac_byte[4], mac_byte[5]);
  Serial.print("ESP board MAC address:  ");
  Serial.println(WiFi.macAddress());
  Serial.print("ESP board  IP address: ");
  Serial.println(WiFi.localIP());
}

// Main application loop
void loop() {
  delay(quant);
  send_passed += quant;

  // Reconnect to WiFi, if needed
  if (WiFi.status() != WL_CONNECTED) {
    reconnect();
    return;
  }

  // Reconnect to ThingsBoard, if needed
  if (!tb.connected()) {
    // Connect to the ThingsBoard
    Serial.print("Connecting to: ");
    Serial.print(THINGSBOARD_SERVER);
    Serial.print(" with token ");
    Serial.println(TOKEN);
    if (!tb.connect(THINGSBOARD_SERVER, TOKEN)) {
      Serial.println("Failed to connect");
      return;
    }
  }

  // Check if it is a time to send switch data
  if (send_passed > send_delay) {
    int switch0, switch1;
    switch0 = digitalRead(SW0);
    switch1 = digitalRead(SW1);
    Serial.print("Sending telemetry data...");
    Serial.print(" SW0:");
    Serial.print(switch0);
    Serial.print(" SW1:");
    Serial.println(switch1);
    tb.sendTelemetryInt("switch00", switch0);
    tb.sendTelemetryInt("switch01", switch1);
    send_passed = 0;
  }

  // Process messages
  tb.loop();

  led_counter++; // LED blink at 1 Hz
  if (led_counter > 50) {
    led_counter = 0;
  }
  if (led_counter > 25) {
    digitalWrite(BUILTIN_LED , LOW);
  } else {
    digitalWrite(BUILTIN_LED , HIGH);
  }
}

void InitWiFi()
{
  Serial.println("Connecting to AP ...");
  // attempt to connect to WiFi network

  WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected to AP");
}

void reconnect() {
  // Loop until we're reconnected
  status = WiFi.status();
  if ( status != WL_CONNECTED) {
    WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    Serial.println("Connected to AP");
  }
}

Pengecekan Telemetri

Setelah sistem dirakit dan ESP32 diprogram, perlu dilakukan pengecekan apakah sistem berfungsi dengan baik. Pengecekan pengiriman data telemetri dilakukan dengan cara:

  • Memantau output dari port serial ESP32
  • Mengecek data telemetri di server Thingsboard

Berikut ini output dari serial monitor ESP32.

19:45:30.745 -> Connecting to AP ...
19:45:31.225 -> ......Connected to AP
19:45:33.738 -> ESP board MAC address:  3C:71:BF:03:41:4C
19:45:33.738 -> ESP board  IP address: 192.168.0.124
19:45:33.773 -> Connecting to: 192.168.0.90 with token QdqSrxBAjBvxAoJyeoXN
19:45:35.798 -> Sending telemetry data... SW0:1 SW1:1
19:45:37.819 -> Sending telemetry data... SW0:1 SW1:1
19:45:39.850 -> Sending telemetry data... SW0:1 SW1:1

Dari output tersebut dapat disimpulkan:

  • ESP32 berhasil terhubung ke access point
  • ESP32 berhasil mendapatkan IP address
  • ESP32 berhasil mengirim data ke ThingsBoard

Untuk mengecek data yang masuk ke server, prosedurnya sebagai berikut:

  • Login ke ThingsBoard sebagai tenant. username: tenant@thingsboard.org, password: tenant (jika belum diganti)
  • Klik di “Devices”
  • Klik di “ESP32 Dual Switch”
  • Klik di “Latest telemetry”

Jika pengiriman data normal, maka status sakelar terakhir akan muncul di Device details, seperti gambar berikut ini.

Angka di kolom “Value” akan sesuai dengan status sakelar. Pada percobaan ini pengiriman data dilakukan dengan interval 2 detik, jadi selambatnya dalam 2 detik status sakelar terakhir akan masuk.

Dashboard di ThingsBoard

Setelah data dicek masuk, kita dapat membuat dashboard di Thingsboard untuk menampilkan data dari sensor. Berikut ini contoh tampilan dashboard.

dashboard ThingsBoard untuk dual switch

Pengambilan Data Telemetri Dengan REST API

Data telemetri di server ThingsBoard dapat diambil menggunakan REST API dari aplikasi lain.

Pada percobaan ini data diambil dengan menggunakan software ‘curl’ di Ubuntu Linux.

Pertama-tama membuat dulu JWT (JSON Web Token).

curl -X POST –header ‘Content-Type: application/json’ –header ‘Accept: application/json’ -d ‘{“username”:”tenant@thingsboard.org”, “password”:”tenant”}’ ‘http://192.168.0.90:8080/api/auth/login’

Hasilnya dalam bentuk JSON sebagai berikut

{“token”:”eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIiwic2NvcGVzIjpbIlRFTkFOVF9BRE1JTiJdLCJ1c2VySWQiOiI2ZjEyZDE5MC1jOTAwLTExZWMtOWRlYS1kN2UzNGUyNDNiZWIiLCJlbmFibGVkIjp0cnVlLCJpc1B1YmxpYyI6ZmFsc2UsInRlbmFudElkIjoiNmU3OTI5YTAtYzkwMC0xMWVjLTlkZWEtZDdlMzRlMjQzYmViIiwiY3VzdG9tZXJJZCI6IjEzODE0MDAwLTFkZDItMTFiMi04MDgwLTgwODA4MDgwODA4MCIsImlzcyI6InRoaW5nc2JvYXJkLmlvIiwiaWF0IjoxNjUxNTgzNjI5LCJleHAiOjE2NTE1OTI2Mjl9.Ci_MA7PgSQk6xeMOgjJ_NRr3ipuRwLc2t3Yow2Nc5WWDCTPNNfbYPl4-bTikYH7DYFB5-ZqBuhnVVMY4kdJpaQ”,”refreshToken”:”eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIiwic2NvcGVzIjpbIlJFRlJFU0hfVE9LRU4iXSwidXNlcklkIjoiNmYxMmQxOTAtYzkwMC0xMWVjLTlkZWEtZDdlMzRlMjQzYmViIiwiaXNQdWJsaWMiOmZhbHNlLCJpc3MiOiJ0aGluZ3Nib2FyZC5pbyIsImp0aSI6ImUwNGE0MDUyLTA3ZmYtNDRmNS04NmQ3LTRhYTIwYjEzNjFiOSIsImlhdCI6MTY1MTU4MzYyOSwiZXhwIjoxNjUyMTg4NDI5fQ.ZpLUtoqoY2BZBooRPu3vqF8DEbXOJuM33YsaRaR6IorBenRyRmnNAb_rTbpRfHWzh8-zBGDc0Ah0w6qqYK3ksA”}

Parameter yang diperlukan adalah hanya “token”, tidak termasuk”refresh token”.

Berikutnya membuat query REST API

curl -v -X GET http://192.168.0.90:8080/api/plugins/telemetry/DEVICE/e19fb430-caa4-11ec-84b1-a3192844351e/values/timeseries?keys=switch00,switch01 \
> –header “Content-Type:application/json” \
> –header “X-Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIiwic2NvcGVzIjpbIlRFTkFOVF9BRE1JTiJdLCJ1c2VySWQiOiI2ZjEyZDE5MC1jOTAwLTExZWMtOWRlYS1kN2UzNGUyNDNiZWIiLCJlbmFibGVkIjp0cnVlLCJpc1B1YmxpYyI6ZmFsc2UsInRlbmFudElkIjoiNmU3OTI5YTAtYzkwMC0xMWVjLTlkZWEtZDdlMzRlMjQzYmViIiwiY3VzdG9tZXJJZCI6IjEzODE0MDAwLTFkZDItMTFiMi04MDgwLTgwODA4MDgwODA4MCIsImlzcyI6InRoaW5nc2JvYXJkLmlvIiwiaWF0IjoxNjUxNTgzNjI5LCJleHAiOjE2NTE1OTI2Mjl9.Ci_MA7PgSQk6xeMOgjJ_NRr3ipuRwLc2t3Yow2Nc5WWDCTPNNfbYPl4-bTikYH7DYFB5-ZqBuhnVVMY4kdJpaQ”
Note: Unnecessary use of -X or –request, GET is already inferred.
* Trying 192.168.0.90:8080…
* TCP_NODELAY set
* Connected to 192.168.0.90 (192.168.0.90) port 8080 (#0)
> GET /api/plugins/telemetry/DEVICE/e19fb430-caa4-11ec-84b1-a3192844351e/values/timeseries?keys=switch00,switch01 HTTP/1.1
> Host: 192.168.0.90:8080
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type:application/json
> X-Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIiwic2NvcGVzIjpbIlRFTkFOVF9BRE1JTiJdLCJ1c2VySWQiOiI2ZjEyZDE5MC1jOTAwLTExZWMtOWRlYS1kN2UzNGUyNDNiZWIiLCJlbmFibGVkIjp0cnVlLCJpc1B1YmxpYyI6ZmFsc2UsInRlbmFudElkIjoiNmU3OTI5YTAtYzkwMC0xMWVjLTlkZWEtZDdlMzRlMjQzYmViIiwiY3VzdG9tZXJJZCI6IjEzODE0MDAwLTFkZDItMTFiMi04MDgwLTgwODA4MDgwODA4MCIsImlzcyI6InRoaW5nc2JvYXJkLmlvIiwiaWF0IjoxNjUxNTgzNjI5LCJleHAiOjE2NTE1OTI2Mjl9.Ci_MA7PgSQk6xeMOgjJ_NRr3ipuRwLc2t3Yow2Nc5WWDCTPNNfbYPl4-bTikYH7DYFB5-ZqBuhnVVMY4kdJpaQ
>

Berikut ini output dari curl:

* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 03 May 2022 13:16:56 GMT
<
* Connection #0 to host 192.168.0.90 left intact
{“switch00”:[{“ts”:1651583815502,”value”:”0″}],”switch01″:[{“ts”:1651583815505,”value”:”1″}]}

Output dalam format JSON

Referensi

Perekam Modbus RTU Dengan ESP32

Pada pengembangan sistem dengan komunikasi Modbus RTU dengan RS485, kadang-kadang kita perlu memonitor pertukaran data di kabel RS-485 untuk memantau apakah komunikasi data berjalan dengan benar. Pada artikel ini diuraikan secara ringkas pembuatan perangkat untuk memantau dan merekam trafik data di komunikasi RS-485 tersebut.

 

Berikut ini skema rangkaiannya secara sederhana

Rangkaian ESP32 dengan RS485
Rangkaian ESP32 dengan RS485

Penjelasan cara kerja rangkaian

Komponen utama sistem adalah sebagai berikut

  • MAX485 berfungsi untuk mengubah level tegangan RS-485 menjadi TTL 5 volt.
  • Level converter berfungsi mengubah tegangan TTL 5 volt dari MAX485 menjadi TTL dengan tegangan 3 volt.
  • Mikrokontroler ESP32 berfungsi merekam data serial dan mengirimnya menggunakan kabel USB ke komputer.
  • Komputer untuk merekam data serial dari ESP32
  • Power supply 5 volt untuk MAX485
Pin pada MAX485
Pin pada MAX485

Berikut ini foto rangkaian dengan breadboard

Alat untuk merekam sinyal Modbus RTU di RS485 dengan mikrokontroler ESP32

Rangkaian ESP32 , MAX485 dan level converter di breadboard
Rangkaian ESP32 , MAX485 dan level converter di breadboard

 

#esp32-freertos-rs485-analyzer
/**
   analisis data RS485
*/
#define LED_BUILTIN 22

#define RXD1 17
#define TXD1 5
#define RXD2 18
#define TXD2 23


void setup() {
  Serial.begin(115200);
  Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);

  Serial.println("Serial Txd 0 is on pin: " + String(TX));
  Serial.println("Serial Rxd 0 is on pin: " + String(RX));

  Serial.println("Serial Txd 1 is on pin: " + String(TXD1));
  Serial.println("Serial Rxd 1 is on pin: " + String(RXD1));

  Serial.println("Serial Txd 2 is on pin: " + String(TXD2));
  Serial.println("Serial Rxd 2 is on pin: " + String(RXD2));

  pinMode(LED_BUILTIN, OUTPUT);


  if (1)
    xTaskCreate(
      TaskBlinker, /* Task function. */
      "TaskBlinker", /* String with name of task. */
      1000, /* Stack size in bytes. */
      NULL, /* Parameter passed as input of the task */
      1, /* Priority of the task. */
      NULL); /* Task handle. */
  xTaskCreate(
    TaskSerial1, /* Task function. */
    "TaskSerial1", /* String with name of task. */
    2000, /* Stack size in bytes. */
    NULL, /* Parameter passed as input of the task */
    1, /* Priority of the task. */
    NULL); /* Task handle. */
}


// the loop function runs over and over again forever

void loop() {
  delay(10000);
}


void TaskBlinker(void *parameter) {
  int counter = 0;
  TickType_t xLastWakeTime;
  const TickType_t xPeriod = 500      ;
  xLastWakeTime = xTaskGetTickCount();
  //  dht.begin(); // aktifkan sensor DHT22
  while (1) {
    digitalWrite(LED_BUILTIN, HIGH);
    vTaskDelayUntil(&xLastWakeTime, xPeriod);
    digitalWrite(LED_BUILTIN, LOW);
    vTaskDelayUntil(&xLastWakeTime, xPeriod);
//    Serial.print("Counter:");
//    Serial.println(counter);
    counter++;
  }
}

#define BUFFER_MAX 200

void TaskSerial1(void *parameter) {
  int interframe_timer_max = 0;
  int interframe_timer = 0; // untuk mengukur waktu antar frame
  char buffer_data[BUFFER_MAX];
  int buffer_idx = 0;
  TickType_t xLastWakeTime;
  const TickType_t xPeriod = 1      ;
  xLastWakeTime = xTaskGetTickCount();
  while (1) {
    int incomingByte;
    if (Serial1.available() > 0) {
      interframe_timer = 0;
    } else {
      interframe_timer++;
    }
    while (Serial1.available() > 0) {
      // read the incoming byte:
      incomingByte = Serial1.read();
      buffer_data[buffer_idx] = incomingByte ;
      buffer_idx++;
      if (buffer_idx > BUFFER_MAX) {
        buffer_idx = BUFFER_MAX;  // buffer overflow protection
      }
    }
    //Serial.print(incomingByte, HEX);
    //Serial.print(" ");
    if (interframe_timer > interframe_timer_max) {
      interframe_timer_max = interframe_timer;
      Serial.print("interframe_max ");
      Serial.println(interframe_timer_max);
    }
    if (interframe_timer > 6 && buffer_idx > 0) {
      // cetak semua data di buffer
      for (int i = 0; i < buffer_idx; i++) {
        Serial.print(buffer_data[i], HEX);
        Serial.print(" ");
      }
      Serial.println();
      buffer_idx = 0; // empty buffer
    }
    vTaskDelayUntil(&xLastWakeTime, xPeriod);
  }
}

Referensi

Instalasi Library mbedtls di Windows

Library mbedtls diperlukan di Linux/Windows kalau kita mau melakukan enkripsi data, terutama kalau aplikasi kita akan berkomunikasi secara tersandikan dengan mikrokontroler ESP32. Library kriptografi di Windows yang kompatibel dengan kriptograsi di ESP32 antara lain adalah mbedtls.

Pertama lakukan instalasi library. Pada contoh berikut ini instalasi dilakukan di cygwin.

Jalankan setup cygwin, kemudian pilih package yang bernama ‘mbedtls”.

Pada contoh di atas, yg dipilih adalah library mbedtls dan mbedtls-devel

Selanjutnya library ini mesti ditambahkan ketika proses link di aplikasi kita.

Sebagai contoh, saya menggunakan IDE Codeblocks. Ketika membuat project, pilih compiler “Cygwin GCC”

Selanjutnya pada CodeBlocks, penambahan library dilakukan dengan masuk ke menu Project -> Build Options -> Linker Setting

Tambahkan file library mbedtls. File library dapat dicari di direktori c:\cygwin64\lib

Nama library yang diperlukan adalah “libmbedcrypto.a”

Selanjutnya kita dapat melakukan aplikasi yang menggunakan mbedtls, seperti “Enkripsi data antara ESP32 dan Windows

Referensi

Real Time pada Raspberry Pi

Raspberry Pi dapat dibuat menjadi real time dengan menggunakan sistem operasi RTOS (Real Time Operating System), misalnya:

Berikut ini petunjuk instalasi RTOS pada Raspberry Pi:

Raspberry Pi umumnya menggunakan sistem operasi Raspbian , yang merupakan varian dari Debian Linux. Sistem operasi Linux ini bukan sistem operasi real time, sehingga Raspberry Pi tidak real time jika menggunakan sistem operasi Raspbian.

Fitur penting pada Raspbery PI adalah kemampuan komputasinya di prosesornya yang cepat (bisa sampai 4 core @1  GHz), memori yang besar (sampai 8 GB) dan sistem operasi Linux yang fleksibel.

Kekurangan Raspberry Pi jika menggunakan RTOS adalah contoh programnya tidak sebanyak Raspberry Pi dengan sistem operasi Raspbian. Jika kita perlu Raspberry Pi dengan sistem operasi Raspbian tapi perlu juga fitur real time, maka salah satu solusinya adalah fitur real time ditangani oleh prosesor lain, misal ATmega328 (Arduino Nano) atau ESP32. Untuk menghubungkan Raspberry Pi ke ArduinoNano / ESP32 dapat menggunakan protokol serial, seperti serial asinkron maupun I2C (Inter Integrated Circuit).

Referensi

 

 

Sensor CO2 MH-Z19B

Sensor Winsen MH-Z19B berfungsi mengukur kadar CO2 di udara. Prinsip pengukuran menggunakan NDIR (Non Dispersive Infrared). Fitur utama: selektivitas tinggi, tidak tergantung oksigen, masa pakai panjang. Di dalamnya sudah ada kompensasi temperatur. Outputnya tersedia 3 macam, berupa sinyal digital serial asinkron, sinyal digital PWM (pulse width modulation) dan tegangan analog.

Kadar CO2 di udara dapat dipakai untuk indikator kualitas ventilasi di suatu ruangan. Ventilasi yang baik ini penting untuk mengurangi penularan COVID-19.

Spesifikasi

Product Model MH-Z19B
Target Gas
CO2
Working voltage 4.5~ 5.5 V DC
Average current < 60mA(@5V)
Peak current 150mA (@5V)
Interface level 3.3 V(Compatible with 5V)
Measuring range refer to Table 2
Output signal
UART(TTL interface level 3.3V)
PWM
DAC(default 0.4-2V)
Preheat time 3 min
Response Time T90< 120 s
Working temperature
0 ~ 50 ℃
Working humidity 0~ 90% RH (No condensation)
Dimension 33 mm×20 mm×9 mm (L×W×H)
Weight 5g
Lifespan > 5 years
 

Penampakan

Berikut ini contoh sensor MH-Z19B yang dihubungkan ke prosesor ESP32 DevkitC. Pertimbangan menggunakan DevkitC adalah karena tegangan sinyal sensor adalah 3 volt namun juga memerlukan tegangan Vcc sebesar 5 volt. Board ESP32 Lolin32 Lite tidak memiliki output 5 volt, jadi agak sulit digunakan pada aplikasi ini.

Contoh Kode

Berikut ini contoh pembacaan sensor melalui port serial.

Konfigurasi port serial di ESP32 menggunakan pin 22 sebagai RXD dan pin 23 sebagai TXD. Jalur port serial dihubungkan melalui resistor 100 ohm untuk perlindungan kalau terjadi kesalahan polaritas pin TX dan RX.

// https://circuits4you.com/2018/12/31/esp32-hardware-serial2-example/

#define RXD2 22
#define TXD2 23

// These constants won't change. They're used to give names to the pins used:

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

#define LED_BUILTIN 2

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  Serial.println("Start Devkit");
  Serial.println("Start Devkit");

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(21, OUTPUT);
}

void loop() {
  char cmd;

  Serial2.print((char)0xff); // 0 : start byte
  Serial2.print((char)0x01);
  Serial2.print((char)0x86);
  Serial2.print((char)0x00);
  Serial2.print((char)0x00);
  Serial2.print((char)0x00);
  Serial2.print((char)0x00);
  Serial2.print((char)0x00);
  Serial2.print((char)0x79); // 8: checksum

  int counter = 0;
  int co2_high, co2_low;
  int available;
  available = Serial2.available();
  while (available > 0) {

    // read the incoming byte:
    int incomingByte = Serial2.read();

    // say what you got:
    Serial.print(">>:");
    Serial.println(incomingByte, DEC);

    if (counter == 2) {
      co2_high = incomingByte;
    }
    if (counter == 3) {
      co2_low = incomingByte;
    }
    available = Serial2.available();

    counter++;
  }
  Serial.print("CO2: ");
  Serial.println(co2_high * 256 + co2_low);

  //  Serial.print("available: ");
  //  Serial.println(available);

  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  digitalWrite(21, HIGH);
  delay(500);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  digitalWrite(21, LOW);    // turn the LED off by making the voltage LOW
  delay(500);
}

Referensi

 

Mengukur kadar Karbondioksida Dengan Sensor CJMCU-8118

Kode

 

// https://github.com/adafruit/Adafruit_CCS811/blob/master/examples/CCS811_test/CCS811_test.ino
// https://github.com/closedcube/ClosedCube_HDC1080_Arduino
// https://github.com/closedcube/ClosedCube_HDC1080_Arduino/blob/master/examples/hdc1080measurement/hdc1080measurement.ino

#define I2C_SDA 15
#define I2C_SCL 13
#define LED_ONBOARD 22

#include "Adafruit_CCS811.h"
#include <Arduino.h>
#include <Wire.h>
#include "ClosedCube_HDC1080.h"

Adafruit_CCS811 ccs;
ClosedCube_HDC1080 hdc1080;

void setup() {
  pinMode(LED_ONBOARD, OUTPUT);
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(1000);

  //wait for serial connection to open (only necessary on some boards)
  while (!Serial);

  Wire.begin(I2C_SDA, I2C_SCL);

  I2C_Scan() ; // just for verifying

  // check CCS811
  Serial.println("CCS811 test");
  if (!ccs.begin()) {
    Serial.println("Failed to start sensor! Please check your wiring.");
    while (1);
  } else {
    Serial.println("CCS811 ready");
  }


  hdc1080.begin(0x40);
  Serial.println("HDC1080");
  Serial.print("Manufacturer ID=0x");
  Serial.println(hdc1080.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
  Serial.print("Device ID=0x");
  Serial.println(hdc1080.readDeviceId(), HEX); // 0x1050 ID of the device

  hdc1080.setResolution(HDC1080_RESOLUTION_11BIT, HDC1080_RESOLUTION_11BIT);

}

void loop() {
  float temperature = 25;
  float pressure = 0;
  float humidity = 60; // default humidity
  float co2 = 0;
  float tvoc = 0;



  temperature = hdc1080.readTemperature();
  humidity = hdc1080.readHumidity();

  ccs.setEnvironmentalData( humidity, temperature );

  if (ccs.available()) {
    if (!ccs.readData()) {
      //      Serial.print("CO2: ");
      //Serial.print(ccs.geteCO2());
      //Serial.print("ppm, TVOC: ");
      //Serial.println(ccs.getTVOC());
      co2 = ccs.geteCO2();
      tvoc =   ccs.getTVOC();
    }
  }

  Serial.print("Temp\t");
  Serial.print(temperature);
  Serial.print("\t");

  Serial.print("Humidity\t");
  Serial.print(humidity);
  Serial.print("\t");

  Serial.print("CO2\t");
  Serial.print(co2);
  Serial.print("\t");
  Serial.print("TVOC\t");
  Serial.print(tvoc);
  Serial.println("");
  delay(500);
  digitalWrite(LED_ONBOARD, HIGH);
  delay(500);
  digitalWrite(LED_ONBOARD, LOW);
}

void I2C_Scan() {
  byte error, address;

  int nDevices;
  Serial.println("I2C Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
      nDevices++;
    }
    else if (error == 4) {
      Serial.print("Unknow error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
}

Referensi

 

Mengukur Kadar Karbondioksida Dengan Sensor CCS811 dan Mikrokontroler ESP32

Pada percobaan ini dilakukan pengukuran kadar gas CO2  (karbon dioksida) di udara dengan sensor CCS811.

Mikrokontroler yang digunakan adalah modul ESP32 Lolin32 Lite

Modul CJMCU-811 tidak dilengkapi sensor temperatur & kelembaban, sehingga perlu tambahan sensor BME280.

Kompensasi temperatur dan kelembaban untuk sensor CCS811 menggunakan sensor BME280

Berikut ini foto perangkat keras yang digunakan

Berikut ini rangkaian sistem mikroprosesornya

Berikut ini source code program yang dipakai (dengan Arduino)

// https://bitbucket.org/christandlg/bmx280mi/src/master/examples/BMx280_I2C/BMx280_I2C.ino
// BMx280_I2C.ino
//
// shows how to use the BMP280 / BMx280 library with the sensor connected using I2C.
//
// Copyright (c) 2018 Gregor Christandl
//
// connect the AS3935 to the Arduino like this:
//
// Arduino - BMP280 / BME280
// 3.3V ---- VCC
// GND ----- GND
// SDA ----- SDA
// SCL ----- SCL
// some BMP280/BME280 modules break out the CSB and SDO pins as well:
// 5V ------ CSB (enables the I2C interface)
// GND ----- SDO (I2C Address 0x76)
// 5V ------ SDO (I2C Address 0x77)
// other pins can be left unconnected.


// https://github.com/adafruit/Adafruit_CCS811
// https://github.com/adafruit/Adafruit_CCS811/blob/master/examples/CCS811_test/CCS811_test.ino


#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;

#include <Arduino.h>
#include <Wire.h>

#define I2C_SDA 15
#define I2C_SCL 13


#include <BMx280I2C.h>

#define I2C_ADDRESS 0x76

//create a BMx280I2C object using the I2C interface with I2C Address 0x76
BMx280I2C bmx280(I2C_ADDRESS);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(1000);

  //wait for serial connection to open (only necessary on some boards)
  while (!Serial);

  //  Wire.begin();
  Wire.begin(I2C_SDA, I2C_SCL);

  I2C_Scan() ; // just for verifying

  // check CCS811
  Serial.println("CCS811 test");
  if (!ccs.begin()) {
    Serial.println("Failed to start sensor! Please check your wiring.");
    while (1);
  } else {
    Serial.println("CCS811 ready");
  }

  //begin() checks the Interface, reads the sensor ID (to differentiate between BMP280 and BME280)
  //and reads compensation parameters.
  if (!bmx280.begin())
  {
    Serial.println("begin() failed. check your BMx280 Interface and I2C Address.");
    while (1);
  }

  if (bmx280.isBME280())
    Serial.println("sensor is a BME280");
  else
    Serial.println("sensor is a BMP280");

  //reset sensor to default parameters.
  bmx280.resetToDefaults();

  //by default sensing is disabled and must be enabled by setting a non-zero
  //oversampling setting.
  //set an oversampling setting for pressure and temperature measurements.
  bmx280.writeOversamplingPressure(BMx280MI::OSRS_P_x16);
  bmx280.writeOversamplingTemperature(BMx280MI::OSRS_T_x16);

  //if sensor is a BME280, set an oversampling setting for humidity measurements.
  if (bmx280.isBME280())
    bmx280.writeOversamplingHumidity(BMx280MI::OSRS_H_x16);
}

void loop() {
  float temperature = 0;
  float pressure = 0;
  float humidity = 60; // default humidity
  float co2 = 0;
  float tvoc = 0;

  delay(1000);

  //start a measurement
  if (!bmx280.measure())
  {
    Serial.println("could not start measurement, is a measurement already running?");
    return;
  }

  //wait for the measurement to finish
  do
  {
    delay(100);
  } while (!bmx280.hasValue());

  //  Serial.print("Pressure: "); Serial.println(bmx280.getPressure());
  //  Serial.print("Pressure (64 bit): "); Serial.println(bmx280.getPressure64());
  //  Serial.print("Temperature: "); Serial.println(bmx280.getTemperature());

  pressure = bmx280.getPressure();
  temperature = bmx280.getTemperature();

  //important: measurement data is read from the sensor in function hasValue() only.
  //make sure to call get*() functions only after hasValue() has returned true.
  if (bmx280.isBME280())
  {
    //    Serial.print("Humidity: ");
    //    Serial.println(bmx280.getHumidity());
    humidity = bmx280.getHumidity();
  }

  ccs.setEnvironmentalData( humidity, temperature );

  if (ccs.available()) {
    if (!ccs.readData()) {
      //      Serial.print("CO2: ");
      //Serial.print(ccs.geteCO2());
      //Serial.print("ppm, TVOC: ");
      //Serial.println(ccs.getTVOC());



      co2 = ccs.geteCO2();
      tvoc =   ccs.getTVOC();
    }
  }

  Serial.print("Temp\t");
  Serial.print(temperature);
  Serial.print("\t");

  Serial.print("Humidity\t");
  Serial.print(humidity);
  Serial.print("\t");

  Serial.print("Pressure\t");
  Serial.print(pressure);
  Serial.print("\t");

  Serial.print("CO2\t");
  Serial.print(co2);
  Serial.print("\t");
  Serial.print("TVOC\t");
  Serial.print(tvoc);
  Serial.println("");
}

void I2C_Scan() {
  byte error, address;

  int nDevices;
  Serial.println("I2C Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
      nDevices++;
    }
    else if (error == 4) {
      Serial.print("Unknow error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
}

Berikut ini software versi lain

// pengukuran CO2, tanpa wifi

// https://bitbucket.org/christandlg/bmx280mi/src/master/examples/BMx280_I2C/BMx280_I2C.ino
// BMx280_I2C.ino
//
// shows how to use the BMP280 / BMx280 library with the sensor connected using I2C.
//
// Copyright (c) 2018 Gregor Christandl
//
// connect the AS3935 to the Arduino like this:
//
// Arduino - BMP280 / BME280
// 3.3V ---- VCC
// GND ----- GND
// SDA ----- SDA
// SCL ----- SCL
// some BMP280/BME280 modules break out the CSB and SDO pins as well:
// 5V ------ CSB (enables the I2C interface)
// GND ----- SDO (I2C Address 0x76)
// 5V ------ SDO (I2C Address 0x77)
// other pins can be left unconnected.


// https://github.com/adafruit/Adafruit_CCS811
// https://github.com/adafruit/Adafruit_CCS811/blob/master/examples/CCS811_test/CCS811_test.ino


#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;

#include <Arduino.h>
#include <Wire.h>
#include <BMx280I2C.h>
#include "ClosedCube_HDC1080.h"

#define I2C_SDA 15
#define I2C_SCL 13
#define I2C_ADDRESS 0x76
#define LED_ONBOARD 22

const char *ssid     = "First";
const char *password = "satu2345";

//create a BMx280I2C object using the I2C interface with I2C Address 0x76
BMx280I2C bmx280(I2C_ADDRESS);
ClosedCube_HDC1080 hdc1080;

char mac_str[20];
byte mac_byte[6];

void setup() {
  pinMode(LED_ONBOARD, OUTPUT);
  // put your setup code here, to run once:
  Serial.begin(9600);
  delay(1000);

  //wait for serial connection to open (only necessary on some boards)
  while (!Serial);

  //  Wire.begin();
  Wire.begin(I2C_SDA, I2C_SCL);

  I2C_Scan() ; // just for verifying

  // check CCS811
  Serial.println("CCS811 test");
  if (!ccs.begin()) {
    Serial.println("Failed to start sensor! Please check your wiring.");
    while (1);
  } else {
    Serial.println("CCS811 ready");
  }
  SetupBME280();
  SetupHDC1080();
}

void SetupBME280() {
  //begin() checks the Interface, reads the sensor ID (to differentiate between BMP280 and BME280)
  //and reads compensation parameters.
  if (!bmx280.begin())
  {
    Serial.println("begin() failed. check your BMx280 Interface and I2C Address.");
    while (1);
  }

  if (bmx280.isBME280())
    Serial.println("sensor is a BME280");
  else
    Serial.println("sensor is a BMP280");

  //reset sensor to default parameters.
  bmx280.resetToDefaults();

  //by default sensing is disabled and must be enabled by setting a non-zero
  //oversampling setting.
  //set an oversampling setting for pressure and temperature measurements.
  bmx280.writeOversamplingPressure(BMx280MI::OSRS_P_x16);
  bmx280.writeOversamplingTemperature(BMx280MI::OSRS_T_x16);

  //if sensor is a BME280, set an oversampling setting for humidity measurements.
  if (bmx280.isBME280())
    bmx280.writeOversamplingHumidity(BMx280MI::OSRS_H_x16);
}

void SetupHDC1080() {

  hdc1080.begin(0x40);
  Serial.println("HDC1080");
  Serial.print("Manufacturer ID=0x");
  Serial.println(hdc1080.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
  Serial.print("Device ID=0x");
  Serial.println(hdc1080.readDeviceId(), HEX); // 0x1050 ID of the device
  hdc1080.setResolution(HDC1080_RESOLUTION_11BIT, HDC1080_RESOLUTION_11BIT);
}

void loop() {
  float temperature = 0;
  float pressure = 0;
  float humidity = 60; // default humidity
  float co2 = 0;
  float tvoc = 0;

  if (1) {
    //start a measurement
    if (!bmx280.measure())
    {
      Serial.println("could not start measurement, is a measurement already running?");
    }
    do
    {
      delay(100);
    } while (!bmx280.hasValue());

    pressure = bmx280.getPressure();
    temperature = bmx280.getTemperature();
    //important: measurement data is read from the sensor in function hasValue() only.
    //make sure to call get*() functions only after hasValue() has returned true.
    if (bmx280.isBME280())
    {
      humidity = bmx280.getHumidity();
    }
  }
  ccs.setEnvironmentalData( humidity, temperature ); // kompensasi humidity & temperature

  if (ccs.available()) {
    if (!ccs.readData()) {
      //      Serial.print("CO2: ");
      //Serial.print(ccs.geteCO2());
      //Serial.print("ppm, TVOC: ");
      //Serial.println(ccs.getTVOC());
      co2 = ccs.geteCO2();
      tvoc =   ccs.getTVOC();
    }
  }

  Serial.print("Temp\t");
  Serial.print(temperature);
  Serial.print("\t");

  Serial.print("Humidity\t");
  Serial.print(humidity);
  Serial.print("\t");

  Serial.print("Pressure\t");
  Serial.print(pressure);
  Serial.print("\t");

  Serial.print("CO2\t");
  Serial.print(co2);
  Serial.print("\t");
  Serial.print("TVOC\t");
  Serial.print(tvoc);
  Serial.println("");

  delay(500);
  digitalWrite(LED_ONBOARD, HIGH);
  delay(500);
  digitalWrite(LED_ONBOARD, LOW);
}

void I2C_Scan() {
  byte error, address;

  int nDevices;
  Serial.println("I2C Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
      nDevices++;
    }
    else if (error == 4) {
      Serial.print("Unknow error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
}

Berikut ini contoh tampilan outputnya

13:08:18.861 -> I2C Scanning...
13:08:18.861 -> I2C device found at address 0x5A
13:08:18.896 -> I2C device found at address 0x76
13:08:18.931 -> done
13:08:18.931 -> 
13:08:18.931 -> CCS811 test
13:08:19.101 -> CCS811 ready
13:08:19.101 -> sensor is a BME280
13:08:19.135 -> HDC1080
13:08:19.135 -> Manufacturer ID=0xFFFF
13:08:19.135 -> Device ID=0xFFFF
13:08:19.239 -> Temp	30.83	Humidity	50.10	Pressure	87156.00	CO2	0.00	TVOC	0.00
13:08:20.368 -> Temp	30.83	Humidity	50.29	Pressure	91715.00	CO2	0.00	TVOC	0.00
13:08:21.464 -> Temp	30.84	Humidity	50.37	Pressure	91714.00	CO2	0.00	TVOC	0.00

 

Referensi