Aplikasi IoT & C#.NET Desktop

Pengembangan aplikasi IoT juga dapat memanfaatkan aplikasi dekstop winform semacam C#.NET. Berikut ini akan kami demonstrasikan bagaimana mengambangkan aplikasi IoT yang mengkomunikasi antara Smart IoT Development Board dengan Bahasa pemrograman C#,NET Winform Desktop.

Aplikasi Sisi IoT Development Board

Seperti yang telah kita kerjakan sebelumnya pada bagian https://dsp-tech.gitbook.io/internet-of-things/membangun-aplikasi-iot-create-from-scratch/mengkomunikasikan-antara-iot-development-board-dengan-message-broker-mosquitto. Kode program tetap sama seperti contoh, yaitu sebagai berikut:

/*-----------------------------------------------
  IoT Smart Device Development Board
  by Dodit Suprianto | DSP-TECH
  https://doditsuprianto.blogspot.com/
  https://doditsuprianto.gitbook.io/dsp-tech/
  https://github.com/doditsuprianto
  Email: doditsuprianto@gmail.com

  Library Link & Credit:
  1. https://github.com/bblanchon/ArduinoJson
  2. https://github.com/Simsso/ShiftRegister74HC595
  3. https://github.com/winlinvip/SimpleDHT
  4. https://github.com/adafruit/Adafruit_SSD1306
  5. https://github.com/adafruit/Adafruit-GFX-Library
  6. https://github.com/crankyoldgit/IRremoteESP8266
  7. https://github.com/kiryanenko/SimpleTimer
  8. https://github.com/knolleary/pubsubclient
  --------------------------------------------------------*/

/*------------------------
  Library yang diperlukan
  ------------------------*/
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>            // library ESP8266 Wifi
#include <PubSubClient.h>           // library MQTT Message
#include <ShiftRegister74HC595.h>   // library shift register 74HC595
#include <SimpleDHT.h>              // library sensor suhu & kelembaban DHT
#include <Wire.h>
#include <Arduino.h>
#include <Adafruit_GFX.h>           // library OLED
#include <Adafruit_SSD1306.h>       // library font OLED
#include <IRremoteESP8266.h>        // library remote infrared
#include <IRrecv.h>
#include <IRutils.h>
#include <SimpleTimer.h>            // library timer (milles) / thread

/*-------------------------------------
   Buffering memory untuk
   Serialisasi String ke JSON Library
  --------------------------------------*/
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
DynamicJsonDocument dhtData(1024);

/*-------------------------------
  Alamat Kanal Shift Register:
  -------------------------------
  Kanal  0 = LED 1
  Kanal  1 = LED 2
  Kanal  2 = LED 3
  Kanal  3 = LED 4
  Kanal  4 = LED 5
  Kanal  5 = LED 6
  Kanal  6 = LED 7
  Kanal  7 = LED 8
  Kanal  8 = LED 9
  Kanal  9 = Buzz Piezo Speaker
  Kanal 10 = Relay
  Kanal 11 - 15 = Kanal expansi
  -------------------------------*/

/*-----------------------------------------
  Mendefinisikan pin kanal shift register
  -----------------------------------------*/
#define pinBuzz        9
#define pinRelay      10

/*------------------------------
  Pin Microcontroller NodeMCU
  ------------------------------*/
#define pinDHT        10 // pin SDD3
#define pinEcho       12 // pin D6
#define pinTrigger    14 // pin D5
#define pinLDR        A0 // pin A0
#define pinFan        15 // pin D8
#define pinIR         13 // pin D7
#define pinData       16 // pin D0
#define pinClock       2 // pin D4
#define pinLatch       0 // pin D3

/*---------------------------------------------------
  Login dan Password Access Point jaringan internet
  Sesuaikan nama WIFI dan PASSWORD Access Point Anda
  ----------------------------------------------------*/
const char* wifiName = "Tenda";
const char* wifiPass = "88888888";

/*------------------------------------------------------------------------------
  Login dan Password ke Message Broker Mosquitto
  User dan Password harus sesuai dengan setting pada Mosquitto
  Alamat IP Message Broker harus disesuaikan, di sini menggunakan 192.168.0.101
  ------------------------------------------------------------------------------*/
const char* brokerUser = "AdminMQTT";
const char* brokerPass = "pwd123";
const char* brokerHost = "192.168.0.103";

/*----------------------------------------------------
  Daftar nama Topic MQTT sebagai Publisher:
  1. Sebagai Publisher DHT11: Suhu & Kelembaban
  2. Sebagai Publisher LDR: Intensitas Cahaya
  3. Sebagai Publisher HC-SR04: Proximity Ultrasonic
  ----------------------------------------------------*/
const char* outTopicDHT  = "/dht";  // pub suhu dan kelembaban
const char* outTopicLDR  = "/ldr";  // pub intensitas cahaya
const char* outTopicSR04 = "/sr04"; // pub jarak penghalang dengan ultrasonic
const char* outTopicIR   = "/remoteir"; // pub remote IR

/*---------------------------------------------
  Daftar nama Topic MQTT sebagai Subscriber:
  1. Sebagai Subscriber FAN PWM
  2. Sebagai Subscriber Relay
  3. Sebagai Subscriber LED
  4. Sebagai Subscriber Buzzer / SPK Piezo
  ---------------------------------------------*/
const char* inTopicFAN   = "/fanpwm";
const char* inTopicRelay = "/relay";
const char* inTopicLED   = "/ledanim";
const char* inTopicPiezo = "/piezo";

/*------------------------------
  Inisialisasi instance/object &
  Deklarasi varibale global
  -------------------------------*/
// OLED 0.96"
Adafruit_SSD1306 display(128, 64, &Wire, -1);

// Konstruktor instance Shift register
ShiftRegister74HC595<2> srChannel(pinData, pinClock, pinLatch);

// Konstruktor instance Sensor DHT11
SimpleDHT11 dht11(pinDHT);

// Konstruktor instance Sensor IR Remote
IRrecv PenerimaIR(pinIR);
decode_results hasil;

// Varibale simpletimer / Timer interval
SimpleTimer TimerDHT, TimerLDR, TimerSR04;

// Deklarasi variable suhu dan kelembaban
byte humValid, tempValid;

// Deklarasi kode tombol remote
unsigned int KodeTombolRemote;

// Deklaasi client wifi
WiFiClient espClient;

// Deklarasi MQTT Client
PubSubClient client(espClient);
long lastReconnectAttempt = 0;

/*-----------------------------------
  Mode Running LED dengan type enum
  -----------------------------------*/
typedef enum {
  AnimKiriKanan,
  AnimKiriKananSendirian,
  AnimTengahSamping,
  AnimSampingTengah
} animLED;

void setup() {
  /*----------------------------------------------------
    Mengatur baudrate serial MCU.
    Baurate disesuaikan dengan baudrate serial monitor)
    ----------------------------------------------------*/
  Serial.begin(115200);
  Wire.begin();

  /*-------------------------
    Inisialisasi layar OLED
    -------------------------*/
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.clearDisplay();

  /*------------------------------------------------
    Membentuk koneksi ke jaringan WIFI Access Point
    ------------------------------------------------*/
  KoneksiWIFI();

  /*--------------------------------
    Koneksi TCP ke Broker MQTT
    port message broker adalah 1883
    --------------------------------*/
  client.setServer(brokerHost, 1883);
  client.setCallback(callback);

  // Mengaktifkan infra red receiver
  // kondisi listen
  PenerimaIR.enableIRIn();

  /*---------------------------------
    Mode pin sensor jarak ultrasonic
    ---------------------------------*/
  pinMode(pinTrigger, OUTPUT);
  pinMode(pinEcho, INPUT);

  /*-------------------
    Mode pin fan/kipas
    -------------------*/
  pinMode(pinFan, OUTPUT);

  /*-------------------
    Mode pin sensor DHT
    -------------------*/
  pinMode(pinDHT, INPUT);

  /*---------------------------------------------
    Set seluruh kanal shift register menjadi OFF
    ---------------------------------------------*/
  srChannel.setAllLow();

  /*----------------------------------------------------
    Atur interval pengecekan LDR, DHT, Jarak Ultrasonic
    ----------------------------------------------------*/
  TimerDHT.setInterval(1500);   // interval 1,5 detik
  TimerLDR.setInterval(300);    // interval 300 mili detik
  TimerSR04.setInterval(500);   // interval 0,5 detik
}

void loop() {
  /*---------------------------------------
    Koneksi ulang ke broker jika terputus
    ---------------------------------------*/
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  /*-------------------------------
    Publish kode keypad Remote IR
    -------------------------------*/
  BacaKodeRemoteIR();

  /*--------------------------------------------------------
    pembacaan sensor cahaya sesuai interval yang ditentukan
    kemudian mengirim datanya ke message broker
    --------------------------------------------------------*/
  if (TimerLDR.isReady()) {
    // kirim data LDR ke message broker
    snprintf (msg, MSG_BUFFER_SIZE, "%d", SensorLDR());
    client.publish(outTopicLDR, msg);

    // update tampilan ke OLED setelah terjadi perubahan nilai
    updateOLED();

    TimerLDR.reset();
  }

  /*-----------------------------------------
    pembacaan sensor jarak sesuai interval,
    kemudian mengirim data ke message broker
    -----------------------------------------*/
  if (TimerSR04.isReady()) {
    // Memanfaatkan memory buffer untuk mempercepat proses fetch data
    // dan  mengurangi waktu tunda saat data dikirim dari microcontroller
    // ke message broker MQTT mosquitto
    snprintf (msg, MSG_BUFFER_SIZE, "%d", SensorJarakUltraSonic());

    // Kirim data yang berada di dalam memory buffer ke message broker
    // sesuai dengan topic yag telah ditentukan dengan peritah client.publish
    client.publish(outTopicSR04, msg);

    // update tampilan ke OLED setelah terjadi perubahan nilai
    updateOLED();

    // timer di reset kembali ke counter 0
    TimerSR04.reset();
  }

  /*---------------------------------------------
    Membaca sensor suhu dan kelembaban DHT11
    sesuai interval yang ditetapkan.
    Kemudian mengirim datanya ke message broker
    sesuai dengan topic yang ditentukan
    ---------------------------------------------*/
  if (TimerDHT.isReady()) {

    // memanggil prosedur SensorDHT()
    // membaca suhu & kelembaban
    SensorDHT();

    // update tampilan ke OLED setelah terjadi perubahan nilai
    updateOLED();

    // Format data dibah menjadi sebuah bentuk array.
    // Hal ini karena dalam satu siklus waktu sensor DHT11
    // menghasilkan 2 nilai sekaligus, yaitu suhu dan kelembaban
    dhtData["suhu"] = tempValid;
    dhtData["kelembaban"] = humValid;

    // Untuk menghemat sumberdaya maka dalam sekali transmisi data ke message broker
    // nilai suhu dan kelembaban dikirim sekaligus, sehingga digunakan serialisasi data.
    // Format data semula berupa array menjadi format JSON dengan menggunakan fungsi serializeJson
    char buffer[256];
    size_t n = serializeJson(dhtData, buffer);

    // Kirim data yang berada di dalam memory buffer ke message broker
    // sesuai dengan topic yag telah ditentukan dengan peritah client.publish
    client.publish(outTopicDHT, buffer, n);

    // Timer di-reset kembali ke counter 0
    TimerDHT.reset();
  }
}

/*--------------------------------------------
  Fungsi mengendalikan kecepatan putaran FAN
  dengan cara PWM (pulse with modulation)
  --------------------------------------------*/
void KontrolKecepatanFan() {
  /*------------------------------------------------------------------------------------------------------------------
    referensi https://www.electronicwings.com/nodemcu/nodemcu-pwm-with-arduino-ide
    analogWriteFreq(500); secara default adalah 500
    analogWriteRange(100); secara default max bernilai 1023

    Memetakan antara jarak objek yang dideteksi oleh ultrasonic dengan RPM Fan
    Semakin dekat antara objek dengan sensor ultrasonic makan RPM Fan akan semakin cepat
    Di sini dipetakan jarak antara 1-30cm menjadi 1023-0 pwm analogread
    Sebagai pembuktian, silahkan dekat dan jauhkan benda dengan ultrasonic, kemudian perhatikan RPM Fan yang terjadi
    -------------------------------------------------------------------------------------------------------------------*/

  int speedfan = map(SensorJarakUltraSonic(), 30, 1, 0, 1023);
  analogWrite(pinFan, speedfan);
  Serial.println("Speed FAN: " + String(speedfan));
}

void SpeedFANSub(int speedFAN) {
  int speedfan = map(speedFAN, 1, 100, 0, 1023);
  analogWrite(pinFan, speedfan);
}

/*-------------------------------------
  Fungsi menghitung intensitas cahaya
  dengan sensor LDR secara analog
  -------------------------------------*/
int SensorLDR() {
  int nilaiAnalogLDR = analogRead(pinLDR);
  /*--------------------------------------------------------------------------------
    Referensi perhitung Lux
    https://arduinodiy.wordpress.com/2013/11/03/measuring-light-with-an-arduino/
    https://emant.com/316002
    VOut = nilaiAnalogLDR * (3.3 / 1023) = nilaiAnalogLDR * ‭‭0.0032258064516129‬
    Perhitungan ini tidak dikalibrasi
    ----------------------------------------------------------------------------------*/

  double Vout = nilaiAnalogLDR * 0.0032258064516129;
  int lux = 330 / (10 * ((3.3 - Vout) / Vout));
  Serial.println("Lux Intensity= " + String(int(lux)));
  return lux;
}

/*------------------------------------------
  Fungsi menghitung jarak benda penghalang
  Dengan Sensor Ultrasonic HR-SR04
  ------------------------------------------*/
int SensorJarakUltraSonic() {

  // Membersihkan pin pinTrigger selama 2 microdetik
  digitalWrite(pinTrigger, LOW);
  delayMicroseconds(2);

  // Set pinTrigger menjadi HIGH selama 10 microdetik
  digitalWrite(pinTrigger, HIGH);
  delayMicroseconds(10);
  digitalWrite(pinTrigger, LOW);

  // Menghitng jarak berdasarkan  waktu perjalanan
  // gelombang suara dalam mikrodetik
  long Durasi = pulseIn(pinEcho, HIGH);
  int JarakCM = Durasi * 0.034 / 2;    // Jarak dalam satuan CM
  int JarakInch = Durasi * 0.0133 / 2; // Jarak dalam satuan INCH

  Serial.println("Jarak Cm=" + String(JarakCM) + " Inch=" + String(JarakInch));
  return JarakCM;
}

/*------------------------------------
  Fungsi membaca suhu dan kelembaban
  dengan sensor DHT11
  ------------------------------------*/
void SensorDHT() {
  byte suhu = 0;
  byte hum = 0;

  int err = SimpleDHTErrSuccess;
  if ((err = dht11.read(&suhu, &hum, NULL)) != SimpleDHTErrSuccess)
  {
    Serial.print("Read DHT11 failed, err="); Serial.println(err);
    delay(100);
    return;
  }

  // Memastikan suhu dan kelembaban valid
  // bila bernilai 0 maka diambil dari nilai sebelumnya
  if (suhu != 0 || hum != 0) {
    tempValid = suhu;
    humValid = hum;
  }

  Serial.print("Sample OK: ");
  Serial.print((int)tempValid); Serial.print(" *C, ");
  Serial.print((int)humValid); Serial.println(" H");
}

/*-------------------------
  Fungsi suara Buzz Piezo
  --------------------------*/
void beepBuzz(unsigned char delayms) {
  srChannel.set(pinBuzz, HIGH);
  delay(delayms);
  srChannel.set(pinBuzz, LOW);
  delay(delayms);
}

/*----------------------
  Function Running LED
  -----------------------*/
void runningLED(animLED al, int tunda) {
  MematikanSemuaLED();
  switch (al) {
    //LED hidup dari kiri ke kanan
    case AnimKiriKanan:
      for (uint8_t i = 0; i <= 8; i++) {
        srChannel.set(i, HIGH);
        delay(tunda);
      }
      break;

    //LED hidup dari kanan ke kiri
    case AnimKiriKananSendirian:
      for (uint8_t i = 0; i <= 8; i++) {
        if (i > 0) srChannel.set(i - 1, LOW);
        srChannel.set(i, HIGH);
        delay(tunda);
      }
      break;

    case AnimTengahSamping:
      srChannel.set(4, HIGH);
      delay(tunda);
      srChannel.set(3, HIGH);
      srChannel.set(5, HIGH);
      delay(tunda);
      srChannel.set(2, HIGH);
      srChannel.set(6, HIGH);
      delay(tunda);
      srChannel.set(1, HIGH);
      srChannel.set(7, HIGH);
      delay(tunda);
      srChannel.set(0, HIGH);
      srChannel.set(8, HIGH);
      delay(tunda);
      break;

    //LEH hidup dari samping ke tengah
    case AnimSampingTengah:
      srChannel.set(0, HIGH);
      srChannel.set(8, HIGH);
      delay(tunda);
      srChannel.set(1, HIGH);
      srChannel.set(7, HIGH);
      delay(tunda);
      srChannel.set(2, HIGH);
      srChannel.set(6, HIGH);
      delay(tunda);
      srChannel.set(3, HIGH);
      srChannel.set(5, HIGH);
      delay(tunda);
      srChannel.set(4, HIGH);
      delay(tunda);
      break;
  }

  MematikanSemuaLED();
}

/*------------------------------------------------------
  Fungsi memetakan kode tombol/keypad remote IR.
  Berdasarkan kode yang dicatat tsb akan menjadi dasar
  engecekkan ke langkah berikutnya
  ------------------------------------------------------*/
void BacaKodeRemoteIR() {
  if (PenerimaIR.decode(&hasil)) {
    // Kualitas remote yang buruk menyebabkan debouncing
    // Decode kode tombol dengan lebar 8 saja yang akan diproses.
    // Setiap remote memiliki lebar kode valid masing-masing
    // Pastikan untuk dilakukan pemetaan kode terlebih dahulu
    String ngatasiDebounce = String((int)hasil.value, (unsigned char)DEC);
    if (ngatasiDebounce.length() == 8) {
      KodeTombolRemote = hasil.value;
      Serial.println("Kode remote: " + String(KodeTombolRemote));

      /// kirim kode keypad ke message broker
      snprintf (msg, MSG_BUFFER_SIZE, "Anda menekan keypad dengan kode: %d", KodeTombolRemote);
      client.publish(outTopicIR, msg);
    } else {
      // proses pemetaan kode tombol ditampilkan pada serial monitor
      unsigned int kodeGagal = hasil.value;
      Serial.println("Length Decode: " + String(ngatasiDebounce.length()));
      Serial.println("Kode remote tidak diproses: " + String(kodeGagal));
    }
    PenerimaIR.resume();
  }
  delay(100);
}

/*--------------------
  Mematikan semua LED
  ---------------------*/
void MematikanSemuaLED() {
  //Set seluruh kanal LED 0 - LED 8 menjadi OFF
  for (uint8_t i = 0; i <= 8; i++) {
    srChannel.set(i, LOW);
  }
}

/*-----------------------------------------
  Mengendalikan LED menggunakan Remote IR
  ------------------------------------------*/
void HidupkanLEDDenganIR() {
  MematikanSemuaLED();

  if (KodeTombolRemote == 1303529910) {
    // hidupkan LED 1
    // dengan tombol 1
    srChannel.set(0, HIGH);
  } else if (KodeTombolRemote == 1303562550) {
    // hidupkan LED 1, 2
    // dengan tombol 2
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
  } else if (KodeTombolRemote == 1303524300) {
    // hidupkan LED 1, 2, 3
    // dengan tombol 3
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
  } else if (KodeTombolRemote == 1303540110) {
    // hidupkan LED 1, 2, 3, 4
    // dengan tombol 4
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
  } else if (KodeTombolRemote == 1303572750) {
    // hidupkan LED 1, 2, 3, 4, 5
    // dengan tombol 5
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
    srChannel.set(4, HIGH);
  } else if (KodeTombolRemote == 1303516140) {
    // hidupkan LED 1, 2, 3, 4, 5, 6
    // dengan tombol 6
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
    srChannel.set(4, HIGH);
    srChannel.set(5, HIGH);
  } else if (KodeTombolRemote == 1303531950) {
    // hidupkan LED 1, 2, 3, 4, 5, 6, 7
    // dengan tombol 7
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
    srChannel.set(4, HIGH);
    srChannel.set(5, HIGH);
    srChannel.set(6, HIGH);
  } else if (KodeTombolRemote == 1303564590) {
    // hidupkan LED 1, 2, 3, 4, 5, 6, 7, 8
    // dengan tombol 8
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
    srChannel.set(4, HIGH);
    srChannel.set(5, HIGH);
    srChannel.set(6, HIGH);
    srChannel.set(7, HIGH);
  } else if (KodeTombolRemote == 1303520220) {
    // hidupkan LED 1, 2, 3, 4, 5, 6, 7, 8, 9
    // dengan tombol 9
    srChannel.set(0, HIGH);
    srChannel.set(1, HIGH);
    srChannel.set(2, HIGH);
    srChannel.set(3, HIGH);
    srChannel.set(4, HIGH);
    srChannel.set(5, HIGH);
    srChannel.set(6, HIGH);
    srChannel.set(7, HIGH);
    srChannel.set(8, HIGH);
  }
}

/*----------------------------------------
  Fungsi koneksi jaringan ke Access Point
  ----------------------------------------*/
void KoneksiWIFI() {
  Serial.print("Connecting to ");
  Serial.println(wifiName);

  display.clearDisplay();

  display.setCursor(0, 0);
  display.println("Connecting to WiFi...");
  display.display();

  // Memposisikan NodeMCU sebagai station
  // NodeMCU dihubungkan ke Access Point
  WiFi.mode(WIFI_STA);
  WiFi.begin(wifiName, wifiPass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // NodeMCU telah terhubung ke Access Point
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Tampilkan informasi IP
  display.setCursor(0, 12); display.println("WiFi connected");
  display.display(); delay(1000);
  display.setCursor(0, 24); display.println(WiFi.localIP());
  display.display(); delay(1000);
  display.setCursor(0, 36); display.println("IoT Smart Device");
  display.display(); delay(1000);
  display.setCursor(0, 48); display.println("Development Board");
  display.display(); delay(3000);
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client", brokerUser, brokerPass)) {

      Serial.println("connected");

      // memastikan bahwa IoT Development Board
      // telah meng-subscribe semua aktuator
      client.subscribe(inTopicFAN);
      client.subscribe(inTopicRelay);
      client.subscribe(inTopicLED);
      client.subscribe(inTopicPiezo);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");

      delay(5000);
    }
  }
}

/*-------------------------------------------------------------
   Fungsi callback adalah 'listen' jika ada data
   yang diterima oleh server(NodeMCU). Kemudian menyimpan data
   ke variable berdasarkan nama TOPIC yang bersesaian
  -------------------------------------------------------------*/
void callback(char* topic, byte* payload, unsigned int length) {
  // variable StringPayload untuk menyimpan konten paket data yang diterima
  String StringPayload = "";

  // Menjadikan setiap character yang diterima menjadi string utuh
  // melalui proses penggabungan character
  for (int i = 0; i < length; i++) {
    StringPayload += (char)payload[i];
  }

  Serial.println("TOPIC: " + String(topic));
  Serial.println("PAYLOAD: " + String(StringPayload));

  // Mem-filter data berdasarkan nama topic nya masing-masing
  if (strcmp(topic, inTopicFAN) == 0) {
    // Topic: "/fanpwm"
    SpeedFANSub(StringPayload.toInt());
  } else if (strcmp(topic, inTopicRelay) == 0) {
    // Topic: "/relay"
    if (StringPayload == "ON") {
      // Mengaktifkan Relay jika StringPayload = "ON"
      srChannel.set(pinRelay, HIGH);
    } else {
      // Menonaktifkan Relay jika StringPayload = "OFF"
      srChannel.set(pinRelay, LOW);
    }
  } else if (strcmp(topic, inTopicPiezo) == 0) {
    // Topic: "/piezo"
    if (StringPayload == "ON") {
      // Mengaktifkan Buzz jika StringPayload = "ON"
      srChannel.set(pinBuzz, HIGH);
    } else {
      // Menonaktifkan Buzz jika StringPayload = "OFF"
      srChannel.set(pinBuzz, LOW);
    }
  } else if (strcmp(topic, inTopicLED) == 0) {
    // Topic: "/ledanim"
    for (int i = 0; i <= 8; i++) {
      // Menset status channel shift register mulai dari 0-8 menjadi LOW
      srChannel.set(i, LOW);
    }

    for (int i = 1; i <= StringPayload.toInt(); i++) {
      // Menset status channel shift register mulai dari 0
      // sampai nilai maks yang diterima dari payload menjadi HIGH
      srChannel.set(i - 1, HIGH);
    }
  }
}

void updateOLED() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);

  display.setCursor(0, 0);    display.print("Light");
  display.setCursor(75, 0);   display.print(String(SensorLDR()));
  display.setCursor(100, 0);  display.print("Lux");

  display.setCursor(0, 14);   display.print("Temperature");
  display.setCursor(75, 14);  display.print(String(tempValid));
  display.drawCircle(100, 13, 2, SSD1306_WHITE);
  display.setCursor(105, 14); display.print("C");

  display.setCursor(0, 28);   display.print("Humidity");
  display.setCursor(75, 28);  display.print(String(humValid));
  display.setCursor(100, 28); display.print("H");

  display.setCursor(0, 42);   display.print("Distance");
  display.setCursor(75, 42);  display.print(String(SensorJarakUltraSonic()));
  display.setCursor(100, 42); display.print("CM");

  display.display();
}

Pengembangan Sisi Aplikasi Desktop

Di sini kita memerlukan IDE Visual Studio dengan bahasa pemgraman C# sebagai pilihannya. Dalam hal ini saya menggunakan Visual Studio 2017, jika Anda belum memilikinya dapat mendownloadnya versi communitiy atau gratis di link berikut https://my.visualstudio.com/Downloads?q=visual studio 2017&wt.mc_id=o~msft~vscom~older-downloads.

Setelah sukses menginstalasi, langkah selanjutnya bisa mengikuti tahapannya berikut ini:

Buka menu File > New Project

Tentukan jenis bahasa pemrogramannya menjadi C#, tentukan nama projek dan lokasi dimana file akan ditempatkan seperti tampak ilustrasi di bawah ini, kemudian klik tombol OK.

Setelah itu akan muncul satu window form bernama form1. Dari window form1 tersebut kita akan menambahkan beberapa control/widget yang diperlukan.

Pertama adalah kita menambahkan sebuah TabControl dengan tiga TabPage di dalamnya. Untuk menambahkan control atau widget dengan cara men-drag-drop widget dari toolbox ke window form.

Ubah Property Text pada masing-masing tabPage, dengan memilih salah satu tabPage. Klik pada body tabPage, kemudian ubah Property Text-nya. Hal ini akan mengubah title masing-masing tabPage. Lakukan cara yang sama untuk semua tabPage.

  • Text tabPage1 = “Actuator”

  • Text tabPage2 = “Sensor LDR & Ultrasonic”

  • Text tabPage3 = “Sensor DHT11 (Suhu & Kelembaban)”

Ganti nama Form yang semula “Form1” menjadi “frmIoTDashboard”

Bila ada permintaan konfirmasi, pilih Yes.

Kebutuhan Library

Sebelum melangkah lebih jauh, kita perlu menambahkan beberapa library ke dalam projek yang kita bangun. Library tersebut antara lain:

  • Library LiveCharts untuk menangani visualisasi grafis.

  • Library M2MQTT untuk menangani komunikasi MQTT

  • Library Newtonsoft.JSON untuk menangani data format JSON

Sama seperti pada lingkungan pengembangan pemrograman modern lainnya, Visual Studio juga menyediakan koneksi repository seperti git atau npm. Di sini menggunakan NuGet.org.

Buka menu Project > Manage Nuget Package.

Pilih kata kunci newtonsoft pada field pencarian, pilih library Newtonsoft.Json, terakhir klik pada tombol Install. Lakukan hal yang sama untuk dua library lainnya sesuai gambar ilustrasi di bawah ini

Untuk mengetahui apakah semua library terinstall dengan baik dan benar, silahkan pilih tab Installed.

Tab Page “Actuator

Sekarang kita kembali ke tabPage Actuator. Di sini kita akan menambahkan beberapa control, antara lain

Adapun rancangan yang dibuat adalah sebagai berikut

  • CheckBox Relay bertujuan untuk mengaktifkan dan menonaktifkan Relay pada IoT Smart Development Board

  • CheckBox Buzzer bertujuan untuk mengaktifkan dan menonaktifkan Buzzer/Speaker pada IoT Smart Development Board

  • TrackBar Led Controller bertujuan untuk mengendalikan seberapa banya LED yang aktif pada IoT Smart Development Board

  • TrackBar Speed FAN, bertujuan untuk mengendalikan kecepatan putar FAN pada IoT Smart Development Board.

Note. Anda tidak perlu kuatir jika mengalami kesalahan, karena diakhir ulasan akan disertai dengan link source code lengkap yang bisa dicoba.

Tab Page "Sensor LDR & Ultrasonic"

Pilih terlebih dahulu tabPage2 atau tabPage “Sensor LDR & Ultrasonic”. Di dalamnya terdapat dua groupBox dimana setiap groupBox berisi dua chart cartesian yang akan memvisualisasikan data intensitas cahaya dalam satuan LUX yang diterima dari sensor LDR dan data jarak penghalang dalam satuan cm yang diterima dari sensor Ultrasonic HC-SR04.

Note. Jika control LiveChart tidak tampak pada Window Toolbox maka simpan terlebih dahulu projek Anda, kemudian tutup dan buka kembali visual studio projek Anda.

Tab Page “Sensor DHT11 (Suhu & Kelembaban)”

Perancangan berikutnya adalah tabPage“Sensor DHT11 (Suhu & Kelembaban)”. Di sini kita akan menambahkan dua groupBox, dimana setiap groupBox berisi chart SolidGauge dari liveCharts. Kedua SolidGauge akan memvisualisasikan nilai suhu dan kelembaban yang diterima dari sensor DHT11.

Control “Timer”

Untuk memvisualisasikan data-data sensor secara real-time pada grafis maka diperlukan control timer. Control timer tersebut akan bekerja sesuai interval yang ditentukan (secara ideal disesuaikan dengan interval pengiriman data sensor di sisi IoT Development Board) untuk membaca data yang diterima oleh event subscriber MQTT.

Di sini dibutuhkan tiga timer, antara lain:

  • timerLDR, control timer sensor LDR dengan interval eksekusi 300 milidetik.

  • timerSR04, control timer sensor pengukur jarak penghalang ultrasonic HC-SR04 dengan interval eksekusi 0,5 detik (500 milidetik).

  • timerDHT, control timer sensor DHT untuk mengukur suhu dan kelembaban dengan interval eksekusi 1,5 detik (1500 milidetik).

Untuk menambahkan control timer, cukup drag-drop control timer ke form windows. Kemudian beri nama dengan timerLDR, timerSR04, dan timerDHT. Sedangkan pengaturan interval dan kapan aktifasi timer akan dilakukan secara kode program.

Kode Program C#.NET Untuk memulai memprogram C# Winform. Pilih control CheckBox cbRelay dua kali, atau kilik sekali kemudian perhatikan pada window event khusunya event CheckedChange. Dari situ akan diarahkan ke mode coding.

Dengan cara yang hampir serupa akan berlaku pada semua event control yang memang dibutuhkan. Anda tinggal memperhatikan nama setiap event yang terdapat dalam contoh sudah dapat menebak event apa yang diperlukan.

Adapun kode program lengkapnya adalah sebagai berikut:

/******************************************************************************
 *  IoT Smart Device Development Board - IoT & C#.NET Winform                 *
 *  Dodit Suprianto | DSP-TECH | https://dsp-tech.gitbook.io                  *
 *  https://doditsuprianto.blogspot.com/ | https://github.com/doditsuprianto  *
 *  Email: doditsuprianto@gmail.com                                           *
 ******************************************************************************/
using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media;
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using Newtonsoft.Json;
using System.Windows;

namespace DemoIoTCSharp
{
    public partial class frmIoTDashboard : Form
    {
        // class data json yang akan di deserialize
        public class DHT
        {
            public UInt32 suhu { get; set; }
            public UInt32 kelembaban { get; set; }
        }

        // Status relay dan buzzer, default OFF
        private String RelayState = "OFF";
        private String BuzzerState = "OFF";

        // Alamat IP dan Port Broker MQTT
        // Silahkan disesuaikan dengan alamat IP Broker MQTT Anda
        private const String IPBroker = "192.168.0.102";
        private const UInt16 PORT = 1883;

        // User & Password Broker MQTT 
        private const String userBroker = "AdminMQTT";
        private const String pwdBroker = "pwd123";

        // Topik Sensor
        private const String outTopicDHT = "/dht";
        private const String outTopicLDR = "/ldr";
        private const String outTopicSR04 = "/sr04";

        // Topik Aktuator
        private const String inTopicFAN = "/fanpwm";
        private const String inTopicRelay = "/relay";
        private const String inTopicLED = "/ledanim";
        private const String inTopicPiezo = "/piezo";

        static UInt32 nilaiLDR;
        static UInt32 nilaiSR04;
        static UInt32 nilaiSuhu;
        static UInt32 nilaiKelembaban;

        // Constructor koneksi client dengan Broker MQTT
        MqttClient client = new MqttClient(IPBroker, PORT, false, null, null, MqttSslProtocols.None);

        //public SeriesCollection SeriesCollection { get; set; }
        public ChartValues<ObservableValue> ValuesLDR { get; set; }
        public ChartValues<ObservableValue> ValuesSR04 { get; set; }

        public frmIoTDashboard()
        {
            InitializeComponent();

            // Text title Form
            this.Text = "IoT Dashboard System";

            // Text title tab page
            tabControl1.TabPages[0].Text = "Actuator";
            tabControl1.TabPages[1].Text = "Sensor LDR & Ultrasonic";
            tabControl1.TabPages[2].Text = "Sensor DHT11 (Suhu & Kelembaban)";

            // Text title checkbox
            cbRelay.Text = "Relay ON/OFF";
            cbBuzzer.Text = "Buzzer ON/OFF";

            // Mengubah warna background trackbar menjadi putih
            System.Drawing.SolidBrush putih = new System.Drawing.SolidBrush(System.Drawing.Color.White);
            SliderLED.BackColor = putih.Color;
            SliderFAN.BackColor = putih.Color;

            // Inisialisasi label text FAN dan LED
            lblValueFAN.Text = "0";
            lblValueLED.Text = "0";

            // Menjalin koneksi dengan Broker MQTT
            client.Connect(Guid.NewGuid().ToString(), userBroker, pwdBroker);

            // Membuat event dan memanggil method client_MqttMsgPublishReceived secara otomatis
            // ketika ada pesan MQTT yang masuk
            client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;

            // Mendeklarasikan subscriber MQTT
            client.Subscribe(new string[] { outTopicLDR }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
            client.Subscribe(new string[] { outTopicSR04 }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
            client.Subscribe(new string[] { outTopicDHT }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });

            // Inisialisasi slider/trackbar LED
            SliderLED.Minimum = 0;
            SliderLED.Maximum = 9;
            SliderLED.Value = 0;

            // Inisialisasi slider/trackbar FAN
            SliderFAN.Minimum = 0;
            SliderFAN.Maximum = 100;
            SliderFAN.Value = 0;
            SliderFAN.TickFrequency = 10;

            // Constructor nilai chart
            ValuesLDR = new ChartValues<ObservableValue> { };
            ValuesSR04 = new ChartValues<ObservableValue> { };

            //chartIntensitasCahaya.LegendLocation = LegendLocation.Right;

            // Menentukan interval dan mengaktifkan timer LDR
            timerLDR.Interval = 300;
            timerLDR.Enabled = true;

            // Menentukan interval dan mengaktifkan timer Ultrasonic
            timerSR04.Interval = 500;
            timerSR04.Enabled = true;

            // Menentukan interval dan mengaktifkan timer DHT
            timerDHT.Interval = 1500;
            timerDHT.Enabled = true;
        }

        static void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
        {
            // Memeriksa topic dan menangkap data untuk diolah lebih lanjut
            if (e.Topic == outTopicDHT)
            {
                // Men-deserialisasi data json menjadi variable suhu dan kelembaban
                DHT dht = JsonConvert.DeserializeObject<DHT>(Encoding.UTF8.GetString(e.Message));
                nilaiSuhu = dht.suhu;
                nilaiKelembaban = dht.kelembaban;
            }
            else if (e.Topic == outTopicLDR)
            {
                // Menampung data LDR
                nilaiLDR = Convert.ToUInt32(Encoding.UTF8.GetString(e.Message));
            }
            else if (e.Topic == outTopicSR04)
            {
                //Menampung data jarak ultrasonic
                nilaiSR04 = Convert.ToUInt32(Encoding.UTF8.GetString(e.Message));
            }
        }

        private void cbRelay_CheckedChanged(object sender, EventArgs e)
        {
            // Mengecek status checkbox Relay
            if (cbRelay.Checked)
            {
                // Jika checkbox Relay dicentang maka status relay ON
                RelayState = "ON";
            }
            else
            {
                // Jika checkbox Relay tidak dicentang maka status relay OFF
                RelayState = "OFF";
            }

            // Memastikan pesan menjadi string sebelum di publish
            string strValue = Convert.ToString(RelayState);

            // publish pesan dengan topik inTopicRelay, QoS 2
            client.Publish(inTopicRelay, Encoding.UTF8.GetBytes(strValue), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
        }

        private void cbBuzzer_CheckedChanged(object sender, EventArgs e)
        {
            // Mengecek status checkbox Buzer
            if (cbBuzzer.Checked)
            {
                // Jika checkbox Buzzer dicentang maka status buzzer ON
                BuzzerState = "ON";
            }
            else
            {
                // Jika checkbox Buzzer dicentang maka status buzzer OFF
                BuzzerState = "OFF";
            }

            // Memastikan pesan menjadi string sebelum di publish
            string strValue = Convert.ToString(BuzzerState);

            // publish pesan dengan topik inTopicPiezo, QoS 2
            client.Publish(inTopicPiezo, Encoding.UTF8.GetBytes(strValue), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
        }

        private void frmIoTDashboard_Load(object sender, EventArgs e)
        {
            //*******************************
            // Timeseries Intensitas Cahaya
            // Sensor LDR
            //*******************************
            chartIntensitasCahaya.Series.Add(new LineSeries
            {
                Values = ValuesLDR,
                StrokeThickness = 2,
                PointGeometrySize = 10,
                DataLabels = true,
                //Stroke = Brushes.Yellow,
            });

            chartIntensitasCahaya.AxisX.Add(new LiveCharts.Wpf.Axis
            {
                Title = "Waktu",
            });


            chartIntensitasCahaya.AxisY.Add(new LiveCharts.Wpf.Axis
            {
                Title = "Intensitas Cahaya",
                LabelFormatter = x => x + " lux"
            });


            //******************************
            // Timeseries Jarak Penghalang
            // Sensor HC-SR04
            //******************************
            chartJarakPenghalang.Series.Add(new LineSeries
            {
                Values = ValuesSR04,
                StrokeThickness = 2,
                PointGeometrySize = 10,
                DataLabels = true,
                Stroke = Brushes.Red,
                Fill = Brushes.Transparent
            });

            chartJarakPenghalang.AxisX.Add(new LiveCharts.Wpf.Axis
            {
                Title = "Waktu",
            });


            chartJarakPenghalang.AxisY.Add(new LiveCharts.Wpf.Axis
            {
                Title = "Jarak Penghalang",
                LabelFormatter = x => x + " cm"
            });
        }

        private void frmIoTDashboard_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Memutuskan koneksi dengan Broker MQTT ketika Form Closed
            client.Disconnect();

            // Memnonaktifkan timer ketika Form Closed
            timerDHT.Enabled = false;
            timerLDR.Enabled = false;
            timerSR04.Enabled = false;
        }

        private void timerLDR_Tick(object sender, EventArgs e)
        {
            // Maksimum 30 data ditampilkan dalam satu chart
            if (ValuesLDR.Count() > 30)
            {
                // Jika lebih dari 30 data maka data index ke-0 dihapus
                // berarti shift data ke kiri atau bergeser ke kiri
                ValuesLDR.RemoveAt(0);
            }
            else
            {
                // Jika kurang dari 30 data maka akan ditambah di sebelah kanan
                ValuesLDR.Add(new ObservableValue(nilaiLDR));
            }
        }

        private void timerSR04_Tick(object sender, EventArgs e)
        {
            // Maksimum 20 data ditampilkan dalam satu chart
            if (ValuesSR04.Count() > 20)
            {
                // Jika lebih dari 20 data maka data index ke-0 dihapus
                // berarti shift data ke kiri atau bergeser ke kiri
                ValuesSR04.RemoveAt(0);
            }
            else
            {
                // Jika kurang dari 20 data maka akan ditambah di sebelah kanan
                ValuesSR04.Add(new ObservableValue(nilaiSR04));
            }
        }

        private void timerDHT_Tick(object sender, EventArgs e)
        {
            // Gauge suhu
            solidGaugeSuhu.Uses360Mode = true;
            solidGaugeSuhu.From = 0;
            solidGaugeSuhu.To = 100;
            solidGaugeSuhu.Value = nilaiSuhu;

            // Gauge kelembaban
            solidGaugeKelembaban.From = 0;
            solidGaugeKelembaban.To = 100;
            solidGaugeKelembaban.Value = nilaiKelembaban;
            solidGaugeKelembaban.Base.LabelsVisibility = Visibility.Hidden;
            solidGaugeKelembaban.Base.GaugeActiveFill = new LinearGradientBrush
            {
                GradientStops = new GradientStopCollection
                {
                    new GradientStop(Colors.Yellow, 0),
                    new GradientStop(Colors.Orange, .5),
                    new GradientStop(Colors.Red, 1)
                }
            };
        }

        private void sliderLED_Scroll(object sender, EventArgs e)
        {
            // Mengkonversi value slider menjadi string
            String strValue = SliderLED.Value.ToString();

            //Menampilkan value slider pada label lblValueLED
            lblValueLED.Text = strValue;

            // Mengirim pesan ke Broker MQTT
            client.Publish(inTopicLED, Encoding.UTF8.GetBytes(strValue), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
        }

        private void sliderFAN_Scroll(object sender, EventArgs e)
        {
            int value = (sender as TrackBar).Value;
            double indexDbl = (value * 1.0) / SliderFAN.TickFrequency;
            int index = Convert.ToInt32(Math.Round(indexDbl));
            SliderFAN.Value = SliderFAN.TickFrequency * index;

            // Mengkonversi value slider menjadi string
            String strValue = SliderFAN.Value.ToString();

            //Menampilkan value slider pada label lblValueFAN
            lblValueFAN.Text = strValue;

            // Mengirim pesan ke Broker MQTT
            client.Publish(inTopicFAN, Encoding.UTF8.GetBytes(strValue), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
        }
    }
}

Download Source Code IoT dan C# Desktop Winform: https://github.com/doditsuprianto/IoT-Development-Board-DSP-TECH/raw/master/DemoIoTCSharp.zip

Demo Video Projek

Last updated

Was this helpful?