Mengirim data melalui jaringan Wifi menggunakan ESP01 dengan metode POST (kasus IOT ThingSpeak)

ESP8266 adalah modul Wi-Fi dengan fitur TCP/IP yang lengkap dan bisa dihubungkan dengan mikrokontroler melalui protokon AT-Command. TCP/IP (Transmission Control Protocol / Internet Protocol)  adalah protokol komunikasi antara dua perangkat yang terhubung dengan sebuah metode paket termasuk metode pengalamatan dan metode transmisi dalam jaringan internet.

Dalam komunikasi TCP/IP dikenal beberapa metode request/permintaan yaitu : HEAD, GET, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH.

Metode POST  sering digunakan untuk mengirimkan data-data ter-enkripsi dan langsung ke server sehingga lebih menjamin kerahasiaan data. berbeda dengan metode GET yang mengirimkan requeat dalam bentuk URL.

Skematik ESP8266/ESP01 (+Arduino Uno) request metode post:

Sketch/koding pemrograman komunikasi ESP01 + arduino dengan server menggunakan metode POST :

char ssid[]             = "xxxx";            // your network SSID (name)
char pass[]             = "xxxxxxxx";        // your network password

char server[]           = "api.thingspeak.com";
byte port               = 80;
char APIKey[]           = "XXXXXXXXXXXXXX";
uint32_t periodeKirim   = 20000;

#include <WiFiEsp.h>
#include <SoftwareSerial.h>

SoftwareSerial SerialEsp(10, 11);
WiFiEspClient client;
int status = WL_IDLE_STATUS;     // the Wifi radio's status
uint32_t millisKirim;
bool statusKirim;

void setup()
{
  Serial.begin(9600);
  Serial.println("Mengirim data melalui jaringan Wifi menggunakan ESP01 dengan metode POST");
  Serial.println("https://www.semesin.com/project/");
  Serial.println();

  SerialEsp.begin(115200);
  SerialEsp.println("AT+UART_DEF=9600,8,1,0,0");
  delay(500);
  SerialEsp.begin(9600);

  WiFi.init(&SerialEsp);

  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
  }
  millisKirim = millis();
}

void loop()
{
  if (millisKirim < millis())
  {
    millisKirim = millis() + periodeKirim;
    
    // hubungkan ke jaringan wifi jika belum tersambung atau terputus (auto reconnect)
    if (WiFi.status() != WL_CONNECTED)
    {
      Serial.print("Menghubungkan ke jaringan SSID: ");
      Serial.println(ssid);
      while (WiFi.status() != WL_CONNECTED)
      {
        WiFi.begin(ssid, pass);
        Serial.print(".");
        delay(5000);
      }
      printWifiStatus();
      Serial.println("Berhasil terhubung ke jaringan");
    }


    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println();
      Serial.println("Menghubungkan dengan server...");
      if (client.connect(server, port))
      {
        Serial.println();
        Serial.println("Terhubung dengan server.");

        char content[30];
        sprintf(content, "field1=%d", millis() % 100);

        client.println("POST /update HTTP/1.1");
        client.println("Host: api.thingspeak.com");
        client.println("User-Agent: tslib-arduino/1.5.0");
        client.print("X-THINGSPEAKAPIKEY: ");
        client.println(APIKey);
        client.println("Content-Type: application/x-www-form-urlencoded");
        client.print("Content-Length: ");
        client.println(strlen(content));
        client.println("Connection: close");
        client.println();

        client.print(content);

        statusKirim = true;
      }
    }
  }

  if (statusKirim)
  {
    while (client.available())
    {
      char c = client.read();
      Serial.write(c);
    }

    if (!client.connected()) 
    {
      Serial.println();
      Serial.println("Memutuskan hubungan dengan server...");
      delay(10);
      client.stop();

      statusKirim = false;
    }
  }
}


void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

Library :

 

Hex Serial Monitor dengan Visual C#

Serial monitor seperti pada putty, hyper terminal, arduino IDE sangat bermanfaat untuk memonitor atau mengontrol perangkat yang sedang dikembangkan/development. Serial monitor juga bisa digunakan untuk proses debug untuk mencari kesalahan algoritma, misalnya dengan memberikan informasi nilai variabel ataupun informasi step by step percabangan.

Penggunaan serial monitor untuk keperluan debug haruslah memberikan informasi detail. Proses debug perangkat digital sangat berhubungan dengan format hexadesimal (hex) dan komunikasi binary.

Fitur Hex Serial Monitor:

  1. Data Serial yang diterima di tampilkan dalam format tabel hexa, juga disertai string.
  2. Baris baru dengan pilihan 0x00 dan 0x0A
  3. Kirim data serial dalam format :
    • String
    • 2 karakter, contohnya 12AB34BC = 0x12, 0xAB, 0x34, 0xBC
    • separator spasi, contoh : 12 AB 34 BC
    • separator koma, contoh : 12, AB,34, BC
    • awalan 0x
    • awalan $, contoh : $12$AB $34  $BC

Fungsi utama software ini adalah untuk berkomunikasi melalui serial port dalam mode binary dan ditampilkan dalam format hexa desimal.

tampilan Hexadecimal serial monitor menggunakan c#:

 

Kode software visual c# hex serial monitor:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace Hex_serial_monitor
{
    public partial class Form1 : Form
    {
        public int baris = 0;
        public int kolom = 0;
        String strKolom;


        public Form1()
        {
            InitializeComponent();
        }

        public delegate void AddDataDelegate();
        public AddDataDelegate delegateSerial;

        private void Form1_Load(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();

            comboBoxCOMPort.Items.Clear();
            foreach (string port in ports)
            {
                comboBoxCOMPort.Items.Add(port);
            }
            if (comboBoxCOMPort.Items.Count > 0)
            {
                comboBoxCOMPort.SelectedIndex = 0;
            }

            this.delegateSerial = new AddDataDelegate(updateSerial);

            dataGridView1.Rows.Clear();
            dataGridView1.Rows.Add();
            dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
            strKolom = "";

            radioButtonAscii.Checked = true;
            //checkBox0.Checked = true;

        }
        private void updateSerial()
        {
            while (serialPort1.BytesToRead > 0)
            {
                int data = serialPort1.ReadByte();
                dataGridView1.Rows[baris].Cells[kolom + 1].Value = data.ToString("X2");

                if ((data >= ' ') && (data < 128))
                {

                    strKolom += (char)data;
                }
                else
                {
                    strKolom += ' ';
                }


                dataGridView1.Rows[baris].Cells[17].Value = strKolom;

                kolom++;
                if (((data == 0) && (checkBox0.Checked)) ||
                    ((data == 0x0A) && (checkBox0A.Checked)) || 
                    (kolom == 16))
                {
                    kolom = 0;
                    baris++;
                    dataGridView1.Rows.Add();
                    dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
                    strKolom = "";
                }
                if (checkBoxAutoScroll.Checked)
                {
                    dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
                }
            }
            textBoxKirim.Focus();
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            dataGridView1.Invoke(this.delegateSerial, new Object[] { });
        }

        private void buttonClear_Click(object sender, EventArgs e)
        {
            dataGridView1.Rows.Clear();
            kolom = 0;
            baris = 0;
            dataGridView1.Rows.Add();
            dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
            strKolom = "";
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (buttonConnect.Text == "Connect")
            {
                serialPort1.PortName = comboBoxCOMPort.Text;
                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    buttonConnect.Text = "Disconnect";
                }
            }
            else
            {
                if (serialPort1.IsOpen)
                {
                    serialPort1.Close();
                    buttonConnect.Text = "Connect";
                }

            }

        }

        private void buttonKirim_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                if (radioButtonAscii.Checked)
                {
                    serialPort1.WriteLine(textBoxKirim.Text + "\r\n");
                }
                else if (radioButton2Character.Checked)
                {
                    if (textBoxKirim.Text.Length % 2 == 0)
                    {
                        int jumlahHex = textBoxKirim.Text.Length / 2;
                        byte[] hex = new byte[jumlahHex];

                        for (int i = 0; i < jumlahHex; i++)
                        {
                            String str2Hex = textBoxKirim.Text.Substring(i * 2, 2);
                            hex[i] = (byte)int.Parse(str2Hex, System.Globalization.NumberStyles.HexNumber);

                        }
                        serialPort1.Write(hex, 0, jumlahHex);
                    }
                }
                else if (radioButtonSpace.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split(' ');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButtonComma.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split(',');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButton0x.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split('x');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButtonDollar.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split('$');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                
            }
        }
    }
}

 

File Hexa Serial monitor berbasis c#

 

Media pembelajaran polygon berbasis Delphi

Poligon adalah geometri dengan banyak sudut seperti segitiga, segi empat dan nonagon dan lainnya. poligon terdiri atas beberapa titik atau vertex yang terhubung menjadi sebuah bentuk geometri. Poligon memiliki penamaan khusus sesuai jumlah sudutnya.

Poligon merupakan geometri yang dapat dikembangkan secara luas sehingga digunakan di berbagai bidang.

Untuk memudahkan mengenal atau memperkenalkan polygon, salah satu media yang bisa digunakan adalah pc/laptop. Fitur dasar seperti create dan modify adalah sebagai langkah awal  untuk membangun sebuah media pembelajaran geometri. Selanjutnya  bisa dikembangkan menjadi lebih fungsional sesuai kebutuhan pengembangannya.

Screenshoot media pembelajaran polygin menggunakan delphi :

Kode delphi pengembangan media belajar geometri polygon :

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Buttons, Grids;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Image1: TImage;
    GroupBox1: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox2: TGroupBox;
    LabelPosisi: TLabel;
    ButtonClose: TButton;
    LabelVertex: TLabel;
    GroupBox4: TGroupBox;
    StringGrid1: TStringGrid;
    ButtonDelete: TButton;
    ButtonDraw: TButton;
    ButtonEdit: TButton;
    ButtonInsert: TButton;
    ButtonClear: TButton;
    ButtonSave: TButton;
    ButtonOpen: TButton;
    Label1: TLabel;
    procedure ButtonDrawClick(Sender: TObject);
    procedure ButtonEditClick(Sender: TObject);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Image1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ButtonCloseClick(Sender: TObject);
    procedure ButtonSaveClick(Sender: TObject);
    procedure ButtonLoadClick(Sender: TObject);
    procedure refreshCanvas();
    procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure ButtonDeleteClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure ButtonInsertClick(Sender: TObject);
    procedure ButtonClearClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type Point = record
 X : integer;
 Y : integer;
end;

var
  Form1: TForm1;
  modeOperasi : integer;
  posisi : Point;
  tinggiCanvas : integer;
  startPolygon : boolean;
  polygon : array[0..20] of Point;
  polygonCount : integer;
  editIndex : integer;

const
  modeIdle = 0;
  modeDraw = 1;
  modeEdit = 3;
  modeInsert = 4;

implementation

{$R *.dfm}

procedure TForm1.refreshCanvas();
var
  i : integer;
begin
  StringGrid1.RowCount := polygonCount + 1;

  i := 0;
  while true do
  begin
    if polygon[i].X = -1 then
      break;
    StringGrid1.Cells[0,i+1] := inttostr(i+1);
    StringGrid1.Cells[1,i+1] := inttostr(polygon[i].X);
    StringGrid1.Cells[2,i+1] := inttostr(polygon[i].Y);

    if i = 0 then
    begin
      image1.Canvas.rectangle(0,0,image1.width-1, image1.height-1);
      image1.Canvas.MoveTo(polygon[0].X, tinggiCanvas - polygon[0].Y);
    end
    else
    begin
      if (modeOperasi = modeInsert) and (editIndex = i) then
      begin
        Image1.Canvas.Pen.Color := clRed;
      end
      else
      begin
        Image1.Canvas.Pen.Color := clBlack;
      end;
      image1.Canvas.LineTo(polygon[i].X, tinggiCanvas - polygon[i].Y);
    end;

    i := i + 1;
    if i = 20 then
      break;

  end;

   if modeOperasi = modeEdit then
   begin
    image1.Canvas.Rectangle(polygon[editIndex].X-5, tinggiCanvas - polygon[editIndex].Y - 5,
      polygon[editIndex].X+5, tinggiCanvas - polygon[editIndex].Y + 5);
   end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.Cells[0,0] := 'No';
  StringGrid1.Cells[1,0] := 'X';
  StringGrid1.Cells[2,0] := 'Y';
end;


procedure TForm1.FormResize(Sender: TObject);
begin
  tinggiCanvas := Image1.Height;

end;

procedure TForm1.ButtonDrawClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [fsBold];
  ButtonEdit.Font.Style := [];
  ButtonInsert.Font.Style := [];

  modeOperasi := modeDraw;
  startPolygon := true;
  polygonCount := 0;
  polygon[polygonCount].X := -1;

  refreshCanvas();
end;

procedure TForm1.ButtonCloseClick(Sender: TObject);
begin

  if (modeOperasi = modeDraw) and (polygonCount > 1) then
  begin
    polygon[polygonCount] := polygon[0];
    polygonCount := polygonCount + 1;

    polygon[polygonCount].X := -1;

    LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

    refreshCanvas();

    buttonDraw.Font.Style := [];
    ButtonEdit.Font.Style := [];
    ButtonInsert.Font.Style := [];
    modeOperasi := modeIdle;
  end;

end;

procedure TForm1.ButtonEditClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [];
  ButtonEdit.Font.Style := [fsBold];
  ButtonInsert.Font.Style := [];
  modeOperasi := modeEdit;
  editIndex := 1;
  refreshCanvas();
end;

procedure TForm1.ButtonInsertClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [];
  ButtonEdit.Font.Style := [];
  ButtonInsert.Font.Style := [fsBold];
  modeOperasi := modeInsert;
  editIndex := 1;
  refreshCanvas();
end;


procedure TForm1.ButtonClearClick(Sender: TObject);
begin
  polygonCount := 0;
  polygon[polygonCount].X := -1;

  refreshCanvas();
end;

procedure TForm1.ButtonDeleteClick(Sender: TObject);
var
  i : integer;
begin
  if editIndex <> 0 then
  begin
    for i := editIndex to polygonCount do
    begin
      polygon[i].X := polygon[i+1].X;
      polygon[i].Y := polygon[i+1].Y;
    end;
    polygonCount := polygonCount - 1;
    refreshCanvas();
  end;
end;


procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  posisi.X := X;
  posisi.Y := tinggiCanvas - Y;
  LabelPosisi.Caption := 'Posisi ' + inttostr(posisi.X) + ', ' + inttostr(posisi.Y);
end;

procedure TForm1.Image1Click(Sender: TObject);
var
  i : integer;
begin
  if (modeOperasi = modeDraw) and (polygonCount < 20) then
  begin
    polygon[polygonCount].X := posisi.X;
    polygon[polygonCount].Y := posisi.Y;
    polygonCount := polygonCount + 1;
    polygon[polygonCount].X := -1;
    LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

    refreshCanvas();
  end
  else if (modeOperasi = modeEdit) and (editIndex > 0)then
  begin
    if (editIndex = 1) or (editIndex = polygonCount-1) then
    begin
      polygon[1].X := posisi.X;
      polygon[1].Y := posisi.Y;
      polygon[polygonCount-1].X := posisi.X;
      polygon[polygonCount-1].Y := posisi.Y;
    end
    else if (editIndex > 0) then
    begin
      polygon[editIndex].X := posisi.X;
      polygon[editIndex].Y := posisi.Y;

    end;

    refreshCanvas();
  end
  else if (modeOperasi = modeInsert) and (editIndex > 0) and (polygonCount < 20) then
  begin
    for i := polygonCount downto editIndex do
    begin
      polygon[i+1].X := polygon[i].X;
      polygon[i+1].Y := polygon[i].Y;
    end;
    polygon[editIndex].X := posisi.X;
    polygon[editIndex].Y := posisi.Y;
    polygonCount := polygonCount + 1;
    refreshCanvas();
 end;

end;


procedure TForm1.ButtonSaveClick(Sender: TObject);
var
  Stream: TStream;
begin
  Stream:= TFileStream.Create('polygon.bin', fmCreate);
  Stream.WriteBuffer(polygonCount, SizeOf(integer));
  Stream.WriteBuffer(polygon, (polygonCount+1) * SizeOf(Point));
  Stream.Free;
end;

procedure TForm1.ButtonLoadClick(Sender: TObject);
var
  Stream: TStream;
begin
  Stream:= TFileStream.Create('polygon.bin', fmOpenRead);
  Stream.ReadBuffer(polygonCount, SizeOf(integer));
  Stream.ReadBuffer(polygon, (polygonCount+1) * SizeOf(Point));
  Stream.Free;

  refreshCanvas();
  LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

end;

procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
begin
  editIndex := ARow - 1;
  refreshCanvas();
end;

end.

code lengkap media pengajaran geometri poligon:
library :

Penjadwalan pembacaan sensor berbasis RTC DS3231

Pembacaan sensor dengan arduino dapat dilakukan kapan saja dan dimana saja dalam sketch arduino sesuai kebutuhan. Penjadwalan diperlukan untuk mendapatkan nilai dari sensor dalam rentang tertentu atau pada waktu-waktu khusus misalnya baca sensor saat gerhana matahari. Jika menginginkan sensor dibaca hanya pada waktu-waktu tertentu saja berdasarkan RTC dapat menggunakan metode berikut:

  1. Metode pergantian waktu, yaitu aksi pembacaan sensor dilakukan ketika satuan waktu berubah, contohnya setiap detik, setiap menit dan seterusnya. contoh :
      if (detikSebelumnya != now.second() )
      {
        detikSebelumnya = now.second();
        bacaSensor();
      }
    
  2. Metode modulus waktu, yaitu pembacaan sensor dilakukan pada setiap kelipatan satuan waktu, seperti pembacaan sensor setiap 2 detik, setiap 30 detik dan seterusnya. Contohnya :
      if (!(now.second() % periodaBacaSensor) )
      {
        bacaSensor();
      }
    
  3. Metode rentang waktu, yaitu pembacaan sensor dilakukan selama waktu pada rentang waktu tertentu. Contohnya :
      if ((now.hour() >= 20) || (now.hour() < 3))
      {
        bacaSensor();
      }
    
  4. Metode Tabel, yaitu pembacaan sensor sesuai waktu yang ditetapkan berdasarkan tanggal dan waktu tertentu, (pembacaan hanya dilakukan 1 kali). Contohnya:
      if (now.getEpoch() == databaseJadwal[i].getEpoch())
      {
        bacaSensor();
      }
    

Rangkaian yang digunakan pada pembacaan sensor sesuai jadwal tertentu berbasis rtc:

berikut kode/sketch penjadwalan pembacaan sensor menggunakan rtc pada arduino:


#define pinSensor     A0

#include <Wire.h>
#include "Sodaq_DS3231.h"

DateTime set = {2020, 2, 22, 7, 0, 0, 1};
DateTime now;
DateTime databaseJadwal[] = {
  {2020, 2, 22, 7, 0, 3, 1},
  {2020, 2, 23, 7, 0, 0, 1},
  {2020, 2, 24, 7, 0, 0, 1},
  {2020, 2, 25, 7, 0, 0, 1},
  {2020, 2, 26, 7, 0, 0, 1},
  {2020, 2, 27, 7, 0, 0, 1},
};
byte detikSebelumnya;
byte menitSebelumnya;

#define periodaBacaSensor   5//detik

void bacaSensor()
{
  analogRead(pinSensor);
}

void setup() {
  Serial.begin(9600);
  Serial.println("Penjadwalan pembacaan sensor berbasis RTC");
  Serial.println("https://www.semesin.com/project");
  Serial.println();

  Wire.begin();
  rtc.begin();
  now = rtc.now();
  //  if (now.year() == 2000)
  {
    rtc.setDateTime(set);
  }
}

void loop() {
  now = rtc.now();

  if (detikSebelumnya != now.second() )
  {

    char bufWaktu[32];
    sprintf(bufWaktu, "%02d:%02d:%02d %02d-%02d-%04d", now.hour(), now.minute(), now.second(), now.date(), now.month(), now.year());
    Serial.println(bufWaktu);

    //pembacaan setiap detik
    detikSebelumnya = now.second();
    Serial.println("setiap detik");
    bacaSensor();


    //pembacaan setiap 2 detik
    if (now.second() % 2)
    {
      Serial.println("setiap 2 detik");
      bacaSensor();
    }

    //pembacaan setiap detik ke-5
    if (now.second() == 5)
    {
      Serial.println("setiap detik ke-5");
      bacaSensor();
    }

    //pembacaan setiap n detik (n 1..60)
    if (!(now.second() % periodaBacaSensor) )
    {
      Serial.println("setiap n detik");
      bacaSensor();
    }

    //pembacaan setiap 937 detik (berlaku untuk x detik 1..~)
    if (!(now.getEpoch() % 937) )
    {
      Serial.println("setiap 937 detik");
      bacaSensor();
    }

    //pembacaan setiap menit
    if (menitSebelumnya != now.minute() )
    {
      Serial.println("setiap menit");
      menitSebelumnya = now.minute();
      bacaSensor();
    }

    //pembacaan setiap 3 jam = 00:00, 03:00, 06:00, 09:00, 12:00, 15:00, 18:00, 21:00
    if (!(now.hour() % 3) && !now.minute() && !now.second())
    {
      Serial.println("setiap 3 jam");
      bacaSensor();
    }

    //pembacaan dalam rentang waktu
    if ((now.hour() >= 7) && (now.hour() < 10))//jam 07:00 - jam 09:59
    {
      Serial.println("pembacaan dalam rentang waktu 1");
      bacaSensor();
    }
    else if ((now.hour() >= 11) && (now.hour() < 15))//jam 11:00 - jam 14:59
    {
      Serial.println("pembacaan dalam rentang waktu 2");
      bacaSensor();
    }
    else if ((now.hour() >= 20) || (now.hour() < 3))//jam 20:00 - jam 02:59
    {
      Serial.println("pembacaan dalam rentang waktu 3");
      bacaSensor();
    }

    //pembacaan berdasarkan tabel waktu
    for (byte i = 0; i < sizeof(databaseJadwal) / sizeof(DateTime); i++)
    {
      if (now.getEpoch() == databaseJadwal[i].getEpoch())
      {
        Serial.print("tabel waktu = ");
        Serial.println(i);
        bacaSensor();
      }
    }
  }

}

library :

DMD Jam, Timer, Countdown dengan arduino

Jam, timer/stopwatch, countdown seringkali di kombinasikan dalam sebuah perangkat, karena fitur2 ini memiliki kemiripan fungsi.

Fitur tampilan dapat dipilih melalui satu tombol, setiap tombol ditekan maka mode waktu akan berubah yaitu Watch – Timer – Countdown, fungsi masing-masing adalah :

Watch (Jam)

Ditandai dengan Karakter ‘W’ (watch) pada sudut kiri atas, berfungsi menampilan waktu rtc saat ini dalam format “hh:mm:dd” atau jam:menit:detik.

Timer (Hitung maju)

Ditandai dengan karakter ‘T’ (Timer) pada sudut kiri atas. Dalam mode ini pewaktu memulai hitungan dari 00:00:00 dan akan terus bertambah setiap detik. Apabila hitungan maju ini telah mencapai 86.400 detik atau 23:59:59 atau 1 hari penuh, maka hitungan akan dimulai lagi dari 0.

Countdown (Hitung mundur)

Ditandai dengan karakter ‘C’ (Count down) pada sudut kiri atas.berfungsi menampilkan waktu tersisa dari batas aawal yang bisa diatur dengan memodifikasi variabel ‘waktuHitungMundur’ seperti contoh :


#define waktuHitungMundur 5 * 60// 5 menit

Waktu hitung mungdur dapat diisi angka 1 – 86400 dalam satuan detik.

Apabila hitungan habis atau mencapai 0 (00:00:00) maka tampilan akan berubah ke mode watch dan menampilkan waktu rtc saat ini.

Komponen yang digunakan dalam perancangan jam, counter, countdown:

  1. Arduino uno
  2. 2x Panel P10 hub12
  3. RTC DS3231

berikut skema rakitan tampilan waktu, hitung maju dan hitung mundur:

Koding/sketch arduino tampilan jam, penghitung mundur dan pewaktu maju:

#define waktuHitungMundur     5 * 60// 5 menit

#define pinTombol             A0


#include "Sodaq_DS3231.h"
#include <DMD2.h>
#include <fonts/angka6x14.h>
#include <fonts/System_Min5x7.h>

enum Mode
{
  modeJam,
  modeHitungMaju,
  modeHitungMundur,
  jumlahMode,
};

DateTime now;
uint32_t hitungMundur;
uint32_t hitungMaju;
byte mode;
char buffer[10];
byte jam, menit, detik;
byte detikSebelumnya;

SPIDMD dmd(2, 1);

void setup() {

  pinMode(pinTombol, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println("Jam, Hitung maju, Hitung mundur (Watch, Timer, Count down)");
  Serial.println("https://www.semesin.com/project");

  rtc.begin();

  //Set rtc untuk keperluan testing
  DateTime dt(2020, 2, 20, 15, 30, 0, 1);
  rtc.setDateTime(dt);

  dmd.begin();

  setMode();
}

void loop() {
  now = rtc.now();
  if (detikSebelumnya != now.second())
  {
    detikSebelumnya = now.second();

    switch (mode)
    {
      case modeJam:
        sprintf(buffer, "%02d%s%02d%s%02d", now.hour(), now.second() % 2 ? ":" : " ", now.minute(), now.second() % 2 ? ":" : " ", now.second());
        dmd.drawString(8, 1, buffer);
        break;

      case modeHitungMaju:
        jam = hitungMaju / (3600L);
        menit = (hitungMaju % (3600L)) / 60;
        detik = hitungMaju % 60;
        sprintf(buffer, "%02d%s%02d%s%02d", jam, now.second() % 2 ? ":" : " ", menit, now.second() % 2 ? ":" : " ", detik);
        dmd.drawString(8, 1, buffer);
        hitungMaju++;
        if(hitungMaju == 86400L)
        {
          hitungMaju = 0; 
        }
        
        break;

      case modeHitungMundur:
        jam = hitungMundur / (3600L);
        menit = (hitungMundur % (3600L)) / 60;
        detik = hitungMundur % 60;
        sprintf(buffer, "%02d%s%02d%s%02d", jam, now.second() % 2 ? ":" : " ", menit, now.second() % 2 ? ":" : " ", detik);
        dmd.drawString(8, 1, buffer);

        hitungMundur--;
        if (!hitungMundur)
        {
          mode = modeJam;
          setMode();
        }
        break;
    }
  }
  if (!digitalRead(pinTombol))
  {
    delay(100);
    if (!digitalRead(pinTombol))
    {
      mode++;
      mode %= jumlahMode;
      setMode();

      while (!digitalRead(pinTombol));
    }
  }
}

void setMode()
{
  dmd.selectFont(System_Min5x7);
  switch (mode)
  {
    case modeJam:
      dmd.drawString(0, 0, "W");
      break;

    case modeHitungMaju:
      dmd.drawString(0, 0, "T");
      hitungMaju = 0;
      break;

    case modeHitungMundur:
      dmd.drawString(0, 0, "C");
      hitungMundur = waktuHitungMundur;
      break;
  }
  dmd.selectFont(angka6x14);
  dmd.fontHeader.fixedWidth = 2;
}

library :

JWS Jammer dengan Database Jadwal Waktu Shalat Bulanan

Jammer merupakan alat pengacak/blokir sinyal handphone/hp dapat digunakan di tempat ibadah untuk me-nonaktifkan perangkat cellular sehingga tidak bisa dipanggil/memanggil dengan tujuan menjaga tempat ibadah dari kebisingan dering ponsel.

Penggunaan jammer/jamming/repeater sinyal hp bisa mengganggu hak orang lain dan berpotensi melanggar undang-undang telekomunikasi, gunakan dengan bijak

Jadwal Shalat

Jadwal shalat berubah-ubah setiap hari tergantung posisi matahari dan bulan, hal ini membuat perangkat digital harus mampu mengikuti perubahan ini. Beberapa metode penggunaan jadwal shalat shalat digital :

  1. Jadwal manual, yaitu jadwal rata-rata dari setiap waktu-waktu shalat
  2. Jadwal database, yaitu jadwal yang tersimpan dalam tabel, bisa mingguan, bulanan atau tahunan
  3. Jadwal Matahari, yaitu penghitungan waktu shalat menggunakan posisi peredaran matahari, dengan memasukkan variabel posisi lintang, bujur, dan tanggal.
  4. Jadwal online, dengan mengambil data dari server online setiap hari.

Skema JWS Jammer menggunakan arduino:

Komponen yang digunakan dalam JWS Database Jadwal Waktu Bulanan:

  1. Arduino Uno
  2. LCD 1602 I2C Backpack
  3. RTC DS3231
  4. Relay untuk jammer
  5. 3x LED
  6. 3x TOmbol

Koding/program JWS Jadwal Bulanan:

#define pinLedMerah             5
#define pinLedHijau             6
#define pinLedBiru              7

#define pinTombolOtomatis       11
#define pinTombolManual         12
#define pinTombolJammer         13

#define pinJammer               4
#define relayOn                 LOW

#define waktuJammerShalat       20
#define waktuJammerShalatJumat  60
#define waktuJammerManual       20

#include <Wire.h>
#include "Sodaq_DS3231.h"
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 16, 2);

struct waktu
{
  byte jam;
  byte menit;
};


const waktu jadwalShalat[][8] PROGMEM = {
  //Imsyak  Shubuh  Terbit  Dhuha Dzuhur  Ashr  Maghrib Isya
  {{ 4, 20}, { 4, 30}, { 5, 51}, { 6, 15}, {12, 7}, {15, 34}, {18, 20}, {19, 36}},
  {{ 4, 20}, { 4, 30}, { 5, 51}, { 6, 15}, {12, 8}, {15, 34}, {18, 20}, {19, 36}},
  {{ 4, 21}, { 4, 31}, { 5, 52}, { 6, 16}, {12, 8}, {15, 35}, {18, 21}, {19, 36}},
  {{ 4, 21}, { 4, 31}, { 5, 52}, { 6, 16}, {12, 9}, {15, 35}, {18, 21}, {19, 37}},
  {{ 4, 22}, { 4, 32}, { 5, 53}, { 6, 17}, {12, 9}, {15, 36}, {18, 21}, {19, 37}},
  {{ 4, 23}, { 4, 33}, { 5, 53}, { 6, 17}, {12, 9}, {15, 36}, {18, 22}, {19, 37}},
  {{ 4, 23}, { 4, 33}, { 5, 54}, { 6, 18}, {12, 10}, {15, 36}, {18, 22}, {19, 38}},
  {{ 4, 24}, { 4, 34}, { 5, 54}, { 6, 18}, {12, 10}, {15, 37}, {18, 23}, {19, 38}},
  {{ 4, 24}, { 4, 34}, { 5, 55}, { 6, 19}, {12, 11}, {15, 37}, {18, 23}, {19, 38}},
  {{ 4, 25}, { 4, 35}, { 5, 55}, { 6, 19}, {12, 11}, {15, 37}, {18, 23}, {19, 38}},
  {{ 4, 25}, { 4, 35}, { 5, 56}, { 6, 20}, {12, 12}, {15, 37}, {18, 24}, {19, 39}},
  {{ 4, 26}, { 4, 36}, { 5, 56}, { 6, 20}, {12, 12}, {15, 38}, {18, 24}, {19, 39}},
  {{ 4, 27}, { 4, 37}, { 5, 56}, { 6, 20}, {12, 12}, {15, 38}, {18, 24}, {19, 39}},
  {{ 4, 27}, { 4, 37}, { 5, 57}, { 6, 21}, {12, 13}, {15, 38}, {18, 25}, {19, 39}},
  {{ 4, 28}, { 4, 38}, { 5, 57}, { 6, 21}, {12, 13}, {15, 38}, {18, 25}, {19, 40}},
  {{ 4, 28}, { 4, 38}, { 5, 58}, { 6, 22}, {12, 13}, {15, 39}, {18, 25}, {19, 40}},
  {{ 4, 29}, { 4, 39}, { 5, 58}, { 6, 22}, {12, 14}, {15, 39}, {18, 25}, {19, 40}},
  {{ 4, 29}, { 4, 39}, { 5, 59}, { 6, 23}, {12, 14}, {15, 39}, {18, 26}, {19, 40}},
  {{ 4, 30}, { 4, 40}, { 5, 59}, { 6, 23}, {12, 14}, {15, 39}, {18, 26}, {19, 40}},
  {{ 4, 30}, { 4, 40}, { 5, 59}, { 6, 23}, {12, 15}, {15, 39}, {18, 26}, {19, 40}},
  {{ 4, 31}, { 4, 41}, { 6, 0}, { 6, 24}, {12, 15}, {15, 39}, {18, 26}, {19, 40}},
  {{ 4, 31}, { 4, 41}, { 6, 0}, { 6, 24}, {12, 15}, {15, 39}, {18, 26}, {19, 40}},
  {{ 4, 32}, { 4, 42}, { 6, 1}, { 6, 25}, {12, 16}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 32}, { 4, 42}, { 6, 1}, { 6, 25}, {12, 16}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 33}, { 4, 43}, { 6, 1}, { 6, 25}, {12, 16}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 33}, { 4, 43}, { 6, 2}, { 6, 26}, {12, 16}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 34}, { 4, 44}, { 6, 2}, { 6, 26}, {12, 17}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 34}, { 4, 44}, { 6, 2}, { 6, 26}, {12, 17}, {15, 39}, {18, 27}, {19, 41}},
  {{ 4, 35}, { 4, 45}, { 6, 3}, { 6, 27}, {12, 17}, {15, 39}, {18, 27}, {19, 40}},
  {{ 4, 35}, { 4, 45}, { 6, 3}, { 6, 27}, {12, 17}, {15, 39}, {18, 27}, {19, 40}},
  {{ 4, 36}, { 4, 46}, { 6, 3}, { 6, 27}, {12, 17}, {15, 39}, {18, 27}, {19, 40}},
};
byte waktuUtama[] =  {1, 4, 5, 6, 7};
char namaHari[][8] = {"Minggu,", "Senin, ", "Selasa,", " Rabu, ", "Kamis, ", "Jumat, ", "Sabtu, "};
char namaWaktu[][16] = {"Imsyak", "Shubuh", "Terbit", "Dhuha", "Dzuhur", "Ashr", "Maghrib", "Isya"};

enum Mode
{
  otomatis,
  manual,
};

byte mode;
char buffer[32];
byte detikSebelumnya = 60;
byte menitSebelumnya = 60;
uint16_t unixRelay;
bool statusJammer;
DateTime now;

void setup()
{
  pinMode(pinLedMerah, OUTPUT);
  pinMode(pinLedHijau, OUTPUT);
  pinMode(pinLedBiru, OUTPUT);
  digitalWrite(pinJammer, !relayOn);
  pinMode(pinJammer, OUTPUT);

  pinMode(pinTombolOtomatis, INPUT_PULLUP);
  pinMode(pinTombolManual, INPUT_PULLUP);
  pinMode(pinTombolJammer, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println("JWS Jammer Sinyal HP dengan Jadwal Bulanan");
  Serial.println("https://www.semesin.com/project");

  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();

  rtc.begin();
  now = rtc.now();
  if (now.year() == 2000)
  {
    //Waktu compiler
    char bulan[12];
    byte indexBulan;
    int jam, menit, detik, tanggal, tahun;

    char *namaBulan[12] = {
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    sscanf(__TIME__, "%d:%d:%d", &jam, &menit, &detik);
    sscanf(__DATE__, "%s %d %d", bulan, &tanggal, &tahun);
    for (indexBulan = 0; indexBulan < 12; indexBulan++) {
      if (strcmp(bulan, namaBulan[indexBulan]) == 0)
        break;
    }
    uint8_t wday = hariDariTanggal(tanggal, indexBulan + 1, tahun);
    DateTime dt(tahun, indexBulan + 1, tanggal, jam, menit, detik, wday);
    rtc.setDateTime(dt);
    Serial.println("RTC sudah otomatis di setting (Sekali saja)");
  }

  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("JWS Bulanan");
  lcd.setCursor(0, 1);
  lcd.print("fitur : Jammer");

  delay(3000);
  lcd.clear();

  digitalWrite(pinLedHijau, HIGH);
  Serial.println("Sistem mulai");

}

void loop()
{
  now = rtc.now();

  uint16_t unixSekarang = now.hour() * 60 + now.minute();
  if (detikSebelumnya != now.second())
  {

    detikSebelumnya = now.second();
    if (!statusJammer)
    {
      sprintf(buffer, "%s %02d/%02d/%02d", namaHari[now.dayOfWeek() - 1], now.date(), now.month(), now.year() - 2000);
      lcd.setCursor(0, 0);
      lcd.print(buffer);
    }
    sprintf(buffer, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    lcd.setCursor(4, 1);
    lcd.print(buffer);
  }
  if (menitSebelumnya != now.minute())
  {
    menitSebelumnya = now.minute();
    if (mode == otomatis)
    {
      for (byte i = 0; i < 5; i++)
      {
        if ((pgm_read_byte(&jadwalShalat[now.date() - 1][waktuUtama[i]].jam) == now.hour()) &&
            (pgm_read_byte(&jadwalShalat[now.date() - 1][waktuUtama[i]].menit) == now.minute()))
        {
          digitalWrite(pinLedMerah, HIGH);
          digitalWrite(pinJammer, relayOn);

          if ((i == 1) && (now.dayOfWeek() == 6)) //Dzuhur / jumatan
          {
            unixRelay = unixSekarang + waktuJammerShalatJumat;//menit
          }
          else
          {
            unixRelay = unixSekarang + waktuJammerShalat;//menit
          }
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("  J A M M E R   ");
          statusJammer = true;

          sprintf(buffer, "Waktu: %s", namaWaktu[waktuUtama[i]]);
          lcd.setCursor(0, 1);
          lcd.print(buffer);

          delay(5000);
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("  J A M M E R   ");
        }
      }
    }
    if (statusJammer)
    {
      if (unixSekarang == unixRelay)
      {
        statusJammer = false;
        digitalWrite(pinLedMerah, LOW);
        digitalWrite(pinJammer, !relayOn);
      }
    }
  }


  if (!digitalRead(pinTombolOtomatis))
  {
    delay(50);
    if (!digitalRead(pinTombolOtomatis))
    {
      mode = otomatis;
      digitalWrite(pinLedHijau, HIGH);
      digitalWrite(pinLedBiru, LOW);
      lcd.setCursor(0, 0);
      lcd.print("    M O D E     ");
      lcd.setCursor(0, 1);
      lcd.print("O T O M A T I S ");
      delay(3000);
      lcd.clear();
    }
  }
  if (!digitalRead(pinTombolManual))
  {
    delay(50);
    if (!digitalRead(pinTombolManual))
    {
      mode = manual;
      digitalWrite(pinLedHijau, LOW);
      digitalWrite(pinLedBiru, HIGH);
      lcd.setCursor(0, 0);
      lcd.print("    M O D E     ");
      lcd.setCursor(0, 1);
      lcd.print("  M A N U A L   ");
      delay(3000);
      lcd.clear();
    }
  }
  if (mode == manual)
  {
    if (!digitalRead(pinTombolJammer))
    {
      delay(50);
      if (!digitalRead(pinTombolJammer))
      {
        if (statusJammer)
        {
          digitalWrite(pinLedMerah, LOW);
          digitalWrite(pinJammer, !relayOn);
          statusJammer = false;
        }
        else
        {
          digitalWrite(pinLedMerah, HIGH);
          digitalWrite(pinJammer, relayOn);
          unixRelay = unixSekarang + waktuJammerManual;//menit
          statusJammer = true;
          lcd.setCursor(0, 0);
          lcd.print("  J A M M E R   ");
          lcd.setCursor(0, 1);
          lcd.print("  M A N U A L   ");
          delay(5000);
        }
        while (!digitalRead(pinTombolJammer));
        delay(50);
      }
    }
  }
}
byte hariDariTanggal(byte tanggal, byte bulan, uint16_t tahun)
{
  uint16_t jumlahHariPerBulanMasehi[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  if (tahun >= 2000)
    tahun -= 2000;

  uint32_t jumlahHari = tahun * 365;
  uint16_t tahunKabisat = tahun / 4;
  for (byte i = 0; i < tahun; i++)
  {
    if (!(i % 4))
    {
      jumlahHari++;
    }
  }
  jumlahHari += jumlahHariPerBulanMasehi[bulan - 1];
  if ( (bulan >= 2) && !(tahun % 4))
  {
    jumlahHari++;
  }
  jumlahHari += tanggal;
  return ((jumlahHari + 5) % 7) + 1;
}

Library:

simulasi Gerak lurus berubah beraturan (GLBB) – Gerak jatuh bebas menggunakan arduino

Gerak lurus berubah beraturan (GLBB) adalah gerak lurus yang percepatannya tetap, artinya kecepatannya berubah beraturan. terdapat beberapa jenis GLBB yaitu :

  1. Gerak vertikal ke atas
  2. Gerak jatuh bebas
  3. Gerak vertikal ke bawah

untuk keperluan simulasi gerak lurus berubah beraturan diperlukan peralatan sebagai berikut :

  1. Arduino uno
  2. 3x sensor obstacle
  3. sensor ultrasonic
  4. Tombol

Dengan tata letak seperti berikut:

Skema simulasi GLBB menggunakan arduino:

sketch/koding simulasi gerak lurus berubah beraturan berbasis arduino:

#define tinggiSensorBawah   10//cm

#define pinTombolMulai      A0

#define pinSensorA          8
#define pinSensorB          9
#define pinSensorC          10

#include <Ultrasonic.h>

Ultrasonic ultrasonic(3, 2);

uint16_t nomor;

void setup() {
  pinMode(pinTombolMulai, INPUT_PULLUP);

  pinMode(pinSensorA, INPUT_PULLUP);
  pinMode(pinSensorB, INPUT_PULLUP);
  pinMode(pinSensorC, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println("Gerak Lurus Berubah Beraturan");
  Serial.println("https://www.semesin.com/project");

  Serial.println();
  Serial.println("Gerak Lurus Berubah Beraturan (jatuh bebas)");
  Serial.println("No\tJarak (cm)\tWaktu (s)\tPercepatan (cm/s2)");

}

void loop() {

  if (!digitalRead(pinTombolMulai))
  {
    delay(50);
    if (!digitalRead(pinTombolMulai))
    {
      while (!digitalRead(pinTombolMulai));

      uint16_t tinggi = 1.0 * ultrasonic.read() - tinggiSensorBawah;
      float jarakSensor = 1.0 * tinggi / 2;

      unsigned long waktuA, waktuB, waktuC;
      bool mulaiDiTitikA, mulaiDiTitikC = false;
      while (1)
      {
        if (!digitalRead(pinSensorA))
        {
          waktuA = micros();
          mulaiDiTitikA = true;
          break;
        }
        if (!digitalRead(pinSensorC))
        {
          waktuC = micros();
          mulaiDiTitikC = true;
          break;
        }
      }

      while (digitalRead(pinSensorB));
      waktuB = micros();

      while (1)
      {
        if (!digitalRead(pinSensorA))
        {
          waktuA = micros();
          break;
        }
        if (!digitalRead(pinSensorC))
        {
          waktuC = micros();
          break;
        }
      }

      float waktu_ac = abs(1.0 * waktuC - waktuA) / 1000000;//detik
      float waktu_ab = abs(1.0 * waktuB - waktuA) / 1000000;//detik
      float waktu_bc = abs(1.0 * waktuC - waktuB) / 1000000;//detik

      float kecepatan_ab = 0.5 * jarakSensor / waktu_ab;//cm/s
      float kecepatan_cb = 0.5 * jarakSensor / waktu_ab;//cm/s
      float kecepatan_bc = 0.5 * jarakSensor / waktu_bc;//cm/s

      float percepatan;
      if (mulaiDiTitikA)
      {
        percepatan = (1.0 * kecepatan_bc - kecepatan_ab) / waktu_bc;//cm/s2
      }
      if (mulaiDiTitikC)
      {
        percepatan = (1.0 * kecepatan_ab - kecepatan_bc) / waktu_ab;//cm/s2
      }

      char buffer[200];
      char strWaktu_ac[10];
      char strPercepatan[10];

      dtostrf(waktu_ac, 8, 2, strWaktu_ac);
      dtostrf(percepatan, 8, 2, strPercepatan);

      sprintf(buffer, "%d\t%d\t\t%s\t%s", nomor, tinggi, strWaktu_ac, strPercepatan);
      Serial.println(buffer);
      nomor++;

    }
  }
}

Library:

webserver + ajax dengan Nodemcu (esp8266) (aplikasi monitoring suhu dan kelembaban)

Monitoring atau kegiatan pemantauan bisa dilakukan dari jarak jauh melalui sistem komunikasi internet maupun metode komunikasi lainnya. Salah satu unsur monitoring data adalah display atau tampilan dimana beberapa aspek yang perlu diperhatikan adalah :

  1. Informatif, data yang ditampilkan mampu memberikan perspektif kondisi yang ada, sebagai contoh tampilan grafik: selain menunjukkan data juga akan memberikan gambaran pergerakan dari data-data sebelumnya (naik/turun).
  2. Real time. yaitu datayang tampil merupakan data lapangan saat ini dan terus diperbaharui dalam frekuensi tertentu dimana semakin besar akan semakin realtime.
  3. Bandwidth, adalah besarnya lalu lintas data yang terpakai oleh aktifitas monitoring, jadi sistem yang baik perlu mempertimbangkan metode lalu lintas data sekecil mungkin tanpa mengurangi aspek lainnya.
  4. Kenyamanan, berkaitan dengan design dan pola dinamik tampilan saat perubahan data.

Ajax

Asynchronous Javascript and XML (Ajax) adalah metode lalu lintas data web secara asynchronous (dibelakang layar) antara interface web (browser) dengan server yang bertujuan memberikan fitur interaktif kepada pengguna.

penggunaan ajax memungkinkan update data fleksibel tanpa perlu melakukan aksi (menekan tombl update) atau refresh halaman web.

Tampilan monitoring suhu dan kelembaban menggunakan nodemcu:

Komponen yang digunakan dalam perancangan monitoring suhu dan kelembaban berbasis webserver nodemcu :

  1. Nodemcu
  2. DHT11

Skema ajax webserver dengan nodemcu:

Sketch/program webserver nodemcu dengan ajax relatime:

#define jumlahData        5
#define hotspotSSID       "XXXXXXXX"
#define hotspotPassword   "XXXXXXXX"
#define pinDHT            D4

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

#include <dht.h>

dht DHT;

ESP8266WebServer server(80);

struct Data
{
  uint32_t index;
  float temperatur;
  float kelembaban;
};

Data data[jumlahData];
byte ringIndex;
uint32_t indexDataDHT;
long millisBacaSensor;

void handleRoot() {
  char buffer[] =
    "<html><head>"
    "  <title>Temperatur dan Kelembaban</title>"
    "  <meta charset='UTF-8'>"
    "  <script src='https://code.highcharts.com/highcharts.js'></script>"
    "</head>"
    "<body>"
    "  <center>"
    "    <h1>Temperatur dan Kelembaban</h1>"
    "    <h2>Grafik Temperatur dan Kelembaban</h2>"
    "    <div id='grafik' style='height:400px;'></div>"
    "  </center>"
    "<script type='text/javascript'>"
    "  var timerId = setInterval('updateGrafik()', 1000);"
    "  var grafik = Highcharts.chart('grafik', {"
    "    title: {text: ''},"
    "    xAxis: {title: {text: 'index'},categories: [],rotation:90,},"
    "    yAxis: ["
    "      {labels: {format: '{value} °C',style: {color: Highcharts.getOptions().colors[1]}},title: {text: 'Temperatur'},},"
    "      {labels: {format: '{value} %',style: {color: Highcharts.getOptions().colors[1]}},title: {text: 'Kelembaban'},opposite: true,}, ],"
    "    credits: {enabled: false},"
    "    series: ["
    "      {name: 'Temperatur',yAxis: 0,data: []}, "
    "      {name: 'Kelembaban',yAxis: 1,data: []}, ]"
    "  });"
    "  function updateGrafik(){"
    "    var xhttp = new XMLHttpRequest();"
    "    xhttp.onreadystatechange = function() {"
    "      if (this.readyState == 4 && this.status == 200) {"
    "        eval(this.responseText.trim());}};"
    "    xhttp.open('GET', '/updateData', true);"
    "    xhttp.send(); }"
    "</script>"
    "</body></html>";
  server.send(200, "text/html", buffer);
}

void handleNotFound()
{
  server.send(404, "text/plain", "Alamat tidak ditemukan");
}

void updateData()
{
  char buffer[1000];
  byte index;

  strcpy(buffer, "grafik.xAxis[0].setCategories([");

  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    sprintf(&buffer[strlen(buffer)], "%lu,", data[index].index);
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  strcpy(&buffer[strlen(buffer)], "grafik.series[0].setData([");
  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    dtostrf(data[index].temperatur, 8, 2, &buffer[strlen(buffer)]);
    strcpy(&buffer[strlen(buffer)], ",");
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  strcpy(&buffer[strlen(buffer)], "grafik.series[1].setData([");
  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    dtostrf(data[index].kelembaban, 8, 2, &buffer[strlen(buffer)]);
    strcpy(&buffer[strlen(buffer)], ",");
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  server.send(200, "text/html", buffer);

}


void setup(void) {
  Serial.begin(9600);
  Serial.println("Web server dengan fitur ajax menggunakan nodemcu (aplikasi monitor suhu dan kelembaban)");
  Serial.println("https://www.semesin.com/project");
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(hotspotSSID, hotspotPassword);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(hotspotSSID);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/updateData", updateData);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();

  if (millisBacaSensor < millis())
  {
    millisBacaSensor = millis() + 1000;

    if (DHT.read11(pinDHT) == DHTLIB_OK)
    {
      data[ringIndex].index = indexDataDHT;
      data[ringIndex].temperatur = DHT.temperature;
      data[ringIndex].kelembaban = DHT.humidity;

      indexDataDHT++;

      ringIndex++;
      ringIndex %= jumlahData;
    }
  }
}

Library:

Timbangan tampilan seven segment menggunakan arduino

Penggunaan timbangan secara umum membutuhkan akurasi dan presisi. Dalam skala lebih luas sebaiknya unit timbangan juga informatif sehingga dapat diketahui hasilnya secara luas. Untuk keperluan ini, unit timbangan  harus memiliki sarana display yang memadai seperti menggunakan display dot mattrix, seven segment dan sebagainya.

Fitur timbangan

Fitur/fasilitas timbangan bisa bermacam sesuai kebutuhan. Dalam perancangan timbangan 7-segment ini memasukkan fitur-fitur penting diantaranya :

    1. Kalibrasi

Kalibrasi adalah kegiatan untuk memastikan timbangan memiliki akurasi dan presisi yang baik. dalam perancangan ini prosedur kalibrasinya sebagai berikut:

    • Posisi perangkat timbangan dalam keadaan mati
    • Letakkan beban tera 1Kg
    • Tekan dan tahan tombol [hold]
    • Hidupkan perangkat timbangan (akan muncul tulisan ‘load 1’ di tampilan seven segment)
    • lepas tombol [hold]
    • Tunggu hingga muncul tulisan ‘load 2’ di 7-segment
    • Letakkan beban tera 2.5 Kg
    • Tekan dan lepas tombol [hold]
    • Tunggu hingga muncul tulisan ‘siap’ didisplay 7-segment
    • Kalibrasi selesai
  1. tombol tare
  2. tombol hold
  3. Tampilan seven segment

Seven Segmen

Seven segment memiliki kelebihan dari display lainnya, diantaranya :

  1. Intensitas cahaya lebih terang (akan berkurang jika menggunakan sistem scanning)
  2. Mudah terbaca dari jarak jauh (lebih jelas)
  3. Jika menggunakan mikrokontroler, lebih sedikit menggunakan resources (memory dan waktu proses)
  4. Menggunakan komponen lebih sedikit (non module)
  5. Perbandingan cahaya vs daya terpakai lebih sedikit sehingga lebih hemat.

Dalam proyek timbangan informatif dengan display 7-segment berbasis arduino ini menggunakan komponen sebagai berikut:

Komponen yang digunakan dalam perancangan timbangan sevent segment menggunakan arduino:

  1. Arduino uno
  2. modul hx711 dan loadcell 5Kg
  3. IC uln2003
  4. 5x 7-Segment CC
  5. 2x Tombol

Skema timbangan tampilan seven segmen:

source code/program arduino timbangan seven segment:

 

#include "HX711.h"
#include <TimerOne.h>
#include <EEPROM.h>

#define CCorCA                1//0 = CC, 1 = CA (rangkaian dengan resistor)
#define tareSaatMulai         1

#define beratKalibrasi1Tera   1.0f  //Kg
#define beratKalibrasi2Tera   1.00f  //Kg
#define beratMaksimal         2.50 //Kg
#define jumlahSampling        5     //kali
#define frekuensiUpdateData   20    //kali per detik

//pin
HX711 scale(3, 2); // (DT, SCK)

#define pin1    A0
#define pin2    A1
#define pin3    A2
#define pin4    A3
#define pin5    A4

#define pinA    4
#define pinB    5
#define pinC    6
#define pinD    7
#define pinE    8
#define pinF    9
#define pinG    10
#define pinDot  11

#define pinTare 12
#define pinHold 13

#define dotBlank 6
#define dotAll   5

byte pin7Segment[] = {pinA, pinB, pinC, pinD, pinE, pinF, pinG};
byte pin7SegmentCommon[] = {pin1, pin2, pin3, pin4, pin5};
byte posisiTitik = dotBlank;

const char angka[] = {
  0b00111111,
  0b00000110,
  0b01011011,
  0b01001111,
  0b01100110,
  0b01101101,
  0b01111100,
  0b00000111,
  0b01111111,
  0b01100111,
  0b00000000,//blank
  0b01000000,//strip
  0b00111000,//L
  0b01011100,//o
  0b01011111,//a
  0b01011110,//d
  0b01101101,//S
  0b00000100,//i
  0b01110011,//p
  
};
#define Seg_blank 10
#define Seg_stip 11
#define Seg_L 12
#define Seg_o 13
#define Seg_a 14
#define Seg_d 15
#define Seg_S 16
#define Seg_i 17
#define Seg_p 18

//volatile uint32_t beratBCD = 0;
volatile uint8_t beratBCD[5];
volatile byte lastScanIndex = 0;
volatile byte index7Segment = 0;

long lastMillis;
long kirimDataMillis;
byte modeKalibrasi = 0;
long beratKalibrasi1;
long beratKalibrasi2;
float beratTerukur;
bool tahanNilai = false;
byte timbangBeratCounter = 0;

#define alamatKalibrasiM 0
#define alamatKalibrasiC 4

void setup() {
  Serial.begin(9600);
  Serial.println(F("Timbangan tampilan seven segment menggunakan arduino"));
  Serial.println(F("https://www.semesin.com/project"));
  
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);
  pinMode(pin5, OUTPUT);

  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pinC, OUTPUT);
  pinMode(pinD, OUTPUT);
  pinMode(pinE, OUTPUT);
  pinMode(pinF, OUTPUT);
  pinMode(pinG, OUTPUT);
  pinMode(pinDot, OUTPUT);

  pinMode(pinTare, INPUT_PULLUP);
  pinMode(pinHold, INPUT_PULLUP);

  Timer1.initialize(2000);
  Timer1.attachInterrupt( timerIsr );

  float m,c;

  scale.power_up();
  
  //Mode kalibrasi
  if(!digitalRead(pinHold))
  {
    delay(50);
    if(!digitalRead(pinHold))
    {
      beratBCD[4] = Seg_L;
      beratBCD[3] = Seg_o;
      beratBCD[2] = Seg_a;
      beratBCD[1] = Seg_d;
      beratBCD[0] = 1;

      while(!digitalRead(pinHold))//lepas
      {
        while(!digitalRead(pinHold))
        {
          delay(50);
        }
      }
      delay(1000);
      beratKalibrasi1 = scale.read_average(10);

      beratBCD[0] = 2;
      while(digitalRead(pinHold))//tekan
      {
        while(digitalRead(pinHold))
        {
          delay(50);
        }
      }
      delay(1000);
      beratKalibrasi2 = scale.read_average(10);
      
      m = 1.0 * (beratKalibrasi2 - beratKalibrasi1) / ( beratKalibrasi2Tera - beratKalibrasi1Tera);
      c = beratKalibrasi2 - (1.0 * m * beratKalibrasi2Tera);

      EEPROM.put(alamatKalibrasiM, m);
      EEPROM.put(alamatKalibrasiC, c);    

      beratBCD[4] = Seg_S;
      beratBCD[3] = Seg_i;
      beratBCD[2] = Seg_a;
      beratBCD[1] = Seg_p;
      beratBCD[0] = Seg_blank;

      delay(500);
    }
  }
  
  EEPROM.get(alamatKalibrasiM, m);
  EEPROM.get(alamatKalibrasiC, c);

  scale.set_scale(m);
  scale.set_offset(c);

#if tareSaatMulai
  scale.tare();
#endif
  
  scale.power_down();
  lastMillis = millis();
  kirimDataMillis = millis();
}

void loop() {

  if(!tahanNilai)
  {
    if(millis() - lastMillis > 10)
    {
      scale.power_up();
      delay(10);
      beratTerukur += scale.get_units(1);
      if(timbangBeratCounter++ == jumlahSampling)
      {
        beratTerukur = beratTerukur * 100 / jumlahSampling;
        if(beratTerukur < 0)
        {
          beratBCD[0] = 0;
          beratBCD[1] = 0;
          beratBCD[2] = 0;
          beratBCD[3] = Seg_stip;
          beratBCD[4] = Seg_blank;
          posisiTitik = 2;
        }
        else if(beratTerukur <= (beratMaksimal*100))
        {
          uint32_t BCD = Convert_IntToBCD32(beratTerukur);
          if((BCD & 0x0F) <= 5)
          {
            beratBCD[0] = 0;
          }
          else
          {
            beratBCD[0] = 5;
          }
          beratBCD[1] = (BCD >> 4) & 0x0F;
          beratBCD[2] = (BCD >> 8) & 0x0F;
          beratBCD[3] = (BCD >> 12) & 0x0F;
          beratBCD[4] = (BCD >> 16) & 0x0F;
          posisiTitik = 2;
        }
        beratTerukur = 0;
        timbangBeratCounter = 0;
      }
      scale.power_down();
      
      lastMillis = millis();
    }
  }

  //Kirim data setiap 100ms
  if(millis() - kirimDataMillis > (1000/frekuensiUpdateData))
  {
    if(beratTerukur <= 0)
    {
      Serial.println("0.00");
    }
    else
    {
      Serial.println(floor(beratTerukur)/100, 2);
    }

    kirimDataMillis = millis();
  }
  if(!digitalRead(pinTare))
  {
    delay(50);
    if(!digitalRead(pinTare))
    {
      beratBCD[0] = Seg_blank;
      beratBCD[1] = Seg_blank;
      beratBCD[2] = Seg_blank;
      beratBCD[3] = Seg_blank;
      beratBCD[4] = Seg_blank;
      posisiTitik = dotAll;
      scale.power_up();
      delay(10);
      scale.tare();
      scale.power_down();
      while(!digitalRead(pinTare));
    }
  }
  if(!digitalRead(pinHold))
  {
    delay(50);
    if(!digitalRead(pinHold))
    {
      tahanNilai = !tahanNilai;
      while(!digitalRead(pinHold));
    }
  }
}
void timerIsr()
{
  digitalWrite(pin7SegmentCommon[lastScanIndex], CCorCA?LOW:HIGH);
  drive7Segment(beratBCD[index7Segment]);
  if(posisiTitik == dotAll)
  {
    digitalWrite(pinDot, CCorCA?LOW:HIGH);
  }
  else if(posisiTitik == dotBlank)
  {
    digitalWrite(pinDot, CCorCA?HIGH:LOW);
  }
  else
  {
    digitalWrite(pinDot, (index7Segment == posisiTitik)?CCorCA?LOW:HIGH:CCorCA?HIGH:LOW);
  }
  digitalWrite(pin7SegmentCommon[index7Segment], CCorCA?HIGH:LOW);
  lastScanIndex = index7Segment++;
  if(index7Segment > 4)index7Segment = 0;
}
void drive7Segment(byte nilai)
{
  byte nilai7Segment = CCorCA?~angka[nilai]:angka[nilai];
  for(byte i=0;i<sizeof(pin7Segment);i++)
  {
    digitalWrite(pin7Segment[i], nilai7Segment & 0x01);
    nilai7Segment >>= 1;
  }
}
uint32_t Convert_IntToBCD32(uint32_t DecimalValue)
{
    uint32_t returnValue = 0;
    //uint32_t LSB_L = DecimalValue;
 
    while (DecimalValue >= 10000000L)
    {
        DecimalValue -= 10000000L;
        returnValue += 0x10000000;
    }
    while (DecimalValue >= 1000000L)
    {
        DecimalValue -= 1000000L;
        returnValue += 0x01000000;
    }
    while (DecimalValue >= 100000L)
    {
        DecimalValue -= 100000L;
        returnValue += 0x00100000;
    }
    while (DecimalValue >= 10000)
    {
        DecimalValue -= 10000;
        returnValue += 0x00010000;
    }
    while (DecimalValue >= 1000L)
    {
        DecimalValue -= 1000L;
        returnValue += 0x00001000;
    }
    while (DecimalValue >= 100)
    {
        DecimalValue -= 100;
        returnValue += 0x00000100;
    }
    while (DecimalValue >= 10)
    {
        DecimalValue -= 10;
        returnValue += 0x00000010;
    }
    return returnValue + DecimalValue;
}

Library yang digunakan dalam perakitan timbangan arduino:

  1. HX711.zip
  2. TimerOne.zip

Keamanan berlapis akses pintu menggunakan sandi keypad dan sidik jari menggunakan Code Vision

Sistem keamanan merupakan bagian sistem yang bertugas memberikan akses terhadap bagian-bagian yang dilindunginya. Jika unit yang dilindungi memiliki fungsi sangat vital yang hanya di boleh diakses oleh orang tertentu maka sistem keamanannya dibuat berlapis.

Infrastruktur sistem keamanan sudah sangat berkembang, beberapa yang sering digunakan pada aplikasi mikrokontroller diantaranya :

  1. Kata sandi dengan tombol/keypad/remot tv
  2. Kartu akses dengan RFID reader
  3. Sidik jari
  4. suara dengan voice recognition

Dalam perancangan dini menggunakan 2 lapis sistem keamanan yaitu kata sandi menggunakan keypad serta sidik jari.

Komponen yang digunakan:

  1. ATMega8535
  2. Keypad membrane 4×4
  3. Fingerprint dy50
  4. LCD I2c 16×2
  5. Solenoid doorlock
  6. Buzzer

Skema perancangan sistem keamanan berlapis (password dan fingerprint):
 

program code vision (cvavr) sistem keamanan menggunakan finger print dan keypad:

#include <mega8535.h>
#include <stdbool.h>
#include "fingerprint.h"
#include "lcdi2c.h"

// Declare your global variables here
#define password    "1234"
#define pinBuzzer   PORTD.3
#define pinKunci    PORTD.2

char buf[10];
uint8_t respon;

uint16_t timingFingerprintAktif;
uint16_t i;

char keypad;
char keypadBuffer[10];
bool statusPassword;
uint8_t keypadCnt;


#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)

// USART Receiver buffer
#define RX_BUFFER_SIZE 20
char rx_buffer[RX_BUFFER_SIZE];

#if RX_BUFFER_SIZE <= 256
unsigned char rx_wr_index=0,rx_rd_index=0;
#else
unsigned int rx_wr_index=0,rx_rd_index=0;
#endif

#if RX_BUFFER_SIZE < 256
unsigned char rx_counter=0;
#else
unsigned int rx_counter=0;
#endif

// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index++]=data;
#if RX_BUFFER_SIZE == 256
   // special case for receiver buffer size=256
   if (++rx_counter == 0) rx_buffer_overflow=1;
#else
   if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      }
#endif
   }
}

#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index++];
#if RX_BUFFER_SIZE != 256
if (rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#endif
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif


char scanning_keypad()
{
    PORTB = 0b01111111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return 'A';}
    if(PINB.1 == 0){while(PINB.1 == 0);return 'B';}
    if(PINB.2 == 0){while(PINB.2 == 0);return 'C';}
    if(PINB.3 == 0){while(PINB.3 == 0);return 'D';}
     
    PORTB = 0b10111111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '3';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '6';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '9';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '#';}
     
    PORTB = 0b11011111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '2';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '5';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '8';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '0';}
     
    PORTB = 0b11101111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '1';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '4';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '7';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '*';}    
    
    return 0;
     
}


// Standard Input/Output functions
#include <stdio.h>

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);

// Port B initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);

// Port C initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=Out Bit3=In Bit2=In Bit1=In Bit0=In 
DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (1<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
// State: Bit7=T Bit6=T Bit5=T Bit4=0 Bit3=T Bit2=T Bit1=T Bit0=T 
PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

// Port D initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (1<<DDD3) | (1<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=0xFF
// OC0 output: Disconnected
TCCR0=(0<<WGM00) | (0<<COM01) | (0<<COM00) | (0<<WGM01) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0<<AS2;
TCCR2=(0<<WGM20) | (0<<COM21) | (0<<COM20) | (0<<WGM21) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;
OCR2=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (0<<TOIE0);

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=(0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
MCUCSR=(0<<ISC2);

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 57600
UCSRA=(0<<RXC) | (0<<TXC) | (0<<UDRE) | (0<<FE) | (0<<DOR) | (0<<UPE) | (0<<U2X) | (0<<MPCM);
UCSRB=(1<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (1<<RXEN) | (1<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
UCSRC=(1<<URSEL) | (0<<UMSEL) | (0<<UPM1) | (0<<UPM0) | (0<<USBS) | (1<<UCSZ1) | (1<<UCSZ0) | (0<<UCPOL);
UBRRH=0x00;
UBRRL=0x08;

// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator's positive input is
// connected to the AIN0 pin
// The Analog Comparator's negative input is
// connected to the AIN1 pin
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
SFIOR=(0<<ACME);

// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);

// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);

// TWI initialization
// TWI disabled
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);

// Global enable interrupts
#asm("sei")

i2c_begin();
lcd_begin(0x27,16,2); // alamat lcd i2c
lcd_clear();
lcd_puts("Sistem Keamanan");
lcd_gotoxy(0,1);
lcd_puts("www.semesin.com");
delay_ms(3000);

lcd_clear();
//rx_wr_index = 15;
fingerPrintBegin((uint8_t*)&rx_buffer, &rx_wr_index);
respon = checkPassword();
//sprintf(buf, "%2X", respon);
lcd_gotoxy(0,0);
lcd_puts("Akses terbatas  ");

delay_ms(2000);

keypadCnt = 0;

while (1)
      {
      // Place your code here
            while(1)
            {      
                if(!statusPassword)
                {
                    keypad = scanning_keypad();
                    if(keypad)
                    {
                        if(keypad == '#')
                        {           
                            keypadBuffer[keypadCnt] = 0; 
                                         
                            lcd_clear();              
                            lcd_gotoxy(0,0);
                            if(strcmp(keypadBuffer, password) == 0)
                            {
                                statusPassword = 1;          
                                timingFingerprintAktif = 30000;
                                lcd_puts("Tempel sidikjari");
                            }
                            else    
                            {
                                statusPassword = 0;
                                lcd_puts("Password salah  ");
                                for(i=0;i<3;i++)
                                {
                                    pinBuzzer = 1;
                                    delay_ms(1000);
                                    pinBuzzer = 0;
                                    delay_ms(1000);
                                }
                                lcd_clear();
                                lcd_puts("Akses terbatas  ");
                                
                            }                      
                            keypadCnt = 0;
                    		
                        }
                        if((keypad >= '0') &&(keypad <= '9')) 
                        {            
                            if(keypadCnt == 0)
                            {
                                lcd_gotoxy(0,0);
                                lcd_puts("Password :       ");
                            }                     
                            if(keypadCnt < 4)
                            {
                                lcd_gotoxy(keypadCnt,1);
                                lcd_send_data(keypad);          
                                keypadBuffer[keypadCnt] = keypad;
                                keypadCnt++;
                            }  
                    	
                        }
                    	
                    }          
                }
                else
                {
                    delay_ms(1);
                    timingFingerprintAktif--;
                    if(!timingFingerprintAktif)
                    {
                        statusPassword = 0;
                        pinBuzzer = 1;
                        delay_ms(1000);
                        pinBuzzer = 0;    
                        lcd_clear();
                        lcd_puts("Panel           ");  
                        
                    }   
                    lcd_gotoxy(0,1);  
                    lcd_puts("Tempelkan jari  ");
                    respon = getImage();
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }  

                    lcd_gotoxy(0,1);  
                    lcd_puts("Konversi gambar ");
                    respon = image2Tz(1);
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }  
                    
                    lcd_gotoxy(0,1);  
                    lcd_puts("Mencari id      ");
                    respon = fingerFastSearch();
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }                     
                    lcd_clear();
                    sprintf(buf, "id = %2d", fingerID);
                    lcd_gotoxy(0,1);  
                    lcd_puts(buf);
                    lcd_gotoxy(0,0);
                    lcd_puts("Panel");
                                        
                    pinKunci = 1;
                    delay_ms(5000);
                    pinKunci = 0; 
                }
            }
      }
}

library:

  1. fingerprint.h
  2. lcdi2c.h

Mesin antrian dmd dilengkapi suara dengan arduino

Mesin antrian adalah sistem informasi antara meja layanan dan pengunjung untuk mengatur waktu pelayanan terhadap pengunjung sesuai urutan kedatangan. Sistem ini terdiri atas media, dan tombol panggil, bisa juga ditambahkan dengan tombol tiket (dengan cetak tiket). Media informasi seperti seven segment, running text (dmd), monitor, suara yang diletakkan di ruang tunggu yang terlihat/terdengar di kursi pengunjung yang sedang antri.

Mesin antrian dalam proyek ini memiliki fitur berikut :

  1. Tombol panggil 4 buah dengan fitur cascade yaitu masing-masing tombol dapat ditekan bersamaan tanpa harus menunggu proses panggilan meja operator lain.
  2. Tombol tiket 1 buah dan dilengkapi cetak tiket
  3. Tombol reset untuk mengembalikan ke keadaan awal
  4. Media dot matik P10 sebagai papan informasi.
  5. Suara panggil mp3 berupa nomor antrian dan nomor loket

Skema Mesin antrian arduino :

Komponen yang digunakan dalam membuat mesin antrian menggunakan dmd (dot matrix display) berbasis arduino :

  1. Arduino uno
  2. DMD P10
  3. Printer thermal
  4. DF Player mini mp3, speaker, resistor
  5. Tombol

Sketch / koding sistem antrian arduino :

#define pinTombolPanggil1   A0
#define pinTombolPanggil2   A1
#define pinTombolPanggil3   A2
#define pinTombolPanggil4   A3

#define pinTombolReset      A4
#define pinTombolTiket      A5

#define pinMP3Busy          2

#define belas               12 //belas.mp3
#define puluh               13 //puluh.mp3
#define seratus             14 //seratus.mp3
#define ratus               15 //ratus.mp3
#define seribu              16 //seribu.mp3
#define ribu                17 //ribu.mp3
#define koma                18 //Koma.mp3

#define antrianNomor        101
#define silahkanKeCS        102

#include <SPI.h>
#include <DMD_Semesin.h>
#include <fonts/angka6x14ABCD.h>
#include <DFPlayer_Mini_Mp3.h>
#include "Adafruit_Thermal.h"


byte nomorAntrianDaftar;
byte nomorAntrianPanggil;
byte nomorCSPanggil;

SPIDMD dmd(1, 1);
SoftwareSerial serialPrinter(4, 5);
Adafruit_Thermal printer(&serialPrinter);

struct DataAntrian
{
  uint16_t counterTiket;
  uint16_t counterPanggil;
  uint8_t CS;
  bool dataTombolTekan[4];
};

DataAntrian dataAntrian;
byte pinTombolPanggil[] = {pinTombolPanggil1, pinTombolPanggil2, pinTombolPanggil3, pinTombolPanggil4};
char buffer[20];

bool statusTombolTiket;
byte statusTombolPanggil[4];


void setup() {
  pinMode(pinTombolPanggil1, INPUT_PULLUP);
  pinMode(pinTombolPanggil2, INPUT_PULLUP);
  pinMode(pinTombolPanggil3, INPUT_PULLUP);
  pinMode(pinTombolPanggil4, INPUT_PULLUP);

  pinMode(pinTombolReset, INPUT_PULLUP);
  pinMode(pinTombolTiket, INPUT_PULLUP);

  Serial.begin(9600);

  Serial.println(F("Mesin antrian arduino"));
  Serial.println(F("https://www.semesin.com/project"));

  mp3_set_serial (Serial);
  mp3_set_volume(30);
  Serial.println();

  serialPrinter.begin(9600);
  printer.begin();
  printer.sleep();

  dmd.clearScreen();
  dmd.setBrightness(255);
  dmd.selectFont(angka6x14ABCD);
  dmd.begin();
}

void loop() {

  bacaTombol();

  if (!digitalRead(pinTombolReset))
  {
    delay(100);
    if (!digitalRead(pinTombolReset))
    {
      dataAntrian.counterTiket = 0;
      dataAntrian.counterPanggil = 0;
      dataAntrian.CS = 0;
      dataAntrian.dataTombolTekan[0] = 0;
      dataAntrian.dataTombolTekan[1] = 0;
      dataAntrian.dataTombolTekan[2] = 0;
      dataAntrian.dataTombolTekan[3] = 0;

      dmd.clearScreen();
      Serial.println("Reset");
      while (!digitalRead(pinTombolReset));
    }
  }

  for (byte i = 0; i < sizeof(pinTombolPanggil); i++)
  {
    if (dataAntrian.counterTiket > dataAntrian.counterPanggil)
    {
      if (dataAntrian.dataTombolTekan[i])
      {
        dataAntrian.counterPanggil++;
        dataAntrian.CS = i + 1;

        sprintf(buffer, "%02d>%1d", dataAntrian.counterPanggil, dataAntrian.CS);
        dmd.drawString(2, 1, buffer);

        Serial.print("call=");
        Serial.println(dataAntrian.counterPanggil);
        Serial.print("cs=");
        Serial.println(dataAntrian.CS);

        mp3_play_and_wait(antrianNomor);
        suaraBilangan(dataAntrian.counterPanggil);
        mp3_play_and_wait(silahkanKeCS);
        suaraBilangan(dataAntrian.CS);

        Serial.println();
        dataAntrian.dataTombolTekan[i] = false;
      }
    }
  }
}

void suaraBilangan(uint32_t Bilangan)
{
  if (Bilangan < 100)
  {
    suaraPuluhan(Bilangan);
  }
  else if (Bilangan < 1000)
  {
    suaraRatusan(Bilangan);
  }
  else
  {
    suaraRibuan(Bilangan);
  }
}
void suaraPuluhan(uint8_t Bilangan)
{
  if (Bilangan < 12)
  {
    mp3_play_and_wait(Bilangan);
  }
  else if (Bilangan < 20)
  {
    mp3_play_and_wait(Bilangan - 10);
    mp3_play_and_wait(belas);
  }
  else
  {
    uint8_t puluhan = Bilangan / 10;
    mp3_play_and_wait(puluhan);
    mp3_play_and_wait(puluh);

    puluhan *= 10;
    if (Bilangan - puluhan != 0)
    {
      mp3_play_and_wait((Bilangan - puluhan));
    }
  }
}
void suaraRatusan(uint16_t Bilangan)
{
  uint8_t ratusan = (uint8_t)(Bilangan / 100);
  if (ratusan == 1)
  {
    mp3_play_and_wait(seratus);
  }
  else
  {
    mp3_play_and_wait(ratusan);
    mp3_play_and_wait(ratus);
  }
  if (Bilangan % 100)
  {
    suaraPuluhan(Bilangan - (ratusan * 100));
  }
}
void suaraRibuan(uint32_t Bilangan)
{
  uint16_t ribuan = (uint16_t)(Bilangan / 1000);
  if (ribuan == 1)
  {
    mp3_play_and_wait(seribu);
  }
  else if (ribuan < 100)
  {
    suaraPuluhan(ribuan);
    mp3_play_and_wait(ribu);
  }
  else
  {
    suaraRatusan(ribuan);
    mp3_play_and_wait(ribu);
  }
  if (Bilangan % 1000)
  {
    suaraRatusan(Bilangan - (ribuan * 1000));
  }
}

void mp3_play_and_wait(uint16_t num) {
  mp3_play (num);
  delay(200);
  while (!digitalRead(pinMP3Busy))
  {
    bacaTombol();
  }
}

void bacaTombol()
{
  if (!digitalRead(pinTombolTiket))
  {
    if (!statusTombolTiket)
    {
      delay(100);
      if (!digitalRead(pinTombolTiket))
      {
        dataAntrian.counterTiket++;
        printer.wake();
        printer.setDefault();

        printer.justify('C');
        printer.println("Nomor Antrian");
        printer.doubleHeightOn();
        printer.println(dataAntrian.counterTiket);
        printer.doubleHeightOff();
        printer.println("Terima kasih");
        printer.feed(2);
        printer.sleep();

        Serial.print("ambil tiket : ");
        Serial.println(dataAntrian.counterTiket);

        statusTombolTiket = true;
      }
    }
  }
  else
  {
    statusTombolTiket = false;
  }

  for (byte i = 0; i < sizeof(pinTombolPanggil); i++)
  {
    if (!digitalRead(pinTombolPanggil[i]))
    {
      if (!statusTombolPanggil[i])
      {
        delay(100);
        if (!digitalRead(pinTombolPanggil[i]))
        {
          dataAntrian.dataTombolTekan[i] = true;
          statusTombolPanggil[i] = true;

          Serial.print("TombolPanggil:");
          Serial.println(i);
        }
      }
    }
    else
    {
      statusTombolPanggil[i] = false;
    }
  }
}

library :

suara mp3

  1. mp3 mesin antrian arduino.zip

Pengisi galon otomatis menggunakan arduino

Depot pengisian air minum menggunakan galon sebagai tempat air. Tahap-tahap pengisian yang lazim di lakukan adalah: pembersihan, pengisian, pemasangan tutup dan perbersihan.

Dalam artikel ini hanya melakukan otomatisasi pada tahap pengisian, cara kerjanya sebagai berikut :

  1. Saat mulai, sensor ir/obstacle mendeteksi keberadaan galon diatas tempat pengisian galon. Jika ada galon maka sistem menginformasikan agar galon diambil terlebih dahulu.
  2. Sistem menunggu hingga galon kosong ditempatkan pada posisi pengisian galon.
  3. Sistem akan menghidupkan pompa air, kemudian menghidupkan solenoid.
  4. Sensor flow meter akan menghitung volume ait yang dialirkan kedalam galon.
  5. Jika volume telah mencukupi maka solenoid dan pompa dimatikan.
  6. Jika selama pengisian galon, posisi galon bergeser atau diambil, maka solenoid dan pompa akan dimatikan.
  7. Sistem akan menunggu galon diambil.
  8. Selesai.

Skema pengisian galon otomatis berbasis arduino:

Komponen yang digunakan dalam perancangan sistem pengisian galon arduino:

  1. Arduino uno
  2. LCD 16×2 lcd backpack
  3. sensor obstacle
  4. flow sensor
  5. solenoid
  6. pompa air
  7. relay 2 channel

Program pengisi galon menggunakan arduino:

#define kapasitasGalon      19//liter
#define pulsaPerLiter       450//sesuai spesifikasi sensor water flow

#define pinWaterFlow        2
#define pinSensorIR         A0
#define pinPompa            7
#define pinSelenoid         6

#define IRAktif             LOW
#define relayAktif          LOW

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <util/atomic.h>

LiquidCrystal_I2C lcd(0x3F, 16, 2);
volatile uint16_t pulseCount;

void setup() {
  digitalWrite(pinPompa, !relayAktif);
  digitalWrite(pinSelenoid, !relayAktif);
  pinMode(pinWaterFlow, INPUT);
  pinMode(pinSensorIR, INPUT);
  pinMode(pinPompa, OUTPUT);
  pinMode(pinSelenoid, OUTPUT);

  Serial.begin(9600);
  Serial.println(F("Pengisi galon otomatis"));
  Serial.println(F("https://www.semesin.com/project"));


  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();

  lcd.backlight();
  lcd.print("Pengisi galon");
  lcd.setCursor(0, 1);
  lcd.print("Semesin.com");
  delay(3000);


  lcd.setCursor(0, 0);
  lcd.print("ambil Galon     ");
  while (digitalRead(pinSensorIR) == IRAktif); //deteksi awal : jika ada galon kosongkan dahulu

  attachInterrupt(digitalPinToInterrupt(pinWaterFlow), pulseCounter, FALLING);
}

void loop() {
  lcd.setCursor(0, 0);
  lcd.print("Letakkan Galon  ");
  while (digitalRead(pinSensorIR) == !IRAktif);

  delay(1000);//memastikan galon diletakkan

  if (digitalRead(pinSensorIR) == IRAktif)
  {
    lcd.setCursor(0, 0);
    lcd.print("Mengisi Galon...");

    pulseCount = 0;
    digitalWrite(pinPompa, relayAktif);
    delay(1000);
    digitalWrite(pinSelenoid, relayAktif);

    lcd.setCursor(0, 1);
    lcd.print("                ");
    uint16_t jumlahPulsa;

    while ((jumlahPulsa < kapasitasGalon * pulsaPerLiter) && (digitalRead(pinSensorIR) == IRAktif))
    {
      ATOMIC_BLOCK(ATOMIC_FORCEON)
      {
        jumlahPulsa = pulseCount;
      }

      lcd.setCursor(0, 1);
      lcd.print((1.0 * jumlahPulsa / pulsaPerLiter));
      lcd.print(" liter");
    }

    digitalWrite(pinSelenoid, !relayAktif);
    delay(100);
    digitalWrite(pinPompa, !relayAktif);

    lcd.setCursor(0, 0);
    if (jumlahPulsa >= kapasitasGalon * pulsaPerLiter)
    {
      lcd.print("Galon penuh     ");
    }
    else if (digitalRead(pinSensorIR) != IRAktif)
    {
      lcd.print("Galon tidak ada ");
    }

    delay(1000);
    lcd.setCursor(0, 0);
    lcd.print("silahkan ambil  ");

    while (digitalRead(pinSensorIR) == IRAktif);
    delay(1000);//memastikan galon sudah diambil
    while (digitalRead(pinSensorIR) == IRAktif);
  }
}

void pulseCounter()
{
  pulseCount++;
}

Library: