Archive for the ‘Otomasi dan Robotika’ Category


Assalamu’alaikum Wr. Wb.

hasil akhir

hasil akhir

alamat server slave di ralat di http://appscenter.aio.co.id/weatherlogger/

alamat master logger di http://weatherlogger.ap.ngrok.io/

Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/
  7. https://tarecha.wordpress.com/2017/07/25/project-weather-logger-part-6-plot-graphic/
  8. https://tarecha.wordpress.com/2017/07/29/project-weather-logger-part-7-ngrok-http-tunneling/
  9. https://tarecha.wordpress.com/2017/08/27/project-weather-logger-part-8-ganti-sensor-bme280/
  10. https://tarecha.wordpress.com/2017/09/03/project-weather-logger-part-9-sync-data/
  11. https://tarecha.wordpress.com/2018/01/13/project-weather-logger-part-10-notifikasi-email-dan-autostart-dan-backup/

oke semakin kesini IoT akan semakin rumit dan kompleks. ya sebenernya bikin ini gk bisa langsung jadi uang berapa. tapi setiap percobaan akan menambah skill kita dalam koding dan analisa alur sistem.

untuk bikin BOT Twitter kita harus mengaktifkan aplikasinya dahulu . saya ambil contoh tutorial berikut untuk mengaktifkan twitter. http://nodotcom.org/python-twitter-tutorial.html

1. Mematikan notif sms

hal ini penting agar bot kita banyak tweet gk perlu sms. cukup menganggu nanti. coba akses https://twitter.com/settings/devices dan matikan notifikasinya dengan login dahulu

matikan Notifikasi SMS

matikan Notifikasi SMS

2. buat aplikasi tweet

Twitter Apps -> Create New App -> Leave Callback URL empty -> Create your Twitter application.

Create Aplication

Create Aplication

3. ganti permission tweet write.

masuk ke aplikasi yang sudah dibuat dan masuk ke tab permission

Ganti Write

Ganti Write

4. ambil token dan generate access

sama seperti yang link tutorial diatas. untuk membuat access key klik generate customer key and secret dan generate access token.

Access Token, Access Token Secret, Consumer Key (API Key), Consumer Secret (API Secret). yang dibutuhkan adalah value dari id berikut. dan ingat ini rahasia

Get Token

Get Token

5. buat file tweet universal sebagai modul

untuk bisa pakai paket tweepy maka harus install dahulu. caranya di windows lumayan gampang. caranya pakai pip install

Install modul Tweepy dengan PIP Install

Install modul Tweepy dengan PIP Install

 

buat file python sebagai berikut. meski saya tahu ada double nya karena ada penulisan yang double buat akses key nya. tapi yang penting jalan dulu. dari tutorial asli diatas saya tambahkan kirim tweet gambar sehingga bisa tweet gambar lalu menghapus gambar yang sudah di tweet.

simpan dengan nama tweetsend.py

import tweepy
import os

def get_api(cfg):
  auth = tweepy.OAuthHandler(cfg['consumer_key'], cfg['consumer_secret'])
  auth.set_access_token(cfg['access_token'], cfg['access_token_secret'])
  return tweepy.API(auth)

def kirimtweet(isi):
  # Fill in the values noted in previous step here
  cfg = { 
    "consumer_key"        : "customer key dari step diatas",
    "consumer_secret"     : "customer screet dari step diatas",
    "access_token"        : "access token dari step diatas",
    "access_token_secret" : "access token key dari step diatas" 
    }

  api = get_api(cfg)
  tweet = isi
  status = api.update_status(status=tweet) 
  # Yes, tweet is called 'status' rather confusing


def kirimtweetgambar(isi,gambarpath):
  # Fill in the values noted in previous step here
  cfg = { 
    "consumer_key"        : "customer key dari step diatas",
    "consumer_secret"     : "customer screet dari step diatas",
    "access_token"        : "access token dari step diatas",
    "access_token_secret" : "access token key dari step diatas" 
    }

  api = get_api(cfg)
  tweet = isi
  status = api.update_with_media(gambarpath,status=tweet)
  os.remove(gambarpath)

6. membuat tweet dengan gambar
ini bagian yang lumayan sulit karena belum nemu caranya. tapi akhirnya nemu juga.
pertama ==>install selenium pakai pip install. karena punya saya sudah ada maka tulisannya sudah ada . dan schedule juga sekalian

PIP Instal selenium dan schedule

PIP Instal selenium dan schedule

kedua ==> download firefox driver gecko https://github.com/mozilla/geckodriver/releases

Download sesuai OS

Download sesuai OS

kemudian exstrak geckodriver.exe di folder yang sama dengan file python untuk tweet nya. misal di document C:\Users\<bama user>\Desktop\program python agung

buka advanced tab. environment variabel. di pathnya masukkan tambahi ” C:\Users\<bama user>\Desktop\program python agung; ”

Penambahan path environment variable

Penambahan path environment variable

 

bikin coding berikut di folder yang sama dengan tweetsend.py karena nanti import file tersebut

 


from selenium import webdriver
import tweetsend
import datetime
import schedule
import time
import sys

global waktu

i=0
global jumtweet
jumtweet = 0
jadwal = "23:59"


def job():
    try:
        waktu = datetime.datetime.now().strftime('%Y-%m-%d')
        path = 'C:\\Users\\mtarecha\\Desktop\\program python agung\\firefoximage.png'        
        source = 'http://appscenter.aio.co.id/weatherlogger/grafiklast24hourdate.php?node=N1&tgl='+ waktu
        pesan = 'Selamat malam, berikut informasi cuaca di Rumah selama 24 jam terakhir detail di '+source
       
       
        driver = webdriver.Firefox()
        driver.set_window_size(800, 800)
        driver.implicitly_wait(5)
        driver.get(source)
 
       
        driver.save_screenshot(path)
        driver.quit()   
        tweetsend.kirimtweetgambar(pesan,path)
        global jumtweet
        jumtweet += 1
    except Exception as e:
        print 'Error Message '+ str(e)
        print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
        print "General Error Unexpected error:", sys.exc_info()[0]
        
    
schedule.every().day.at(jadwal).do(job)

while True:
    schedule.run_pending()
    print i
    i+=1
    print 'Jumlah kirim '+str(jumtweet)
    time.sleep(2)



hasilnya akan tweet jam 23:59 dengan gambar berikut. nanti di counting sudah tweet berapa kali

Tweet BOT

Tweet BOT

 

7. membuat file grafiklast24hour dengan input tanggal sehingga ketika di tweet gambar ada link yang bisa diklik dan menampilkan grafik informatif javascript nya . sebenarnya sama dengan grafiklast24hour.php cman dikasi parameter tanggal saat ini
berikut grafiklast24hourdata.php

<?php include "header.php"; include "config.php"; if(isset($_GET['node'])&&isset($_GET['tgl'])) { $idnode = $_GET['node']; $tgl = $_GET['tgl']; } $query = "select unix_timestamp(waktu)*1000 as x,temp,hum,press,waktu from masterdata where idnode='$idnode' and date(waktu)= '$tgl' order by waktu asc"; $querysummary = "select min(temp) as mintemp, round(avg(temp),2) as avgtemp, max(temp) as maxtemp, min(hum) as minhum, round(avg(hum),2) as avghum, max(hum) as maxhum, min(press) as minpress, round(avg(press),2) as avgpress, max(press) as maxpress from masterdata where idnode='$idnode' and date(waktu)= '$tgl' order by waktu asc"; $data = mysqli_query($conn, $query); $dataPointstemp = array(); $dataPointshum = array(); $dataPointspress = array(); while ($row = mysqli_fetch_array($data, MYSQL_ASSOC)) { array_push($dataPointstemp,array('x'=>$row['x'],'y'=>$row['temp']));
	array_push($dataPointshum, array('x'=>$row['x'],'y'=>$row['hum']));
	array_push($dataPointspress, array('x'=>$row['x'],'y'=>$row['press']));
}

$lastTemp = end($dataPointstemp)['y'];
$lastHum = end($dataPointshum)['y'];
$lastPress = end($dataPointspress)['y'];
//ambil tanggal start date dan end date
$firstupdateU = reset($dataPointstemp)['x']/1000;
$lastupdateU =end($dataPointstemp)['x']/1000;
$firstupdate = date("d F Y H:i:s",$firstupdateU);
$lastupdate = date("d F Y H:i:s",$lastupdateU);

//bikin strip garis pembagi per pukul 00:00
$firstupdatetengahmalam = date("d F Y  H:i:s",$firstupdateU);
$begin = (new DateTime( $firstupdatetengahmalam ))->setTime(0,0);
$end = new DateTime( $lastupdate );
$begin = $begin->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

$datasummary = mysqli_query($conn, $querysummary);
$summary = mysqli_fetch_assoc($datasummary);



?>

<body style="padding-top: 3px;">




<div id="chartContainer"></div>


<div style="position: absolute;top:420px;left:50px;">
<?php include "summary.php"; ?>
</div>

</body>

bagian bawah dari file yang sama



<script type="text/javascript">
$(function () {
    var chart = new CanvasJS.Chart("chartContainer", {
        theme: "theme",
        zoomEnabled: true,
		exportEnabled: true,
        animationEnabled: false,
		axisY: 
		{
			title: "Suhu C dan Kelembapan %",
            titleFontFamily: "arial",
            titleFontSize: 12,
            includeZero: false,
			suffix: " C / %"
        },
		axisY2: 
		{
            title: "Pressure hPa",
            titleFontFamily: "arial",
            titleFontSize: 12,
            includeZero: false,
			lineColor: "#86b402",
            suffix: " hPa"            
        },
        title: 
		{
            text: "Graphic Weather Logger of <?php echo $idnode;?>"
        },
	subtitles:[
		{
			text: "by Mochamad Agung Tarecha"
		
			
		},
		{
			
			text: "<?php echo "Time range from ".$firstupdate ." to ". $lastupdate;?>"
			
		}
		],
		
		axisX:
		{   stripLines: [
		<?php
			foreach($daterange as $date)
			{
			
						
				echo "{";
				echo "value: ".(string)$date->format("U").'000,';			
				echo "showOnTop: false";           
				echo "},";
			}
		?>
            
            ],
			valueFormatString: "DD MMM YYYY HH:mm"
		},
		toolTip: 
		{
            shared: true
        },
        data: [
        {
			
            type: "line",
			xValueType: "dateTime",
			xValueFormatString:"DD MMM YYYY HH:mm:ss",
			showInLegend: true,		
			name: "Suhu C",							
            dataPoints: <?php echo json_encode($dataPointstemp, JSON_NUMERIC_CHECK); ?>
        },
		{
			
            type: "line",
			xValueType: "dateTime",
			xValueFormatString:"DD MMM YYYY HH:mm:ss",
			showInLegend: true,
			name: "Humidity %",			
            dataPoints: <?php echo json_encode($dataPointshum, JSON_NUMERIC_CHECK); ?>
        },
		
	{
            type: "line",
            axisYType: "secondary",
			xValueType: "dateTime",
			xValueFormatString:"DD MMM YYYY HH:mm:ss",	
            name: "Pressure hPa",
			showInLegend: true,
            dataPoints: <?php echo json_encode($dataPointspress, JSON_NUMERIC_CHECK); ?>
        }
		
        ],
		legend: {
                cursor: "pointer",
				horizontalAlign: "center",
				verticalAlign: "bottom",
                itemclick: function (e) {
                    if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
                        e.dataSeries.visible = false;
                    }
                    else {
                        e.dataSeries.visible = true;
                    }
                    chart.render();
                }
            }
    });
    chart.render();
});
</script>
</html>

berikut hasil akhirnya

hasil akhir

hasil akhir

Iklan

Assalamu’alaikum Wr. Wb.

setup autostart

setup autostart

 

alamat server slave di ralat di http://appscenter.aio.co.id/weatherlogger/

alamat master logger di http://weatherlogger.ap.ngrok.io/

Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/
  7. https://tarecha.wordpress.com/2017/07/25/project-weather-logger-part-6-plot-graphic/
  8. https://tarecha.wordpress.com/2017/07/29/project-weather-logger-part-7-ngrok-http-tunneling/
  9. https://tarecha.wordpress.com/2017/08/27/project-weather-logger-part-8-ganti-sensor-bme280/
  10. https://tarecha.wordpress.com/2017/09/03/project-weather-logger-part-9-sync-data/

 

udah lama gk posting . langsung saja di rapel deh.

ada 3 bahasan yaitu gmn saat raspi start dia langsung run program nya karena kadang listrik rumah mati dan baru saya run lain setelah pulang, kalau sekarang sih sudah ada ups nya. tapi biar lebih otomatis saja. kedua notifikasi email bila sensor nya gk konek atau diluar threshold suhu yang kita tentukan. ini berguna untuk ruangan yang harus terkontrol suhu dan kelembapan atau tekanannya tapi disini saya pakai suhu saja. ketiga backup semua file nya dan menyimpan ke nas server milik saya. jadi di klik maka akan backup data.

untuk sync nya saya sudah buat sync 2 server. 1 server pabrik 1 server nas saya qnap ada webserver php nya. kebetulan kompatibel. tapi sama seperti dulu kok. tulisan ini akan membosankan buat pengguna awam programming. buat dokumentasi pribadi saja

1. autostart

caranya edit file autostart, ternyata tidak semua bisa di autostart. yang ngrokstart.sh gk berhasil. gtw kenapa. tapi yang penting ambil data dan sync nya jalan gk masalah. filenya ada di gambar dibawah

2.

setup autostart

setup autostart

2. notifikasi email

seperti disampaikan diawal ini untuk ngirim notif email. kode nya tidak serapi yang smart pdu sih karena ini pertama bikin. mau ganti males. yang penting udah jalan

berikut hasilnya

notif email

notif email

import socket
import sys
import datetime
import time
import urllib2
import urllib
import math
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

fromaddr = "smartdo*******@gmail.com"
password = "**********"
port = 587
toaddr = ['mtarecha@aio.co.id','agung.tarecha@gmail.com']
mailserver ='smtp.gmail.com'


thresholdbawahsuhu = 25
thresholdatassuhu = 29
emailalertenable = 1
 
UDP_IP = "192.168.3.192"
UDP_PORT = 80
MESSAGE = "d"
urlserver = "http://192.168.3.199/insertdata.php"
key = "****sadasd888****"
namafilecsv = "rawdata6.csv"

print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
print "message:", MESSAGE
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
sock.settimeout(10) #dalam 10 detik setelah kirim tunggu data, kalau lewat bypass
data ={}
errordatasensor ='belum pernah NAN'
lasthum = -99999
lasttemp = -99999
lastpress = -99999
lastseennode = datetime.datetime.now()
idnode = 'N1'
i=1
while True:
         
        try :
                print '--------------------------------------------------------'
                print i
                i+=1
                #Set the whole string
                sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
                 
                # receive data from client (data, addr)
                d = sock.recvfrom(1024)
                #pemisahan data dengan karakter "," nanti buat save di mysql
                
                reply = d[0].split(",")
               
                
                curmin = datetime.datetime.now().time().minute
                cursec = datetime.datetime.now().time().second
                #curhour = datetime.datetime.now().time().hour
                curtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                lastseennode = datetime.datetime.now()
                print datetime.datetime.now()
                print 'Nodesensor reply : ' + str(reply) +' at '+curtime

                #kadang dht22 gagal baca dan menghasilkan nan (not a number)
                #jadi kalau gagal gunakan data dari pembacaan sebelumnya
                #untuk amannya kalau data bukan angka maka gunakan data sebelumnya
                #termasuk data dari sensor bmp280               
                if reply[1].replace('.','').isdigit():#dihilangkan koma dan titik lalu dicek apakah nomor saja
                        lasthum=reply[1]            
                        #ini adalah kondisi normal dimana data dibaca benar. 
                else:
                        errordatasensor = 'pernah NAN hum di waktu '+curtime+' '+reply[1]
                        reply[1]=lasthum
                        print 'Ada data NaN di dhthum gunakan last data '+str(lasthum)
                        
                        
                if reply[2].replace('.','').isdigit():
                        lasttemp=reply[2]
                        #ini adalah kondisi normal dimana data dibaca benar.
                        
                else:
                        errordatasensor = 'pernah NAN temp di waktu '+curtime+' '+reply[2]
                        reply[2]=lasttemp
                        print 'Ada data NaN di dhttemp gunakan last data '+str(lasttemp)
                         
                if reply[3].replace('.','').isdigit():
                        lastpress=reply[3]
                        #ini adalah kondisi normal dimana data dibaca benar.
                else:
                        errordatasensor = 'pernah NAN press di waktu '+curtime+' '+reply[3]
                        reply[3]=lastpress
                        print 'Ada data NaN di bmptemp gunakan last data '+str(lastpress)
                        
             

                        
                #cek isi apakah data terakhir benar
                #print 'lastdhthum '+lastdhthum
                #print 'lastdhttemp '+lastdhttemp
                #print 'lastbmptemp '+lastbmptemp
                #print 'lastbmppress '+lastbmppress
                print errordatasensor      
                  
                
                #tiap 10 menit simpan di file csv, karena dalam 10 menit lama
                #untuk mencegah save berulang di menit ke 10 dilihat detiknya
                #selama kurang dari 5 maka di save karena ada sleep 5 detiknya
                idnode = reply[0]
                if (curmin % 10 ==0)and (cursec < 5) : #if 1: #simpan menggunakan php data['k']= key data['iddata'] = 'auto' data['idnode'] = reply[0] data['hum'] = reply[1] data['temp'] = reply[2] data['press'] = reply[3] data['waktu'] = curtime data['sync1'] = 'false' data['sync2'] = 'false' dataformaturl = urllib.urlencode(data) #print dataformaturl requesttoserver = urllib2.urlopen(urlserver,dataformaturl,timeout=5) infosavephp = requesttoserver.read().split("#") #print infosavephp #print '==========================================================================' if(infosavephp[0]=='i')and(infosavephp[1]=='Berhasil Insert'): print 'Informasi Save ke Server : ID '+infosavephp[2]+' '+infosavephp[3] if(infosavephp[0]=='i')and(infosavephp[1]!='Berhasil Insert'):#selain berhasil print 'Gagal Insert Server : '+infosavephp[2] #karena gagal insert maka isi infosavephp[2] adalah sql nya untuk di cek #karena tetap pakai dimana infosavephp[2] harusnya berisi id auto increment maka diganti #untuk di save csv infosavephp[2] = 'No ID' with open(namafilecsv,'a') as file: file.writelines(infosavephp[2]+','+curtime+','+d[0]+'\n') #urutan csv nya iddataterakhir disave, waktu, data d (isi sensor) print 'data disimpan di csv ' +infosavephp[2]+' '+curtime+' '+str(reply) # print '==========================================================================' if emailalertenable : if (float(data['temp']) > thresholdatassuhu) or (float(data['temp']) < thresholdbawahsuhu):
                                        msg = MIMEMultipart()
                                        msg['From'] = fromaddr
                                        msg['To'] = ", ".join(toaddr)
                                        msg['Subject'] = "Weather Logger - Over Threshold Limit"
                                        server = smtplib.SMTP()
                                        server.connect(mailserver,port)                                       
                                        server.ehlo()
                                        server.starttls()                                        
                                        server.login(fromaddr,password)                              
                                        body = "Suhu ada di luar threshold "+ data['temp'] +" Celcius dengan threshold set " + str(thresholdbawahsuhu) +" Celcius hingga "+ str(thresholdatassuhu) +" Celcius pada "+str(curtime)
                                        body += " cek monitoring di http://appscenter.aio.co.id/weatherlogger/grafiklast24hour.php?node=N1"                                        
                                        msg.attach(MIMEText(body, 'plain'))
                                        text = msg.as_string()
                                        pesan = str(server.sendmail(fromaddr, toaddr, text))
                                        if pesan=='{}' :
                                                 print 'berhasil kirim email'
                                        else : 
                                                 print 'gagal kirim email'
                                        server.quit()       


                          
        except urllib2.HTTPError:
                print 'Tidak konek server'
        except urllib2.URLError:
                print 'URL Salah'
                
        except socket.error:
                print 'Tidak konek node'
                curmin = datetime.datetime.now().time().minute
                cursec = datetime.datetime.now().time().second
                #curhour = datetime.datetime.now().time().hour
                curtimelastseen = datetime.datetime.now()
                lastseensience = curtimelastseen - lastseennode                               
                print 'Lastseen Node '+str(lastseennode.strftime('%Y-%m-%d %H:%M:%S'))+ ' is '+str(lastseensience)[:-7]+' ago'
                if (curmin % 30 ==0)and (cursec <span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce_SELRES_start"></span>< 15) :
                #if True:
                        try :
                                if emailalertenable :                                        
                                        msg = MIMEMultipart()
                                        msg['From'] = fromaddr
                                        msg['To'] = ", ".join(toaddr)
                                        msg['Subject'] = "Weather Logger - Disconnected Node"
                                        server = smtplib.SMTP()
                                        server.connect(mailserver,port)                                       
                                        server.ehlo()
                                        server.starttls()
                                        server.login(fromaddr,password)
                                        body = "Node "+idnode+" tidak konek, Lastseen "+str(lastseennode.strftime('%Y-%m-%d %H:%M:%S'))+ ' is '+str(lastseensience)[:-7]+' ago'
                                        body += " cek monitoring di http://appscenter.aio.co.id/weatherlogger/grafiklast24hour.php?node=N1"                                        
                                        msg.attach(MIMEText(body, 'plain'))
                                        text = msg.as_string()
                                        pesan = str(server.sendmail(fromaddr, toaddr, text))
                                        if pesan=='{}' :
                                                print 'berhasil kirim email'
                                        else : 
                                                print 'gagal kirim email'
                                        server.quit()
                        except Exception as e:
                                print 'Error Message '+ str(e)
                                print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                                print "General Error Unexpected error:", sys.exc_info()[0]
                                #tidak pernah exit karena kalau tidak konek dilewati dan ngulang
                                #5 detik kemudian
                                #sys.exit()
        except Exception as e:
                print 'Error Message '+ str(e)
                print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                print "General Error Unexpected error:", sys.exc_info()[0]
        time.sleep(5)

3. backup. backup ini penting.

bisa backup di raspi sendiri atau dibackup di nas. karena saya punya nas maka saya pilih nas saja biar kalau raspinya bermasalah masih punya backup kode
intinya backup mysql nya lalu data2 web dan file python kemudian di zip

script backup

script backup

 

hasilnya akan banyak backup dengan nama waktu backup

proses backup

proses backup

 

 

hasil backup

hasil backup

 

kira2 seperti itu.

terima kasih

wassalamu’alaikum wr. wb.


Assalamu’alaikum Wr. Wb.

mau nulis nih. project yang lama terbengkalai yaitu ketika server cctv saya di pabrik ngehang dan butuh restart power manual sehingga butuh waktu sampai senin datang ke pabrik buat restart. bayangkan berapa jam downtime nya kalau down sejak jum’at malam ? kadang2 downnya sabtu sih. dan memang server yang cctv kadang butuh restart manual power source nya. hal ini yang melatar belakangi saya bikin smart PDU / Power Distribution Unit. oke di pasaran sudah ada sih.. yap merk APC. bisa merestart dengan meremote IP APC tersebut.

fiture remote restart

fiture remote restart.

saya mau  bikin hal yang sama atau lebih baik sedikit dalam segi feature. kalau pdu ini harus di restart dengan mengaksesnya terlebih dahulu. saya mau biki smart PDU dengan fitur

1. autoreset power bila target IP tidak merespon

2. kirim email notifikasi bila target IP telah direset power relaynya

3. simpan log untuk mengentahui catatan PDU restart dan responnya untuk investigasi kemudian hari

 

detailnya sepertinya dalam skema dan code. mungkin kurang menarik ya. tapi buat dokumentasi.

 

notif email

notif email

 

coding arduino node mcu

untuk detail instalasi dll bisa merefer ke node mcu weather logger saya kemarin

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>


WiFiUDP Udp;
byte localUdpPort = 80;  // local port to listen on
char incomingPacket[255];  // buffer for incoming packets
char  replyPacket[255];



//untuk server
const char* ssid = "tarecha";
const char* ssid2 = "aiocyber";
const byte buzzerPin = D8;
const byte relayPin1 = D1;
const byte relayPin2 = D2;
const byte inputPin = D7;
const char* password = "12345678a";
const String aHostname = "N3";
boolean relayStatus1 = HIGH;
boolean relayStatus2 = HIGH;
String infoNode = "";

void setup()
{
  
  pinMode(LED_BUILTIN, OUTPUT);   
  pinMode(relayPin1, OUTPUT);   
  pinMode(relayPin2, OUTPUT); 
  pinMode(buzzerPin, OUTPUT); 
  pinMode(inputPin, INPUT);
  digitalWrite(relayPin1,HIGH);
  digitalWrite(relayPin2,HIGH);
  
  Serial.begin(9600);
  
  Serial.println("start");
  WiFi.begin(ssid, password);
  //WiFi.begin(ssid2);
  //bagian setup wifi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.mode(WIFI_STA);
  WiFi.hostname(aHostname);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");

  }
  Serial.println(" connected");
  Udp.begin(localUdpPort);
  infoNode = (String)"Restart reason : " + ESP.getResetReason().c_str() + "\n MAC : " + WiFi.macAddress().c_str() + "\n Hostname : " + WiFi.hostname().c_str() + "\n Signal Stringth : " + WiFi.RSSI() +
             "\n Local IP : " + WiFi.localIP().toString().c_str() + "\n Port : " + localUdpPort + "\n CPU Freq : " + String(ESP.getCpuFreqMHz()) + " MHz\n Sketch Size : " + ESP.getSketchSize()
             + "\n Free Space Sketch Size : " + ESP.getFreeSketchSpace();



  Serial.println(infoNode);


  buzzerlampu();
}
void lampu()
{
   digitalWrite(LED_BUILTIN, LOW); 
   delay(500);  
   digitalWrite(LED_BUILTIN, HIGH); 
   delay(5); 
}
void buzzerlampu()
{
  digitalWrite(LED_BUILTIN, LOW); 
  digitalWrite(buzzerPin, HIGH);   
  delay(500);                      
  digitalWrite(LED_BUILTIN, HIGH);  
  digitalWrite(buzzerPin, LOW); 
  delay(500);   
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(buzzerPin, HIGH);
  delay(500);                   
  digitalWrite(LED_BUILTIN, HIGH);  
  digitalWrite(buzzerPin, LOW);
  delay(500);
                     
}




void loop()
{

  
 if (relayStatus1)
 {
   digitalWrite(relayPin1,HIGH);
 }
 else
 {
   digitalWrite(relayPin1,LOW);
 }
 if (relayStatus2)
 {
   digitalWrite(relayPin2,HIGH);
 }
 else
 {
   digitalWrite(relayPin2,LOW);
 }
 requestHandler();

}

void resetR1()
{
  digitalWrite(relayPin1,HIGH);
  delay(1000);
   digitalWrite(relayPin1,LOW);
  delay(10000);
  digitalWrite(relayPin1,HIGH);
  delay(1000);
  
}
void resetR2()
{
   digitalWrite(relayPin2,HIGH);
  delay(1000);
   digitalWrite(relayPin2,LOW);
  delay(10000);
  digitalWrite(relayPin2,HIGH);
  delay(1000);
}

void requestHandler()
{
  int packetSize = Udp.parsePacket();
  String perintah = "";
  if (packetSize)
  {
    // receive incoming UDP packets
    Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
    int len = Udp.read(incomingPacket, 255);
    if (len > 0)
    {
       incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet contents: %s\n", incomingPacket);
    if (incomingPacket[0] == 'a') //get data
    {
      relayStatus1 = LOW;
      perintah = "Matikan Relay 1";
        
    }
    else if (incomingPacket[0] == 'b') //get info sensor
    {
      relayStatus1 = HIGH;
      perintah = "Hidupkan Relay 1";
        
    }
     else if (incomingPacket[0] == 'd') //get info sensor
    {
      relayStatus2 = LOW;
      perintah = "Matikan Relay 2";
        
    }
     else if (incomingPacket[0] == 'e') //get info sensor
    {
      relayStatus2 = HIGH;
      perintah = "Hidupkan Relay 2";
        
    }
         else if (incomingPacket[0] == 'c') //get info sensor
    {
       resetR1();
       perintah = "Reset Relay 1";
        
    }
         else if (incomingPacket[0] == 'f') //get info sensor
    {
      resetR2();
       perintah = "Reset Relay 2";
        
    }
    else if (incomingPacket[0] == 'r') //perintah restart
    {
      perintah = (String)"Terima udp perintah restart dari "+ Udp.remoteIP().toString().c_str();
      perintah.toCharArray(replyPacket, 255);
      Serial.println(perintah);
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.write(replyPacket);
      Udp.endPacket();
      digitalWrite(buzzerPin, HIGH);    
      digitalWrite(LED_BUILTIN, LOW);    
      delay(10000); 
      ESP.restart();
    }
     else if (incomingPacket[0] == 'i') //get info sensor
    {
      perintah = infoNode;
    }
    else
    {
      perintah = "masuk else";
      
    }
    perintah.toCharArray(replyPacket, 255);
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(replyPacket);
    Udp.endPacket();
    Serial.printf("isi reply packet %s\n", replyPacket);
    buzzerlampu(); //nyalakan lampu biru setiap kirim data

  }


}
<span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce_SELRES_start"></span>

untuk pythonnya berikut. oh ya install dlu pyping ya. silahkan cari cara installnya. habis download packagenya. exstrak. masuk foldernya pakai cmd.
python setup.py install

import socket
import sys
import datetime
import time
import urllib2
import urllib
import math
import smtplib
import pyping
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

fromaddr = "email@gmail.com"
password = "coba ya"
port = 587
#toaddr = ['mtarecha@aio.co.id','mwibowo@aio.co.id']
toaddr = ['mtarecha@aio.co.id']
mailserver ='smtp.gmail.com'

# a matikan relay 1
# b hidupkan relay 1
# c reset relay 1
# d matikan relay 2
# e hidupkan relay 2
# f reset relay 2
emailalertenable = True
batasPacketLost = 60 #batas berapa kali time out baru restart
timeoutset = 100 #1000 ms / 1 detik
pingCount = 100
targetIP1 = "192.168.3.199"
targetIP2 = "192.168.3.199"
UDP_IP = "192.168.3.180"
UDP_PORT = 80
pingeveryseconds = 600 #ping tiap 600 detik atau 5 menit memberi kesempatan server ready untuk
#ping lagi setelah 5 menit. kalau terlalu cepat nanti sebelum siap restart terus



print "Power Relay IP :"+ UDP_IP
print "Power Relay Port :", UDP_PORT
print "Target IP 1 : "+ targetIP1
print "Target IP 2 : "+ targetIP2
statusResetPower1 = False
statusResetPower2 = False
statusNodeNotConnected = False
lastseennode = datetime.datetime.now()   

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
sock.settimeout(20) #dalam 10 detik setelah kirim tunggu data, kalau lewat bypass

def kirimPerintah(pesan):     
        sock.sendto(pesan, (UDP_IP, UDP_PORT))
        reply = sock.recvfrom(1024)[0]
        lastseennode = datetime.datetime.now()            
        return 'Nodesensor reply : ' + str(reply) +' at '+curtime

def kirimEmail(isiemail,subject):
        if emailalertenable :
                try :
                        msg = MIMEMultipart()
                        msg['From'] = fromaddr
                        msg['To'] = ", ".join(toaddr)
                        msg['Subject'] = subject
                        server = smtplib.SMTP(mailserver,port)                                        
                        server.login(fromaddr,password)
                        body = isiemail
                        msg.attach(MIMEText(body, 'plain'))
                        text = msg.as_string()
                        pesan = str(server.sendmail(fromaddr, toaddr, text))
                        if pesan=='{}' :
                                 print 'berhasil kirim email : '+isiemail
                        else : 
                                 print 'gagal kirim email'
                        server.quit()
                except Exception as e:
                        print 'Error Message '+ str(e)
                        print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                        print "General Error Unexpected error:", sys.exc_info()[0]

def saveLog(data):
        with open('logsmartpdu.txt','a') as file:
                file.writelines(data+'\n')
        print 'Berhasil save log : '+data
                               

while True:
         
        try :
                curmin = datetime.datetime.now().time().minute
                cursec = datetime.datetime.now().time().second
                curtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    
                print '--------------------------------------------------------'
                print curtime
                print 'Mulai ping Relay 1 dengan IP : '+targetIP1
                lostIP1 = pyping.ping(targetIP1, timeout=timeoutset, count=pingCount).packet_lost        
                if lostIP1 > batasPacketLost:
                        print kirimPerintah('c')
                        #reset
                        #sleep 300 detik / menit untuk ulang ping lagi dan nunggu waktu server cctv ready                        
                        statusResetPower1 = True
                        emailtext = 'Relay 1 dengan IP '+str(targetIP1)+' ping timeout sebanyak '+str(lostIP1)+' dari '+str(pingCount)+ ' pada '+ curtime +' telah di reset'
                        kirimEmail(emailtext,'Smart PDU - Reset Relay 1')
                        print 'Reset Relay 1'
                        saveLog(emailtext)
                        #time.sleep(300)
                        
                else:
                        #print kirimPerintah('b')
                        print 'Ping IP 1 : '+targetIP1+' OK'
                        #hidupkan
                        if statusResetPower1:
                                emailtext = 'Relay 1 dengan IP '+str(targetIP1)+' sudah aktif normal pada '+curtime
                                kirimEmail(emailtext,'Smart PDU - Relay 1 back to normal')
                                statusResetPower1 = False
                                saveLog(emailtext)
                print 'Mulai ping Relay 2 dengan IP : '+targetIP2
                lostIP2 = pyping.ping(targetIP1, timeout=timeoutset, count=pingCount).packet_lost               
                if lostIP2 > batasPacketLost:
                        print kirimPerintah('f')
                        #reset
                        #sleep 300 detik / menit untuk ulang ping lagi dan nunggu waktu server cctv ready                        
                        statusResetPower2 = True
                        emailtext = 'Relay 2 dengan IP '+str(targetIP2)+' ping timeout sebanyak '+str(lostIP2)+' dari '+str(pingCount)+ ' pada '+ curtime +' telah di reset'
                        kirimEmail(emailtext,'Smart PDU - Reset Relay 2')
                        print 'Reset Relay 2'
                        saveLog(emailtext)
                        #time.sleep(300)
                else:
                        #print kirimPerintah('e')
                        print 'Ping IP 2 : '+targetIP2+' OK'
                        #hidupkan
                        if statusResetPower2:
                                emailtext = 'Relay 2 dengan IP '+str(targetIP2)+' sudah aktif normal pada '+curtime
                                kirimEmail(emailtext,'Smart PDU - Relay 2 back to normal')
                                statusResetPower2 = False
                                saveLog(emailtext)
                                
                if statusNodeNotConnected :
                        emailtext = 'Node Konek Pada '+curtime
                        kirimEmail(emailtext,'Smart PDU - Node back to normal connected')
                        statusNodeNotConnected = False
                        saveLog(emailtext)
                        print emailtext
          
        except urllib2.HTTPError:
                print 'Tidak konek server'
        except urllib2.URLError:
                print 'URL Salah'
                
        except socket.error as a:
                print 'Tidak konek node '+str(a)
                statusNodeNotConnected = True 
                curmin = datetime.datetime.now().time().minute
                cursec = datetime.datetime.now().time().second
                #curhour = datetime.datetime.now().time().hour
                curtimelastseen = datetime.datetime.now()
                lastseensience = curtimelastseen - lastseennode                               
                print 'Lastseen Node '+str(lastseennode.strftime('%Y-%m-%d %H:%M:%S'))+ ' is '+str(lastseensience)[:-7]+' ago'
                if (curmin < 10) :
                
                        try :
                                if emailalertenable :                                        
                                        msg = MIMEMultipart()
                                        msg['From'] = fromaddr
                                        msg['To'] = ", ".join(toaddr)
                                        msg['Subject'] = "Smart Reset PDU - Disconnected Node"
                                        server = smtplib.SMTP(mailserver,port)                                        
                                        server.login(fromaddr,password)
                                        body = "Node "+UDP_IP+" tidak konek, Lastseen "+str(lastseennode.strftime('%Y-%m-%d %H:%M:%S'))+ ' is '+str(lastseensience)[:-7]+' ago'
                                                           
                                        msg.attach(MIMEText(body, 'plain'))
                                        text = msg.as_string()
                                        pesan = str(server.sendmail(fromaddr, toaddr, text))
                                        if pesan=='{}' :
                                                print 'berhasil kirim email : '+body
                                        else : 
                                                print 'gagal kirim email'
                                        server.quit()
                                        saveLog(body)
                        except Exception as e:
                                print 'Error Message '+ str(e)
                                print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                                print "General Error Unexpected error:", sys.exc_info()[0]
                                #tidak pernah exit karena kalau tidak konek dilewati dan ngulang
                                #5 detik kemudian
                                #sys.exit()
        except Exception as e:
                print 'Error Message '+ str(e)
                print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                print "General Error Unexpected error:", sys.exc_info()[0]
        time.sleep(pingeveryseconds)


Assalamu’alaikum Wr. Wb.

Diagram Sync Data ke server slave

Diagram Sync Data ke server slave

alamat server slave di ralat di http://appscenter.aio.co.id/weatherlogger/

alamat master logger di http://weatherlogger.ap.ngrok.io/

Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/
  7. https://tarecha.wordpress.com/2017/07/25/project-weather-logger-part-6-plot-graphic/
  8. https://tarecha.wordpress.com/2017/07/29/project-weather-logger-part-7-ngrok-http-tunneling/
  9. https://tarecha.wordpress.com/2017/08/27/project-weather-logger-part-8-ganti-sensor-bme280/

 

sepertinya ini part terahir ya. selanjutnya cman tambahan2 fitur saja mungkin, seperti mbandingkan suhu malang sama pasuruan atau membandingkan suhu dalam 3 hari terakhir. cman improvement di PHP nya saja.

Big Picture System

Big Picture System

tentu masih ingat gambar ini kan di part sebelumnya. ya tujuan ada synconize data adalah mengatasi ketebatasan koneksi internet yang baru bisa jalan saat tehtering internet melalui smartphone dilakukan. dan ketika master logger raspberry pi tidak konek internet sehingga tidak bisa diakses langsung menggunakan ngrok http tunelling. user bisa akses ke slave server. tentu kombinasinya itu multi site, jadi berapapun master logger raspberry pi / windows yang ada bisa di sync datanya ke slave server. slave server ini pun bisa lebih dari 1, bisa 2, bisa 3. tapi sih cukup 1 saja saya rasa sudah lebih dari cukup

Jika Anda kritis dengan skema yang saya buat kira2 akan menanyakan hal seperti ini

  1. Mengapa menggunakan port 80 baik pada socket untuk komunikasi dengan node mcu maupun save datanya menggunakan php yang juga jalan di port 80 ?
  2. mengapa simpan datanya tidak langsung pakai mysql yang nembak langsung ke database, lebih efisien daripada masih harus bikin program php nya

Ok saya akan coba jawab

  1. Port 80 adalah port open yang sangat jarang sekali oleh administrator jaringan di blok ( klo di blok gk bisa buka kompas youtube ataupun gmail ) jadi karena “port yang pasti dibuka” maka saya gunakan port 80 untuk komunikasi nya. meski agak aneh kirim data UDP ke node mcu pakai port 80.
  2. Jarang sekali ada web hosting yang membuka port 3306 untuk akses MySQL secara langsung, saya berfikir kedepan gimana kalau saya taruh di webhosting yang murah atau bahkan webhosting gratisan ? setahu saya mereka gk buka port 3306 nya. Jadi saya pakai program PHP untuk save datanya karena dia jalan di port 80 yang pasti open dan bisa ditaruh di webhosting shared biasa. contohnya slave server http://appscenter.aio.co.id/weatherlogger/ s    ini hanya diopen port 80 nya saja karena ini adalah webserver yang bisa diakses dari luar :-). soal keamanan juga. sebenarnya saya numpang ke server saya sendiri he he . tapi insyaAllah aman kok.

 

kode lengkapnya saya upload di https://github.com/tarecha/weatherlogger karena saya gk bisa pakai git, biasanya pakai svn tortoise, ah ribet jadi diupload biasa pakai file .zip

file sync.py

import socket
import sys
import datetime
import time
import urllib2
import urllib

 

urlsourcegetsync = "http://localhost/getsync.php"
urlsourceupdatestatus = "http://localhost/updatestatussync.php"
urldestinationinsertdata = "http://isikan alamat slave server/insertdata.php"
key = "12345"
updateflag = 'sync1'
limit = '20'
sleeptime = 1
sleeptimeerror = 5
i=0
while 1:
         
        try :
                print '# '+str(i)
                datagetupdate={}
                datagetupdate['k']= key
                datagetupdate['sync'] = updateflag
                datagetupdate['limit'] = limit
                dataformaturl = urllib.urlencode(datagetupdate)                    
                requesttoserver = urllib2.urlopen(urlsourcegetsync,dataformaturl,timeout=5)
                datarowall=requesttoserver.read()
                             
                if(datarowall[0]!='i'): #jika kembalian ada i nya maka gagal kalau tidak maka lanjut
                        datarow = datarowall.split("#")
                        datarow.pop() #hapus last row karena isinya kosong (/r/n/r/n) new line                        
                        print 'ada data baru'
                        #print datarowall
                        #print datarowall
                        #print datarow
                        for valuetosync in datarow:
                                
                                datasync={}
                                datasync=valuetosync.split(",")
                                data={}
                                data['k']= key
                                data['iddata'] = datasync[0]
                                data['idnode'] = datasync[1]
                                data['hum'] = datasync[2]
                                data['temp'] = datasync[3]
                                data['press'] =  datasync[4]                                
                                data['waktu'] = datasync[5]                                
                                if updateflag=='sync1':
                                        data['sync1'] = '1'
                                else:
                                        data['sync1'] = '0'
                                if updateflag=='sync2':
                                        data['sync2'] = '1'
                                else:
                                        data['sync2'] = '0'
                                #print data
                                dataformaturl = urllib.urlencode(data)                                
                                requesttoserver = urllib2.urlopen(urldestinationinsertdata,dataformaturl,timeout=5)               
                                
                                infosavephp = requesttoserver.read().split("#")
                                time.sleep(sleeptime) 
                                
                                #print '=========================================================================='  
                                if(infosavephp[0]=='i')and(infosavephp[1]=='Berhasil Insert'):
                                        print 'Informasi : Sync ke Slave Server : ID '+infosavephp[2]
                                        #updatestatus
                                        dataupdatestatus={}
                                        dataupdatestatus['k']=key
                                        dataupdatestatus['iddata']=infosavephp[2]
                                        dataupdatestatus['sync']=updateflag
                                        dataformaturl = urllib.urlencode(dataupdatestatus)
                                        requesttoserver = urllib2.urlopen(urlsourceupdatestatus,dataformaturl,timeout=5)
                                        infosavephp = requesttoserver.read().split("#")                                       
                                        print 'Informasi : '+infosavephp[1]+ ' ke master logger'
                                else:
                                        print 'update status sync gagal '
                                        print infosavephp
                                
                                
                                     
                                        
                else: #kembalian getsync.php
                        print 'tidak ada data baru'
                        
                time.sleep(sleeptime)                        
                 
        except urllib2.HTTPError:
                print 'Tidak konek server untuk sync'
                sleep(sleeptimeerror) 
        except urllib2.URLError:
                print 'Tidak konek server untuk sync'
                time.sleep(sleeptimeerror) 
                
        except socket.error:
                print 'Tidak konek node'
                #tidak pernah exit karena kalau tidak konek dilewati dan ngulang
                #5 detik kemudian
                #sys.exit()
                time.sleep(sleeptimeerror) 
        except Exception as e:
                print 'Error Message '+ str(e)
                print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
                print "General Error Unexpected error:", sys.exc_info()[0]
                time.sleep(sleeptimeerror) 
        i+=1
        
       


Sync Data

Sync Data

 

ini dari cara kerjanya adalah .

selama koneksi ada maka dia akan sync data, ketika tidak ada ya tetep jalan cman gk sync data karena gk konek. ketika ada koneksi maka list data yang belum di sync kana disync secara berurutan.

sekian kira2.

nanti akan saya buatkan resume dalam bahasa inggris sekalian di ikutkan dalam project hackster.io.

 

Terima Kasih

Wassalamu’alikum Wr. Wb.


Assalamu’alaikum Wr. Wb.

Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/
  7. https://tarecha.wordpress.com/2017/07/25/project-weather-logger-part-6-plot-graphic/
  8. https://tarecha.wordpress.com/2017/07/29/project-weather-logger-part-7-ngrok-http-tunneling/

2017-08-27 11_34_15-untitled - Paint

setelah beberapa saat humidity dari sensor DHT drop menjadi 1.4 %. lalu saya perbaiki dengan di solder lagi, mungkin karena sensornya kena panas jadi dia kembali normal lagi. setelah beberapa saat drop lagi. saya yakin ini bukan soal softwarenya, dan setelah cari-cari memang sensor DHT22 ini sensor murah, jadi ya gak bisa perform lama.salah satu keterangan di forum berikut . https://arduino.stackexchange.com/questions/17237/humidity-values-returned-by-dht22-sensors-failing-after-some-time

jadi saya beli lagi sensor BME280 dan node mcu 1 unit lagi. dan mengganti seluruh codingnya jadi 3 data saja, humidity, temperature, dan pressure. tinggal edit2 sedikit sih.

bme 280

kurang lebih mirip sensor bmp 280, alamatnya juga diganti, dibrary dari adafruit librarynya bisa didownload dari sini https://learn.adafruit.com/adafruit-bme280-humidity-barometric-pressure-temperature-sensor-breakout/wiring-and-test 2017-08-27 11_42_50-D__arduino-1.8.2-windows_arduino-1.8.2-windows_arduino-1.8.2_libraries_Adafruit_

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

Adafruit_BME280 bme; // I2C

WiFiUDP Udp;
byte localUdpPort = 80;  // local port to listen on
char incomingPacket[255];  // buffer for incoming packets
char  replyPacket[255];

//untuk server
const char* ssid = "tarecha";
const byte buzzerPin = D8;
const char* password = "12345678a";
const String aHostname = "N1";
String dataSensor;
String infoNode;
unsigned long delayKirimDataSensor = 0;

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
  Serial.begin(9600);

  Wire.begin(D2, D1); //Sensor bme
  WiFi.begin(ssid, password);
  if (!bme.begin()) {
    Serial.println(F("Could not find a valid bme280 sensor, check wiring!"));
    while (1);
  }
  //bagian setup wifi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.mode(WIFI_STA);
  WiFi.hostname(aHostname);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");

  }
  Serial.println(" connected");
  Udp.begin(localUdpPort);
  infoNode = (String)"Restart reason : " + ESP.getResetReason().c_str() + "\n MAC : " + WiFi.macAddress().c_str() + "\n Hostname : " + WiFi.hostname().c_str() + "\n Signal Stringth : " + WiFi.RSSI() +
             "\n Local IP : " + WiFi.localIP().toString().c_str() + "\n Port : " + localUdpPort + "\n CPU Freq : " + String(ESP.getCpuFreqMHz()) + " MHz\n Sketch Size : " + ESP.getSketchSize()
             + "\n Free Space Sketch Size : " + ESP.getFreeSketchSpace();

  Serial.println(infoNode);

  buzzerlampu();
}

void buzzerlampu()
{
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(buzzerPin, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, HIGH);
  digitalWrite(buzzerPin, LOW);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(buzzerPin, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, HIGH);
  digitalWrite(buzzerPin, LOW);
  delay(500);

}

void lampu()
{
   digitalWrite(LED_BUILTIN, LOW);
   delay(500);
   digitalWrite(LED_BUILTIN, HIGH);
   delay(5); 

}

void loop()
{

  if (delayKirimDataSensor > 200000)
  {
    UpdateSensor();
    Serial.println(dataSensor);
    delayKirimDataSensor = 0;
  }
  delayKirimDataSensor++;
  requestHandler();

}

void UpdateSensor()
{

  dataSensor = (String)aHostname + "," + bme.readHumidity()+ "," + bme.readTemperature() + "," + bme.readPressure()/100.0F;
}

void requestHandler()
{
  int packetSize = Udp.parsePacket();

  if (packetSize)
  {
    // receive incoming UDP packets
    Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
    int len = Udp.read(incomingPacket, 255);
    if (len > 0)
    {
       incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet contents: %s\n", incomingPacket);
    if (incomingPacket[0] == 'd') //get data
    {
      dataSensor.toCharArray(replyPacket, 255);
    }
    else if (incomingPacket[0] == 'i') //get info sensor
    {
      infoNode.toCharArray(replyPacket, 255);
    }
    else if (incomingPacket[0] == 'r') //perintah restart
    {
      String perintah = (String)"Terima udp perintah restart dari "+ Udp.remoteIP().toString().c_str();
      perintah.toCharArray(replyPacket, 255);
      Serial.println(perintah);
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.write(replyPacket);
      Udp.endPacket();
      digitalWrite(buzzerPin, HIGH);
      digitalWrite(LED_BUILTIN, LOW);
      delay(10000);
      ESP.restart();
    }
    else
    {
      String lain = "masuk else";
      lain.toCharArray(replyPacket, 255);
    }

    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(replyPacket);
    Udp.endPacket();
    Serial.printf("isi reply packet %s\n", replyPacket);
    lampu(); //nyalakan lampu biru setiap kirim data

  }

}

kode lainnya tinggal penyesuaian saja dihilangkan temperature dht nya karena gk pakai sensor tersebut.

hasilnya sebagai berikut. nanti code terakhirnya saya svn kan saja, saya gk bisa pakai git he he.

dan jadinya seperti ini jadi 3 data saja. schematiknya tetap sama. saya gk jadi pakai yang baru. yang node sudah jadi lama saya upload ulang dan ganti sensor bme 280 saja. sedangkan node satunya mau saya pakai remote reboot saja untuk ikut kompetisi di hackster.io

2017-08-27 11_48_51-VNC Viewer

 

oh ya soal sync data ke slave server saya belum bisa. masih gagal ketika koneksi putus dan ada lagi langsung  lanjutkan sync data. jadi nanti saja saya posting kalau sudah berhasil. tinggal sync data doank sih. klo udah ya selesai. klo mau ya bisa ditambahkan perbandingan suhu di kota A dan di kota B dalam bentuk grafik. biar tahu perbandingannya.

oke udah ya

Wassabamu’alaikum Wr. Wb.


Ngrok skema : sumber gambar ngrok.com

Ngrok skema : sumber gambar ngrok.com

Assalamu’alaikum Wr. Wb.
Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/
  7. https://tarecha.wordpress.com/2017/07/25/project-weather-logger-part-6-plot-graphic/

 

web servernya bisa langsung diakses di sini ya http://weatherlogger.ap.ngrok.io/

oke kita lanjutkan part 7 yaitu http tunelling.  ingat gambar berikut ? ada garis yang akses langsung ke raspberry nya. ada gmbar tunelling ke raspberry pi akses port 80 melalui browser. tentu ini kalau raspinya ada internetnya. karena dirumah gk ada internet kabel. saya pakai thetering hp. jadi tunneling ini akan bekerja kalau saya aktifkan theteringnya. kalau mau aktif terus ya langganan internet. tapi mahal he he. karena itu next chapter akan melakukan sync data dari raspberry pi ke web hosting yang datanya otomatis tersyncronize saat raspi tersambung internet. user bisa lihat 24jam/7hari ke slave server itu.

 

Big Picture System

Big Picture System

1. Ngrok.com http tunneling

Tujuannya adalah seperti gambar diatas. karena raspi menggunakan IP LOCAL sehingga gk bisa diakses melalui internet maka diperlukan http tunelling agar bisa diakses langsung ke raspi secara real time. kita buat http tunelling pakai ngrok.com. intinya kita menembus firewall .dan mempublis port 80 kita ke public

Ngrok skema : sumber gambar ngrok.com

Ngrok skema : sumber gambar ngrok.com

buka https://ngrok.com/ buat akun dan silahkan login.

Download sesuai sistem yang digunakan. saya menggunakan raspberry pi jadi download yang linux ARM ya. ekstrak dan taruh di desktop raspberry pi

rubah permissionnya jadi 777 dengan klik kanan dan permission any one

rubah properties ngrok

rubah properties ngrok

2. buat file di desktop ngrokstart.sh

dan pastikan 777 juga

permission ngrokstart.sh

permission ngrokstart.sh

isinya sbb

ngrok start

ngrok start

argumennya yang lengkap bisa di baca di dokumentasinya. http 80 berart yang di exspose port 80 -subdomain saya pakai weatherlogger sebagai nama -region ap (asia pasific / mengarah ke server singapore yang dekat) -auttoken kode token diperoleh dari sini

auth token

auth token

lalu klik 2x ngrokstart.sh akan jalan seperti ini

tunnel yang jalan

tunnel yang jalan

dan web saya bisa diakses langsung deh di http://weatherlogger.ap.ngrok.io

hasil expose

hasil expose

dan bisa dilihat bahwa alamatnya udah jadi weatherlogger.ap.ngrok.io , kecepatannya tergantung kecepatan koneksi hp nya. dan hanya nyala kalau theteringnya nyala

 

3. edit base_url

ada yang perlu diedit sedikit agar link nya mengarah ke nama server yang benar ketika diakses localhost , same LAN, ataupun expose http tunelling. kalau tidak nanti link nya mengarah ke localhost pc client

edit config ci

edit config ci

edit sebagai berikut


$root = "http://".$_SERVER['HTTP_HOST'];
$root .= str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']);
$config['base_url'] = "$root";

sehingga ketika diakses base url nya bisa ganti otomatis

akses localhost raspi

akses localhost raspi linknya jadi localhost

 

akses same LAN pakai laptop

akses same LAN pakai laptop linknya jadi alamat IP 192.168.3.199

 

hasil expose linknya

hasil expose link jadi weahterlogger.ap.ngrok.io

hasil expose link jadi weahterlogger.ap.ngrok.io

 

sekian dulu.

next chapter

8. sync data

 

Wassalamu’alaikum Wr. Wb.


Grafik Last 24 Hours

Grafik Last 24 Hours

Big Picture System

Big Picture System

Assalamu’alaikum Wr. Wb.
Oke saya lanjutkan. remind lagi ini adalah seri berantai dari posting berikut ya

  1. https://tarecha.wordpress.com/2015/10/26/project-raspberry-pi/
  2. https://tarecha.wordpress.com/2017/06/05/project-weather-logger-part-1/
  3. https://tarecha.wordpress.com/2017/06/26/project-weather-logger-part-2-tambahan-sensor-tekanan-udara/
  4. https://tarecha.wordpress.com/2017/07/01/project-weather-logger-part-3-komunikasi-udp/
  5. https://tarecha.wordpress.com/2017/07/15/project-weather-logger-part-4-simpan-di-server-mysql/
  6. https://tarecha.wordpress.com/2017/07/24/project-weather-logger-part-5-view-data-dan-export/

oke selanjutnya ke tahap plot grafik, sebuah data dengan X waktu akan mudah dibaca dengan grafik garis. disini tujuannya melihat hubungan suhu, kelembapan, dan tekanan dalam waktu yang sama. dari pengamatan saya Suhu berbanding terbalik dengan kelembapan dan kelembapan berbanding lurus dengan tekanan.

1.  Memilih plugin untuk ploting grafik

disini ada beberapa pertimbangan yaitu render grafik di server lalu dikirim ke browser client dalam bentuk gambar atau kirim data ke client dan di render di client. opsi pertama plugin akan membuat gambar di server dan ini cukup berat jika dilakukan pada server raspberry pi, sehingga saya pilih opsi ke dua. client side plotting. jadi server mengirim data dan di render menggunakan javascrip di browser client. pilihan saya jatuh pada https://canvasjs.com/ karena mudah dan interaktif. bisa multi y axis dimana untuk yang presure karena rangengya beda jauh antara kelembapan dan suhu maka saya gunakan axis y secondari yang sebelah kanan. sedangkan axis y utama digunakan suhu dan kelembapan karena rangenya dekat. selain itu canvasjs bisa plot jadi satu dan bisa muncul data dengan x (waktu) yang sama seperti berikut. selain itu bisa di hidden legend nya. misal pengen lihat suhu saja tinggal klik saja legennya, bisa zoom, pan, dan save sebagai jpg.

tool tip pop up

tool tip pop up

save plot grafik jpg

save plot grafik jpg

ok silahkan download canvasjs tersebut dan jadikan 1 ya. kita mulai programnya

1. Header.php file header ini untuk menampung script sehingga di masing grafik cukup menginclude kan. disini saya memasukkan yang dibutuhan untuk plot grafik dan datetime picker untuk select berdasarkan waktu meski library yang saya gunakan untuk jquery versi 3 tidak kompatibel dengan date time pickernya. jadi gak ada gambar tangannya ketika kasi filter tapi masih fungsi. daripada bila versinya diturunkan plot grafiknya yang jadi gk bisa

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
		
		<title>Graphic Weather Logger</title>
		
		<!-- stylesheets -->
		<link href="/assets/bootstrap.min.css" rel="stylesheet">
		<link href="/assets/font-awesome/css/font-awesome.min.css" rel="stylesheet">
		<link href="/assets/style.css" rel="stylesheet">
		
		<link href="/assets/jquery.simple-dtpicker.css" rel="stylesheet" type="text/css" />
		
		<!-- scripts -->
		<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fjs%2Fjquery-3.1.0.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />	
		<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fjs%2Fbootstrap.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
		<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fjs%2Fcanvasjs.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
		<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fassets%2Fjs%2Fjquery.simple-dtpicker.js%22%20type%3D%22text%2Fjavascript%22%20%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
		
	
	

	</head>

2. grafik.php
disini saya select all data berdasarkan node defaultnya yaitu N1, misal node nya ada banyak tinggal kasi argumen grafik.php?node=N2 . dari awal saya rancang untuk multi node ya.

<?php 
include "header.php"; 
include "config.php"; 
if(isset($_GET['node']))
{ 
$idnode = $_GET['node']; 
$query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress from masterdata where idnode='$idnode' order by waktu asc"; 
} 
else 
{ 
        $idnode = "N1"; 
        //untuk multi data dari beberapa node kita ambil default node nya N1 
        $query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress from masterdata where idnode='$idnode' order by waktu asc"; } $data = mysqli_query($conn, $query); $dataPointsdhttemp = array(); $dataPointsdhthum = array(); $dataPointsbmptemp = array(); $dataPointsbmppress = array(); while ($row = mysqli_fetch_array($data, MYSQL_ASSOC)) { array_push($dataPointsdhttemp,array('x'=>$row['x'],'y'=>$row['dhttemp']));
	array_push($dataPointsdhthum, array('x'=>$row['x'],'y'=>$row['dhthum']));
	array_push($dataPointsbmptemp, array('x'=>$row['x'],'y'=>$row['bmptemp']));
	array_push($dataPointsbmppress, array('x'=>$row['x'],'y'=>$row['bmppress']));
}

//ambil tanggal start date dan end date
$firstupdateU = reset($dataPointsdhttemp)['x']/1000;
$lastupdateU =end($dataPointsdhttemp)['x']/1000;
$firstupdate = date("d F Y H:i:s",$firstupdateU);
$lastupdate = date("d F Y H:i:s",$lastupdateU);

//bikin strip garis pembagi per pukul 00:00
$firstupdatetengahmalam = date("d F Y  H:i:s",$firstupdateU);
$begin = (new DateTime( $firstupdatetengahmalam ))->setTime(0,0);
$end = new DateTime( $lastupdate );
$begin = $begin->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

?>


<body style="padding-top: 3px;">



<div id="chartContainer"></div>






<div>

</div>





</body>


<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%24(function%20()%20%7B%0A%20%20%20%20var%20chart%20%3D%20new%20CanvasJS.Chart(%22chartContainer%22%2C%20%7B%0A%20%20%20%20%20%20%20%20theme%3A%20%22theme%22%2C%0A%20%20%20%20%20%20%20%20zoomEnabled%3A%20true%2C%0A%09%09exportEnabled%3A%20true%2C%0A%20%20%20%20%20%20%20%20animationEnabled%3A%20true%2C%0A%09%09axisY%3A%20%0A%09%09%7B%0A%09%09%09title%3A%20%22Suhu%20C%20dan%20Kelembapan%20%25%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%09%09%09suffix%3A%20%22%20C%20%2F%20%25%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09axisY2%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3A%20%22Pressure%20pa%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%09%09%09lineColor%3A%20%22%2386b402%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20suffix%3A%20%22%20pa%22%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20title%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3A%20%22Graphic%20Weather%20Logger%20of%20%3C%3Fphp%20echo%20%24idnode%3B%3F%3E%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09subtitles%3A%5B%0A%09%09%7B%0A%09%09%09text%3A%20%22by%20Mochamad%20Agung%20Tarecha%22%0A%09%09%0A%09%09%09%0A%09%09%7D%2C%0A%09%09%7B%0A%09%09%09%0A%09%09%09text%3A%20%22%3C%3Fphp%20echo%20%22Time%20range%20from%20%22.%24firstupdate%20.%22%20to%20%22.%20%24lastupdate%3B%3F%3E%22%0A%09%09%09%0A%09%09%7D%0A%09%09%5D%2C%0A%09%09%0A%09%09axisX%3A%0A%09%09%7B%20%20%20stripLines%3A%20%5B%0A%09%09%3C%3Fphp%20foreach(%24daterange%20as%20%24date)%20%7B%20echo%20%22%7B%22%3B%20echo%20%22value%3A%20%22.(string)%24date-%3Eformat(%22U%22).'000%2C'%3B%09%09%09%0A%09%09%09%09echo%20%22showOnTop%3A%20false%22%3B%20%20%20%20%20%20%20%20%20%20%20%0A%09%09%09%09echo%20%22%7D%2C%22%3B%0A%09%09%09%7D%0A%09%09%3F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%09%09%09valueFormatString%3A%20%22DD%20MMM%20YYYY%20HH%3Amm%22%0A%09%09%7D%2C%0A%09%09toolTip%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shared%3A%20true%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20data%3A%20%5B%0A%20%20%20%20%20%20%20%20%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20DHT%22%2C%09%09%09%09%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhttemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%09%09%09name%3A%20%22Humidity%20%25%20DHT%22%2C%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhthum%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%09%09%0A%09%09%09showInLegend%3A%20true%2C%09%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20BMP%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmptemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axisYType%3A%20%22secondary%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Pressure%20pa%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmppress%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%0A%09%09%0A%20%20%20%20%20%20%20%20%5D%2C%0A%09%09legend%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20%22pointer%22%2C%0A%09%09%09%09horizontalAlign%3A%20%22center%22%2C%0A%09%09%09%09verticalAlign%3A%20%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20itemclick%3A%20function%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(typeof%20(e.dataSeries.visible)%20%3D%3D%3D%20%22undefined%22%20%7C%7C%20e.dataSeries.visible)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20chart.render()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%20%20chart.render()%3B%0A%7D)%3B%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</html>

hasilnya sebagai berikut

Grafik all time

Grafik all time

 

3. grafikwaktu.php

sama saja bedanya kita bisa milih node yang mana dan range waktu tertentu. script utamanya sama tinggal ada inputan formnya

<?php 
include "header.php"; 
include "config.php"; 
$startdate = ""; 
$enddate = ""; 
$idnode = "N1"; 
//defalut value 
if(isset($_POST['startdate'])&&isset($_POST['enddate'])&&isset($_POST['idnode'])) 
{       $startdate = $_POST['startdate']; 
        $enddate = $_POST['enddate']; 
        $idnode = $_POST['idnode']; 
        if(($startdate!="")&&($enddate!=""))
         { 
        $query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress from masterdata where idnode='$idnode' and waktu between '$startdate' and '$enddate' order by waktu asc"; 
       } else {
          $query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress from masterdata where idnode='$idnode' order by waktu asc"; }
 } else { $query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress from masterdata where idnode='$idnode' order by waktu asc"; }
 $data = mysqli_query($conn, $query); $dataPointsdhttemp = array(); $dataPointsdhthum = array(); $dataPointsbmptemp = array(); $dataPointsbmppress = array();
 while ($row = mysqli_fetch_array($data, MYSQL_ASSOC)) { array_push($dataPointsdhttemp,array('x'=>$row['x'],'y'=>$row['dhttemp']));
	array_push($dataPointsdhthum, array('x'=>$row['x'],'y'=>$row['dhthum']));
	array_push($dataPointsbmptemp, array('x'=>$row['x'],'y'=>$row['bmptemp']));
	array_push($dataPointsbmppress, array('x'=>$row['x'],'y'=>$row['bmppress']));
}

//ambil tanggal start date dan end date
$firstupdateU = reset($dataPointsdhttemp)['x']/1000;
$lastupdateU =end($dataPointsdhttemp)['x']/1000;
$firstupdate = date("d F Y H:i:s",$firstupdateU);
$lastupdate = date("d F Y H:i:s",$lastupdateU);

//bikin strip garis pembagi per pukul 00:00
$firstupdatetengahmalam = date("d F Y  H:i:s",$firstupdateU);
$begin = (new DateTime( $firstupdatetengahmalam ))->setTime(0,0);
$end = new DateTime( $lastupdate );
$begin = $begin->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

?>

<body style="padding-top: 3px;">


<form action="grafikwaktu.php" method="post">


<div align="center">
    
	Node ID : 
	<select name="idnode">
<option value="N1" <?php if ($idnode=='N1') echo "selected"?>>N1</option>
<option value="N2" <?php if ($idnode=='N2') echo "selected"?>>N2</option>
<option value="N3" <?php if ($idnode=='N3') echo "selected"?>>N3</option>
<option value="N4" <?php if ($idnode=='N4') echo "selected"?>>N4</option>
<option value="N5" <?php if ($idnode=='N5') echo "selected"?>>N5</option>
<option value="N6" <?php if ($idnode=='N6') echo "selected"?>>N6</option>
<option value="N7" <?php if ($idnode=='N7') echo "selected"?>>N7</option>
	</select>
	
	
	Range time  : 
	<input type="text" name="startdate" id="startdate" value="<?php echo $startdate;?>"> - 
	<input type="text" name="enddate" id="enddate" value="<?php echo $enddate;?>">
	<input type="submit" name="submit" value="Filter">
	<button type="button" onclick="ClearFields();">Clear Filter</button>
	
	
</div>



</form>





<div id="chartContainer"></div>


</body>

	<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%09%09%24(function()%7B%0A%09%09%09%24('%23startdate').appendDtpicker(%0A%09%09%09%7B%0A%09%09%09%09%09%22autodateOnStart%22%3A%20false%0A%09%09%09%7D)%3B%0A%09%09%7D)%3B%0A%09%09%24(function()%7B%0A%09%09%09%24('%23enddate').appendDtpicker(%0A%09%09%09%7B%0A%09%09%09%09%22autodateOnStart%22%3A%20false%0A%09%09%09%7D)%3B%0A%09%09%7D)%3B%0A%09%09%0A%09%09function%20ClearFields()%20%0A%09%09%7B%0A%0A%09%09%09document.getElementById(%22startdate%22).value%20%3D%20%22%22%3B%0A%09%09%09document.getElementById(%22enddate%22).value%20%3D%20%22%22%3B%0A%09%09%7D%0A%09%09%0A%09%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%0A%0A%24(function%20()%20%7B%0A%20%20%20%20var%20chart%20%3D%20new%20CanvasJS.Chart(%22chartContainer%22%2C%20%7B%0A%20%20%20%20%20%20%20%20theme%3A%20%22theme%22%2C%0A%20%20%20%20%20%20%20%20zoomEnabled%3A%20true%2C%0A%09%09exportEnabled%3A%20true%2C%0A%20%20%20%20%20%20%20%20animationEnabled%3A%20true%2C%0A%09%09axisY%3A%20%0A%09%09%7B%0A%09%09%09title%3A%20%22Suhu%20C%20dan%20Kelembapan%20%25%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%09%09%09suffix%3A%20%22%20C%20%2F%20%25%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09axisY2%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3A%20%22Pressure%20pa%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20suffix%3A%20%22%20pa%22%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20title%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3A%20%22Graphic%20Weather%20Logger%20of%20%3C%3Fphp%20echo%20%24idnode%3B%3F%3E%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09subtitles%3A%5B%0A%09%09%7B%0A%09%09%09text%3A%20%22by%20Mochamad%20Agung%20Tarecha%22%0A%09%09%0A%09%09%09%0A%09%09%7D%2C%0A%09%09%7B%0A%09%09%09%0A%09%09%09text%3A%20%22%3C%3Fphp%20echo%20%22Time%20range%20from%20%22.%24firstupdate%20.%22%20to%20%22.%20%24lastupdate%3B%3F%3E%22%0A%09%09%09%0A%09%09%7D%0A%09%09%5D%2C%0A%09%09%0A%09%09axisX%3A%0A%09%09%7B%20%20%20stripLines%3A%20%5B%0A%09%09%3C%3Fphp%20foreach(%24daterange%20as%20%24date)%20%7B%20echo%20%22%7B%22%3B%20echo%20%22value%3A%20%22.(string)%24date-%3Eformat(%22U%22).'000%2C'%3B%09%09%09%0A%09%09%09%09echo%20%22showOnTop%3A%20false%22%3B%20%20%20%20%20%20%20%20%20%20%20%0A%09%09%09%09echo%20%22%7D%2C%22%3B%0A%09%09%09%7D%0A%09%09%3F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%09%09%09valueFormatString%3A%20%22DD%20MMM%20YYYY%20HH%3Amm%22%0A%09%09%7D%2C%0A%09%09toolTip%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shared%3A%20true%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20data%3A%20%5B%0A%20%20%20%20%20%20%20%20%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20DHT%22%2C%09%09%09%09%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhttemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%09%09%09name%3A%20%22Humidity%20%25%20DHT%22%2C%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhthum%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%09%09%0A%09%09%09showInLegend%3A%20true%2C%09%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20BMP%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmptemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axisYType%3A%20%22secondary%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Pressure%20pa%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmppress%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%0A%09%09%0A%20%20%20%20%20%20%20%20%5D%2C%0A%09%09legend%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20%22pointer%22%2C%0A%09%09%09%09horizontalAlign%3A%20%22center%22%2C%0A%09%09%09%09verticalAlign%3A%20%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20itemclick%3A%20function%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(typeof%20(e.dataSeries.visible)%20%3D%3D%3D%20%22undefined%22%20%7C%7C%20e.dataSeries.visible)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20chart.render()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%20%20chart.render()%3B%0A%7D)%3B%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</html>



hasilnya sebagai berikut
ini yang kata saya krusornya gk muncul gambar tangan, tapi masih bisa digunakan

grafik seleksi waktu

grafik seleksi waktu

 

4. grafiklast24hour.php

sama seperti yang pertama bedanya langsung select 24 jam terakhir mulai pukul 00 tengah malam

<?php include "header.php"; include "config.php"; if(isset($_GET['node'])) { $idnode = $_GET['node']; $query = "select unix_timestamp(waktu)*1000 as x,dhttemp,dhthum,bmptemp,bmppress,waktu from masterdata where idnode='$idnode' and waktu >= curdate() order by waktu asc";
	
}
else
{
	$idnode = "N1";
	//untuk multi data dari beberapa node kita ambil default node nya N1
	$query = "select unix_timestamp(waktu)*1000  as x,dhttemp,dhthum,bmptemp,bmppress,waktu from masterdata where idnode='$idnode' and waktu >= curdate() order by waktu asc";
}


$data = mysqli_query($conn, $query);
$dataPointsdhttemp = array();
$dataPointsdhthum = array();
$dataPointsbmptemp = array();
$dataPointsbmppress = array();



while ($row = mysqli_fetch_array($data, MYSQL_ASSOC)) 
{
	array_push($dataPointsdhttemp,array('x'=>$row['x'],'y'=>$row['dhttemp']));
	array_push($dataPointsdhthum, array('x'=>$row['x'],'y'=>$row['dhthum']));
	array_push($dataPointsbmptemp, array('x'=>$row['x'],'y'=>$row['bmptemp']));
	array_push($dataPointsbmppress, array('x'=>$row['x'],'y'=>$row['bmppress']));
}

//ambil tanggal start date dan end date
$firstupdateU = reset($dataPointsdhttemp)['x']/1000;
$lastupdateU =end($dataPointsdhttemp)['x']/1000;
$firstupdate = date("d F Y H:i:s",$firstupdateU);
$lastupdate = date("d F Y H:i:s",$lastupdateU);

//bikin strip garis pembagi per pukul 00:00
$firstupdatetengahmalam = date("d F Y  H:i:s",$firstupdateU);
$begin = (new DateTime( $firstupdatetengahmalam ))->setTime(0,0);
$end = new DateTime( $lastupdate );
$begin = $begin->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

?>

<body style="padding-top: 3px;">


<div id="chartContainer"></div>


<div>

</div>



</body>


<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%24(function%20()%20%7B%0A%20%20%20%20var%20chart%20%3D%20new%20CanvasJS.Chart(%22chartContainer%22%2C%20%7B%0A%20%20%20%20%20%20%20%20theme%3A%20%22theme%22%2C%0A%20%20%20%20%20%20%20%20zoomEnabled%3A%20true%2C%0A%09%09exportEnabled%3A%20true%2C%0A%20%20%20%20%20%20%20%20animationEnabled%3A%20true%2C%0A%09%09axisY%3A%20%0A%09%09%7B%0A%09%09%09title%3A%20%22Suhu%20C%20dan%20Kelembapan%20%25%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%09%09%09suffix%3A%20%22%20C%20%2F%20%25%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09axisY2%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3A%20%22Pressure%20pa%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontFamily%3A%20%22arial%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3A%2012%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20includeZero%3A%20false%2C%0A%09%09%09lineColor%3A%20%22%2386b402%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20suffix%3A%20%22%20pa%22%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20title%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3A%20%22Graphic%20Weather%20Logger%20of%20%3C%3Fphp%20echo%20%24idnode%3B%3F%3E%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09subtitles%3A%5B%0A%09%09%7B%0A%09%09%09text%3A%20%22by%20Mochamad%20Agung%20Tarecha%22%0A%09%09%0A%09%09%09%0A%09%09%7D%2C%0A%09%09%7B%0A%09%09%09%0A%09%09%09text%3A%20%22%3C%3Fphp%20echo%20%22Time%20range%20from%20%22.%24firstupdate%20.%22%20to%20%22.%20%24lastupdate%3B%3F%3E%22%0A%09%09%09%0A%09%09%7D%0A%09%09%5D%2C%0A%09%09%0A%09%09axisX%3A%0A%09%09%7B%20%20%20stripLines%3A%20%5B%0A%09%09%3C%3Fphp%20foreach(%24daterange%20as%20%24date)%20%7B%20echo%20%22%7B%22%3B%20echo%20%22value%3A%20%22.(string)%24date-%3Eformat(%22U%22).'000%2C'%3B%09%09%09%0A%09%09%09%09echo%20%22showOnTop%3A%20false%22%3B%20%20%20%20%20%20%20%20%20%20%20%0A%09%09%09%09echo%20%22%7D%2C%22%3B%0A%09%09%09%7D%0A%09%09%3F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%09%09%09valueFormatString%3A%20%22DD%20MMM%20YYYY%20HH%3Amm%22%0A%09%09%7D%2C%0A%09%09toolTip%3A%20%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20shared%3A%20true%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20data%3A%20%5B%0A%20%20%20%20%20%20%20%20%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20DHT%22%2C%09%09%09%09%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhttemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%09%09%09name%3A%20%22Humidity%20%25%20DHT%22%2C%09%09%09%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsdhthum%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%09%09%0A%09%09%09showInLegend%3A%20true%2C%09%09%09%0A%09%09%09name%3A%20%22Suhu%20C%20BMP%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmptemp%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%2C%0A%09%09%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20type%3A%20%22line%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axisYType%3A%20%22secondary%22%2C%0A%09%09%09xValueType%3A%20%22dateTime%22%2C%0A%09%09%09xValueFormatString%3A%22DD%20MMM%20YYYY%20HH%3Amm%3Ass%22%2C%09%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Pressure%20pa%22%2C%0A%09%09%09showInLegend%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20dataPoints%3A%20%3C%3Fphp%20echo%20json_encode(%24dataPointsbmppress%2C%20JSON_NUMERIC_CHECK)%3B%20%3F%3E%0A%20%20%20%20%20%20%20%20%7D%0A%09%09%0A%20%20%20%20%20%20%20%20%5D%2C%0A%09%09legend%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20%22pointer%22%2C%0A%09%09%09%09horizontalAlign%3A%20%22center%22%2C%0A%09%09%09%09verticalAlign%3A%20%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20itemclick%3A%20function%20(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(typeof%20(e.dataSeries.visible)%20%3D%3D%3D%20%22undefined%22%20%7C%7C%20e.dataSeries.visible)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.dataSeries.visible%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20chart.render()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%20%20chart.render()%3B%0A%7D)%3B%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</html>

grafik last 24 hour

grafik last 24 hour

 

oke sekiat dahulu chapter berikut.

next chapter

7. Ngrok http tunneling

8. Sync data dengan server slave.

9. saya belum punya ide soal ini

 

terima kasih semoga bermanfaat.

Wassalamu’alaikum Wr. Wb.