ESP-WROOM-02 開発ボード-WEB画面からの操作でLEDを点灯(クライアント編)
今回はESP-WROOM-02をクライアントにしてインターネット上の他のサーバから指示を受けて100Vの電源をON/OFFする
インターネット上のサーバはWEB画面から指示を受け付けてESP-WROOM-02に連絡する機能を持つ。
完成図映像
システム構成図
![システム構成](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022211.png)
今回は100Vの電源を扱うことから感電や、パーツを破損する確立が格段に高くなるので高度な注意力を要する。
気を引き締めていこう。
課題は以下の通り。
・100Vのドライブ
・インターネット上のサーバーはどうする?
・クライアントのプログラムはどうする?
一つ一つ解決して行こう。
100Vのドライブ。
これはリレーを使う方法が1番に思い浮かんだのだが、今回のESP-WROOM-02は3Vである事とリレーをドライブする電流が出せない可能性が高いので却下。
SSRを見て行くといいのが見つかる。入力電圧が3V~8Vで動作可能。
http://akizukidenshi.com/catalog/g/gK-00210/
ページの説明を見ると入力は4.8V~となっているけど店頭で黄色い説明書を見ると3V~って書いてあったんです。
と思い、秋月のページの写真を拡大してみると、ちゃんと3V~って書いてあるじゃん。間違っているのね。などと思いつつ次へ進める。
説明書の回路図。
![SSRキットの回路図](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022207.jpg)
なんでも、ゼロクロス回路っていうのが入っていて、交流電圧が0Vの時にON/OFFの操作を行うことでスイッチングノイズを大幅減するって事です。
すげーーーー。ま、結構高いしね。
ていうか、
http://akizukidenshi.com/catalog/g/gK-00203/
の方が実験で使うには安くてコンパクトで良かった。(泣
気を取り直して組み立ててみた。
でいつも通りにちゃんと半田付けできているかテスターを当てて行くと、
なんと、回路図と、基盤のパターンがあっていない箇所を発見!!
色々考えてみると回路図のゼロクロスの上のピンが6番、下のピンが4番となっているけど
これが逆ですよー秋月さん。ま、ちゃんと動けば文句は無いです。
ちゃんと半田付けが出来ていないのかと思ってびびっただけです。
SSRの写真
![SSR写真](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022208.jpg)
INに3.3Vを入れてあげるとちゃんと電気が流れました。めでたしめでたし。
インターネット上のサーバーはどうする?
サーバに関する課題は2つ。
1.WEB方式で行くのか?それとも、TCPとかUDPで直で行くか?
2.サーバはどこにおくのか?自宅サーバーかレンタルサーバーか?
1の課題に関してはTCPとかUDPを直で操作するのは敷居が高いのでパス。
それにESP-WROOM-02のクライアント側のサンプルプログラムが良さげだったのでWEB方式に決定。
2の課題は自宅サーバーをWEBに公開するとセキュリティー的に不味そうなのとDDNSの契約とかもメンドウなんで今現在借りているhttp://www.sakura.ad.jp/に決定します。
今現在使っているCGIを改造すれば行けそうだし既にノウハウがあるほうが話は早いし。
というわけで、WEB方式で、レンタルサーバー方式で行きたいと思います。
商用向けにはWEB方式は効率的に悪いので、TCPまたはUDP直で行ったほうがお勧めですが、これは実験ですので。効率は度外視で行きます。
久々にperlでプログラムを作ったぞっと。
control.cgi : システム構成図のWebServerの右側のCGIでパソコン等から指示を出して結果を表示するプログラムです。
wroom.cgi : システム構成図のWebServerの左側のCGIでESP-WROOM-02からの指示読み出し要求と結果の送信の処理を行います。
control.cgiの画面
![control.cgi画面](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022217.png)
The current state : 表示領域です。現在のESP-WROOM-02の状況を表示します。
The present order is : 表示領域です。現在の指示内容を表示します。
Please set a state of the LED. : 操作領域です。ON または OFFの指示を出します。
Reload : 最新の画面を表示します。
control.cgiのプログラム
#! /usr/bin/perl
############################
require 'jcode.pl';
############################
$webmaster = 'xxxx@xxxxxx.xxx';
&parse_form_data(*simple_form);
$led = $simple_form{'led'};
LockON();
if($led ne ""){
if($led eq "on"){
$OutRec = "HIGH";
}
else{
$OutRec = "LOW";
}
if(!open(present_file, "> present.dat")){
EmergencyStop(3000);
}
print present_file "$OutRec¥n";
close(present_file);
}
if(!open(status_file, "status.dat")){
EmergencyStop(1000);
}
if($InRec=<status_file>){
$nLedStatus = SpaceCut($InRec);
}
close(status_file);
if(!open(present_file, "present.dat")){
EmergencyStop(1000);
}
if($InRec=<present_file>){
$nLedPresentStatus = SpaceCut($InRec);
}
close(present_file);
# $nLedStatus = "HIGH";
# $nLedPresentStatus = "HIGH";
LockOFF();
# HTML出力
print "Content-type: text/html¥n¥n";
print "<html>¥n";
print "<head>¥n";
print " <META http-equiv=Control content=no-cache>¥n";
print " <META http-equiv=Pragma content=no-cache>¥n";
if($led ne ""){
print " <meta http-equiv='Refresh' content='0;URL=control.cgi'>¥n";
}
if($nLedStatus ne $nLedPresentStatus){
print " <meta http-equiv='Refresh' content='1;URL=control.cgi'>¥n";
}
print " <META http-equiv=Content-type content='text/html; charset=Shift_JIS'>";
print "<title>LED-SWITCH</title>¥n";
print "</head>¥n";
print "<body>¥n";
print "The current state ";
print "<BLOCKQUOTE> ";
print "<FONT SIZE=+10>";
if($nLedStatus eq "HIGH"){
print "ON.";
}
else{
print "OFF.";
}
print "</FONT>";
print "</BLOCKQUOTE> ";
print "The present order is ";
print "<BLOCKQUOTE> ";
print "<FONT SIZE=+10>";
if($nLedPresentStatus eq "HIGH"){
print "ON.";
}
else{
print "OFF.";
}
print "</FONT>";
print "</BLOCKQUOTE> ";
print "Please set a state of the LED.";
print "<BLOCKQUOTE>";
print "<a href=¥"/arduino/control.cgi?led=on¥">LED ON</a><br>";
print "<a href=¥"/arduino/control.cgi?led=off¥">LED OFF</a><br>";
print "</BLOCKQUOTE>";
print "Reload.";
print "<BLOCKQUOTE>";
print "<a href=¥"/arduino/control.cgi¥">RELOAD</a><br>";
print "</BLOCKQUOTE>";
# HTML出力
print "</body>¥n";
print "</html>¥n";
exit;
sub LockON
{
if(!open(LOCK, "> LOCK")){
EmergencyStop(1000);
}
flock(LOCK, 2);
}
sub LockOFF
{
close(LOCK);
}
sub EmergencyStop
{
local($ErrorNo) = @_;
print "System Error $ErrorNo¥n";
print "";
print_footer();
exit(1);
}
sub SpaceCut
{
local($String) = @_;
$String =~ s/ //g; #// 全角空白を削除
$String =~ s/ //g; #// 半角空白を削除
$String =~ s/¥n//g; #// enterを削除
return ($String);
}
# SPECIAL LIB
#
sub parse_form_data
{
local(*FORM_DATA) = @_;
local( $request_method, $query_string, @key_value_pairs, $key_value, $key, $value);
$request_method = $ENV{'REQUEST_METHOD'};
if($request_method eq "GET"){
$query_string = $ENV{'QUERY_STRING'};
} elsif($request_method eq "POST"){
read (STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
} else {
&return_error(500, "Server Error", "Server uses unsupported method");
}
@key_value_pairs = split(/&/, $query_string);
foreach $key_value (@key_value_pairs){
($key, $value) = split(/=/, $key_value);
$value =~ tr/+/ /;
$value =~ s/%([¥dA-Fa-f][¥dA-Fa-f])/pack("C",hex($1))/eg;
if(defined($FORM_DATA{$key})){
$FORM_DATA{$key} = join("¥0", $FORM_DATA{$key}, $value);
} else {
$FORM_DATA{$key} = $value;
}
}
}
sub return_error
{
local($status, $keyword, $message) = @_;
print "Content-type: text/html¥n¥n";
print "Status: ", $status, " ", $keyword, "¥n¥n";
print <<End_of_Error;
<title>CGI Program - Unexpected Error</title>
<h1>$keyword</h1>
<hr>
$message
<hr>
Prease contact $webmaster for more information.
End_of_Error
exit(1);
}
解説
LockONとLockOFFで排他制御します。
ledのパラメータが入ってきた場合は指示が有った場合です。
present.datファイルにHIGHまたはLOWを記録します。
status.datを読んで画面のThe current state部分に表示します。
present.datを読んで画面のThe present order is部分に表示します。
content-typeにtext/htmlを指定してHTMLでの出力であることを示します。
METAの記述以下を実施します。
キャッシュをOFF
指示ありの場合はすぐにReloadさせます。
The current stateとThe present order isのステータスが異なる場合は1秒単位でReloadさせます。
wroom.cgiのプログラム
#! /usr/bin/perl
############################
require 'jcode.pl';
############################
$webmaster = 'xxxx@xxxx.xxx;
&parse_form_data(*simple_form);
$led = $simple_form{'led'};
# HTML出力
print "Content-type: text/plain¥n¥n";
LockON();
if($led ne ""){
if($led eq "on"){
$OutRec = "HIGH";
}
else{
$OutRec = "LOW";
}
if(!open(status_file, "> status.dat")){
EmergencyStop(3000);
}
print status_file "$OutRec¥n";
close(status_file);
}
if(!open(present_file, "present.dat")){
EmergencyStop(1000);
}
if($InRec=<present_file>){
$nLedPresentStatus = SpaceCut($InRec);
}
close(present_file);
LockOFF();
if($nLedPresentStatus eq "HIGH"){
print "ON";
}
else{
print "OFF";
}
exit;
sub LockON
{
if(!open(LOCK, "> LOCK")){
EmergencyStop(1000);
}
flock(LOCK, 2);
}
sub LockOFF
{
close(LOCK);
}
sub EmergencyStop
{
local($ErrorNo) = @_;
print "System Error $ErrorNo¥n";
print "";
exit(1);
}
sub SpaceCut
{
local($String) = @_;
$String =~ s/ //g; #// 全角空白を削除
$String =~ s/ //g; #// 半角空白を削除
$String =~ s/¥n//g; #// enterを削除
return ($String);
}
# SPECIAL LIB
#
sub parse_form_data
{
local(*FORM_DATA) = @_;
local( $request_method, $query_string, @key_value_pairs, $key_value, $key, $value);
$request_method = $ENV{'REQUEST_METHOD'};
if($request_method eq "GET"){
$query_string = $ENV{'QUERY_STRING'};
} elsif($request_method eq "POST"){
read (STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
} else {
&return_error(500, "Server Error", "Server uses unsupported method");
}
@key_value_pairs = split(/&/, $query_string);
foreach $key_value (@key_value_pairs){
($key, $value) = split(/=/, $key_value);
$value =~ tr/+/ /;
$value =~ s/%([¥dA-Fa-f][¥dA-Fa-f])/pack("C",hex($1))/eg;
if(defined($FORM_DATA{$key})){
$FORM_DATA{$key} = join("¥0", $FORM_DATA{$key}, $value);
} else {
$FORM_DATA{$key} = $value;
}
}
}
sub return_error
{
local($status, $keyword, $message) = @_;
print "Content-type: text/html¥n¥n";
print "Status: ", $status, " ", $keyword, "¥n¥n";
print <<End_of_Error;
<title>CGI Program - Unexpected Error</title>
<h1>$keyword</h1>
<hr>
$message
<hr>
Prease contact $webmaster for more information.
End_of_Error
exit(1);
}
content-typeにtext/plainを指定してtextでの出力であることを示します。
LockONとLockOFFで排他制御します。
ledのパラメータが入ってきた場合は指示が有った場合です。
status.datファイルにHIGHまたはLOWを記録します。
present.datを読んでONまたはOFFを表示します。
クライアントのプログラムはどうする?
さっきもちょっと触れたけどWEBクライアントでの良い感じのサンプルがありました。
例として選んだのがESP8266HttpClientの中からBasicHttpClient。
これを変更して今回の仕様に合わせて改造します。
クライアントのプログラム
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;
int nLedStatus = LOW;
const int PIN_LED = 13;
void setup() {
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
Serial.begin(115200);
Serial.println("WebClient");
for(uint8_t t = 3; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...¥n", t);
Serial.flush();
delay(1000);
}
WiFiMulti.addAP("xxxxxxxx", "xxxxxxxx");
}
void loop() {
// wait for WiFi connection
if((WiFiMulti.run() == WL_CONNECTED)) {
Serial.println("Connected using IP:");
Serial.println(WiFi.localIP());
Serial.println("And MAC address:");
Serial.println(WiFi.macAddress());
String sUrlParam="";
while(1){
HTTPClient http;
Serial.print("[HTTP] begin...¥n");
String sUrl="http://xxxxxxxxx.sakura.ne.jp/arduino/wroom.cgi";
sUrl += sUrlParam;
http.begin(sUrl); //HTTP
Serial.print("[HTTP] GET...¥n");
// start connection and send HTTP header
int httpCode = http.GET();
sUrlParam="";
// httpCode will be negative on error
if(httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d¥n", httpCode);
// file found at server
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
if(payload=="ON"){
if(nLedStatus==LOW){
digitalWrite(PIN_LED, HIGH);
nLedStatus = HIGH;
sUrlParam="?led=on";
continue;
}
}
if(payload=="OFF"){
if(nLedStatus==HIGH){
digitalWrite(PIN_LED, LOW);
nLedStatus = LOW;
sUrlParam="?led=off";
}
}
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s¥n", http.errorToString(httpCode).c_str());
}
http.end();
if(sUrlParam!=""){
continue;
}
break;
}
}
delay(1 * 1000);
}
一定時間間隔で自分のレンタルサーバーのwroom.cgiにGETさせます。
wroom.cgiから受け取った内容(ONまたはOFF)が現在の自分が持っているステータスと異なる場合は以下を行います。
・指示内容に合わせてdigitalWriteで出力電圧を変更します。
・新しいステータスをwroom.cgiに伝えるためにパラメーター「?led=on」または「?led=off」を付けてwroom.cgiをGETします。
・新しいステータスをグローバル変数に保持します。
![ESP-WROOM-02 開発ボード-WEB画面からの操作でLEDを点灯(クライアント編)回路図](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022206.png)
写真
![写真](https://img-proxy.blog-video.jp/images?url=http%3A%2F%2Fimg-cdn.jg.jugem.jp%2F85e%2F51887%2F20160325_2022212.jpg)