饂飩コーディング

iOSアプリやら、Unityやら、Cocos2dやらごにょごにょ書いております

ESP8266でネット経由で家電を操作する

f:id:appdeappuappu:20200607220016j:plain
家電リモコンの概要

f:id:appdeappuappu:20200607220157p:plain
ESP8266ブレッドボードでの回路


f:id:appdeappuappu:20200607220549j:plain
スマホから指示する画面

f:id:appdeappuappu:20200607220707j:plain
テレビはレグザ

f:id:appdeappuappu:20200607220648j:plain
エアコンは霧ヶ峰

f:id:appdeappuappu:20200607220638j:plain
扇風機はバルミューダ



私は猫を飼っています。夏は設定温度28度で付けっ放しで外出していますが、
エアコンつけ忘れたら猫が熱中症で危ないですよね??

というわけで、概要の絵のような感じで外出先からエアコンやら扇風機やらのオンオフが
できるようにESP8266を使って組み立てて見ました。


パートを分けるとしたら以下の通り
1、ESP8266で赤外線でリモコン情報をピピっつ!と飛ばす部分
2、スマホのブラウザで電源オンオフを行うボタンを表示する部分
3、ブラウザやESP8266からPHPで情報の受け渡しをする部分
4、mySQLでリモコン管理ようのテーブルを設計する部分

だいたいこの4つですが1の部分が楽しくてしょうがないですw(電子工作初心者だとリモコン楽しい〜)

本当は、赤外線LEDから送信されるデータはすべて管理ようのデータベーステーブルに登録しておいて
毎回そのデータをネットから受信して発光させようと思ってたんですが、なんだか無駄な気がして
とりあえずは赤外線LEDを発光させるデータはスケッチにオンコーディングしています。


それでは早速!
1、ESP8266で赤外線でリモコン情報をピピっつ!と飛ばす部分
電源を5V→3.3Vにレギュレータを使って降圧します。
秋月電子で購入した以下のパーツを使っています。
akizukidenshi.com

ちょっと前までは5vでESP8266をそのまま動かしていたのですが
金属のカバー部分がめっちゃくちゃ熱くなってたのでヤバイと感じて3.3Vにして見たら
発熱も気にならなくなりました。

赤外線LEDは二つ使います。テレビと扇風機に向ける赤外線LED1個とエアコンに向ける赤外線LED1個
計2つです。秋月電子で購入した以下の赤外線LEDを使いました。
akizukidenshi.com
Vf=1.35Vで最大電力が100mA
これを70mAで使っていきます。

70mA流したいのでトランジスタを使っています。有名なトランジスタのセカンドソース品ですw
akizukidenshi.com


IrSendDemoESP8266.ino
スケッチはサンプルスケッチIRsendDemoを改変して使いましょうw
不必要なコードが入っているので近日中に削除しておきます。

/* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend.
 *
 * Version 1.1 January, 2019
 * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
 * Copyright 2009 Ken Shirriff, http://arcfn.com
 *
 * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
 * as specified by kIrLed below.
 *
 * TL;DR: The IR LED needs to be driven by a transistor for a good result.
 *
 * Suggested circuit:
 *     https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending
 *
 * Common mistakes & tips:
 *   * Don't just connect the IR LED directly to the pin, it won't
 *     have enough current to drive the IR LED effectively.
 *   * Make sure you have the IR LED polarity correct.
 *     See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
 *   * Typical digital camera/phones can be used to see if the IR LED is flashed.
 *     Replace the IR LED with a normal LED if you don't have a digital camera
 *     when debugging.
 *   * Avoid using the following pins unless you really know what you are doing:
 *     * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
 *     * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
 *     * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
 *   * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
 *     for your first time. e.g. ESP-12 etc.
 */

#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Mitsubishi.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <ir_Mitsubishi.h>

const uint16_t kIrLed = 4;  // ESP8266 GPIO pin to use. Recommended: 4 (D2).

const char *ssid = "xxxxxxxxxxxxxxxxxx";  //ENTER YOUR WIFI SETTINGS
const char *password = "xxxxxxxxxxxxxx";

IRMitsubishiAC ac(kIrLed);  // Set the GPIO used for sending messages.

IRsend irsend(kIrLed);  // Set the GPIO to be used to sending the message.
#define PIN_PUSHBUTTON   12


// Example of data captured by IRrecvDumpV2.ino
uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550,
                        600, 1650, 650, 550, 600, 1650, 650, 1650, 650, 1650,
                        600, 550, 650, 1650, 650, 1650, 650, 550, 600, 1650,
                        650, 1650, 650, 550, 650, 550, 650, 1650, 650, 550,
                        650, 550, 650, 550, 600, 550, 650, 550, 650, 550,
                        650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650,
                        650, 1650, 650, 1650, 650, 1650, 600};

// Example Samsung A/C state captured from IRrecvDumpV2.ino
uint8_t samsungState[kSamsungAcStateLength] = {
    0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
    0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0};



void setup() {
  irsend.begin();
  pinMode(PIN_PUSHBUTTON, INPUT);
  delay(200);
  
  
#if ESP8266
  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
#else  // ESP8266
  Serial.begin(115200, SERIAL_8N1);
#endif  // ESP8266

WiFi.mode(WIFI_OFF);        //Prevents reconnection issue (taking too long to connect)
  delay(1000);
  WiFi.mode(WIFI_STA);        //This line hides the viewing of ESP as wifi hotspot
  
  WiFi.begin(ssid, password);     //Connect to your WiFi router
  Serial.println("");

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

  //If connection successful show IP address in serial monitor
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  //IP address assigned to your ESP

}

void loop() {

  // Send request
    HTTPClient http;    //Declare object of class HTTPClient
    http.useHTTP10(true);
    //http.begin("http://arduinojson.org/example.json");
    http.begin("http://xxxxxxxxxxxxx.com/testPHP/phpRemocon1.php");
    http.GET();

    // Parse response
    DynamicJsonDocument doc(500);
    deserializeJson(doc, http.getStream());

    // Read values
    int iidnumber_recieve = doc["iidnumber"];
    Serial.println(iidnumber_recieve);
    int istats_recieve = doc["istats"];
    Serial.println(istats_recieve);
    const char* iptyp_recieve = doc["iptyp"];
    Serial.println(iptyp_recieve);
    const char* itext_recieve = doc["itext"];
    Serial.println(itext_recieve);
    http.end(); 

    //扇風機ON
    if(strcmp(iptyp_recieve,"FNON")==0){
      senpuukiOn();
      motodatakoushin(iidnumber_recieve);
    }
    //扇風機OFF
    if(strcmp(iptyp_recieve,"FNOF")==0){
      senpuukiOn();
      motodatakoushin(iidnumber_recieve);
    }
    //TV ON
    if(strcmp(iptyp_recieve,"TVON")==0){
      terebiOn();
      motodatakoushin(iidnumber_recieve);
    }
    //TV OFF
    if(strcmp(iptyp_recieve,"TVOF")==0){
      terebiOn();
      motodatakoushin(iidnumber_recieve);
    }
    //MitsubishiAC ON
    if(strcmp(iptyp_recieve,"ACON")==0){
      airconOn();
      motodatakoushin(iidnumber_recieve);
    }
    //MitsubishiAC OFF
    if(strcmp(iptyp_recieve,"ACOF")==0){
      airconOFF();
      motodatakoushin(iidnumber_recieve);
    }
 
    delay(10000);
  
}

void senpuukiOn (){
  irsend.sendPanasonic(0x3242, 0x32427078807F);
  Serial.println("senpuuki ON");
   delay(1000);   
}
void terebiOn (){
  //NEC 
    Serial.println("NEC");
    irsend.sendNEC(0x2FD48B7);//TV ON
    delay(1000);
}
void airconOn(){
  //mitsubishiAC
    ac.begin();
    delay(200);
    ac.on();
    ac.setFan(1);
    ac.setMode(kMitsubishiAcCool);
    ac.setTemp(26);
    ac.setVane(kMitsubishiAcVaneAuto);
  //Serial.println("Sending IR command to A/C ...");
    ac.send(); 
}
void airconOFF(){
  //mitsubishiAC
    ac.begin();
    delay(200);
    ac.off();
  //Serial.println("Sending IR command to A/C ...");
    ac.send(); 
}

void motodatakoushin(int x){
  // Send request
    String postData,watasuData;
    watasuData = String(x);
    postData = "soushinData=" + watasuData ;

    Serial.println(watasuData);
    Serial.println(postData);
    Serial.println(x);
    
    HTTPClient http;    //Declare object of class HTTPClient
    http.begin("http://xxxxxxxxxxxxx.com/testPHP/phpRemocon2.php");
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    int httpCode = http.POST(postData);
    http.end();
}

phpRemocon1.php
(ESP8266のループから10秒間隔で呼び出されます。)
管理テーブルIrcontrolTableをチェックして赤外線LEDを光らせて
家電を操作してし終わったら管理テーブルを更新させるように動きます。

<?php

    $dbh= null;
    $user = null;
    $password = null;

    // データベースに接続するために必要なデータソースを変数に格納
    // mysql:host=ホスト名;dbname=データベース名;charset=文字エンコード
    $dsn = 'mysql:host=hogehoge.com;dbname=hogehogeDatabeseName;charset=utf8'; 
    // データベースのユーザー名
    $user = 'hogehoge';
    // データベースのパスワード
    $password = 'hogehoge';

    $userData = array(); 

    // tryにPDOの処理を記述
    try {
        // PDOインスタンスを生成
        $dbh = new PDO($dsn, $user, $password);
        $sth = $dbh->prepare("SELECT * FROM IrcontrolTable WHERE irstatus = 0"); //SQLを準備
        $sth->execute();
        //SQLを実行
        
        $rowcount = 0; // カウンターセット                               
        while($row = $sth->fetch(PDO::FETCH_ASSOC)){        //SQLで取得したデータ($sth)から連想配列でフェッチして$rowに一行づつ入れる
        $userData[]=array(
        'iidnumber'=>$row['iridnumber'],
        'istats'=>$row['irstatus'],
        'iptyp'=>$row['irprocesstype'],
        'itext'=>$row['irtext'],
        );
        $rowcount++;
        }

            if($rowcount == 0){
                $userData[]=array(
                    'iidnumber'=>0,
                    'istats'=>9,
                    'iptyp'=>'NONR',
                    'itext'=>'No record',
                );
            }

        // エラー(例外)が発生した時の処理を記述
    }catch (PDOException $e) {
        // エラーメッセージを表示させる
            echo 'データベースにアクセスできません!' . $e->getMessage();
            
            // 強制終了
            exit;
    }



    //jsonとして出力
    //header('Content-type: application/json');           //ヘッダの作成(JSON出力の明確化)
    echo json_encode($userData[0]);                        //ユーザーデータ配列をJSON化!!!
   
?>

phpRemocon2.php
ESP8266で家電の電源入れたり消したりしたら管理テーブルIrcontrolTableの更新を行って
「終わったよ」と書き込んでおきます。

<?php

$uketoriiridnumber = $_POST['soushinData'];

// データベースに接続するために必要なデータソースを変数に格納
  // mysql:host=ホスト名;dbname=データベース名;charset=文字エンコード
$dsn = 'mysql:host=hogehoge.com;dbname=hogehogeDatabeseName;charset=utf8'; 
    // データベースのユーザー名
    $user = 'hogehoge';
    // データベースのパスワード
    $password = 'hogehoge';
 
// tryにPDOの処理を記述
try {
  // PDOインスタンスを生成
  $dbh = new PDO($dsn, $user, $password);
// エラー(例外)が発生した時の処理を記述
} catch (PDOException $e) {
  // エラーメッセージを表示させる
  echo 'データベースにアクセスできません!' . $e->getMessage();
  // 強制終了
  exit;
}


// SELECT文を変数に格納
$sql = "UPDATE IrcontrolTable SET irstatus = 1, irupdatedate = NOW() WHERE iridnumber = $uketoriiridnumber";
// SQLステートメントを実行し、結果を変数に格納
$stmt = $dbh->query($sql);
 
?>

リモコン操作を指示するIrcontrolTableのDDS
ESP8266はPHPを介してこのテーブルをチェックしてます。

CREATE TABLE `IrcontrolTable` (
  `iridnumber` int(5) NOT NULL COMMENT '登録IDナンバー',
  `irstatus` int(5) NOT NULL DEFAULT '0' COMMENT '0:未処理 1:処理済',
  `irprocesstype` char(4) NOT NULL COMMENT '処理対象タイプ',
  `irtext` text NOT NULL COMMENT '備考',
  `ircreatedate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登録タイムスタンプ',
  `irupdatedate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新タイムスタンプ'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


remoconGamen2.php
スマホ等で表示する指示画面(外出時のリモコン画面といったとこですね)
適当なサーバにアップロードしてブラウザから開いてください。

<html>
  <head>
    <link rel="stylesheet" href="rest.css" />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
  <p class="textColor"> リモートリモコン</p>

<form action="../phpRemoconStatusUpdate.php" method="post">
    <button id="submit_button" type="submit" name="soushinId" value = 1> 扇風機オン </button>
    <button id="submit_buttonOFF" type="submit" name="soushinId" value = 4> 扇風機オフ </button>
    </br>
    </br>
    <button id="submit_button" type="submit" name="soushinId" value = 2> テレビオン </button>
    <button id="submit_buttonOFF" type="submit" name="soushinId" value = 5> テレビオフ </button>
    </br>
    </br>
    <button id="submit_button" type="submit" name="soushinId" value = 3> エアコンオン </button>
    <button id="submit_buttonOFF" type="submit" name="soushinId" value = 6> エアコンオフ </button>
    </br>
    </br>
    <button id="submit_button" type="submit" name="soushinId" value = 7> マルチチャンネル </button>
    </br>
    </br>

</form>

//同一画面に部屋の温度情報を表示させたい時はこんな感じで
<iframe id="inlineFrameExample"
    title="Inline Frame Example"
    width="600"
    height="2000"
    src="https://ambidata.io/ch/channel.html?id=hoehogehogehoge">
</iframe>

  </body>
</html>


phpRemoconStatusUpdate.php
ブラウザで開いたリモコン画面から呼ばれるPHPです
管理テーブルIrcontrolTableを更新してるだけの簡単なお仕事してます。
このPHPを利用してアレクサから利用できます

<?php

$uketoriiridnumber = $_POST['soushinId'];

// データベースに接続するために必要なデータソースを変数に格納
  // mysql:host=ホスト名;dbname=データベース名;charset=文字エンコード
$dsn = 'mysql:host=hogehoge.com;dbname=hogehogeDatabeseName;charset=utf8'; 
    // データベースのユーザー名
    $user = 'hogehoge';
    // データベースのパスワード
    $password = 'hogehoge';
 
// tryにPDOの処理を記述
try {
  // PDOインスタンスを生成
  $dbh = new PDO($dsn, $user, $password);
// エラー(例外)が発生した時の処理を記述
} catch (PDOException $e) {
  // エラーメッセージを表示させる
  echo 'データベースにアクセスできません!' . $e->getMessage();
  // 強制終了
  exit;
}


// SELECT文を変数に格納
$sql = "UPDATE IrcontrolTable SET irstatus = 0, irupdatedate = NOW() WHERE iridnumber = $uketoriiridnumber";
// SQLステートメントを実行し、結果を変数に格納
$stmt = $dbh->query($sql);

header("refresh:2;url=testPHPGamen/remoconGamen2.php");
//print ("該当のリモコンが約10秒後に送信されます。<br>");
?>

<html>
  <head>
    <meta http-equiv=”refresh” content=”0;URL='testPHPGamen/remoconGamen2.php' />
    <link rel="stylesheet" href="rest.css" />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
  <p class="textColor"> 10秒後にリモコンが起動します。</p>
  <p class="textColor"> このページは自動で戻ります。</p>

  </body>
</html>


style.css

@media screen and (min-width:0px){
    body{
      color: #5bc0de;
    }
  }
  
  @media screen and (min-width:300px){
    body{
      color: #d9534f;
    }
    
    p{
        font-size : 1.0rem ; /* これでフォントサイズは16pxになる*/
    }

    form{
        font-size : 1.0rem ; /* これでフォントサイズは16pxになる*/ 
    }

  }
  
  @media screen and (min-width:600px){
    body{
      color: #115a71;
    }
    p{
        font-size : 2.0rem ; /* これでフォントサイズは16pxになる*/
    }
    form{
      font-size : 1.6rem ; /* これでフォントサイズは16pxになる*/ 
    }
        button#submit_button {
        font-size: 1.0rem;
        font-weight: bold;
        background-color: #ff0000;
        width: 200px;
        height: 50px;
      }

        button#submit_buttonOFF {
        font-size: 1.0rem;
        font-weight: bold;
        background-color: #a8a7b3;
        width: 200px;
        height: 50px;
      }
  }


rest.css

/* 
html5doctor.com Reset Stylesheet
v1.6.1
Last Updated: 2010-09-17
Author: Richard Clark - http://richclarkdesign.com 
Twitter: @rich_clark
*/

html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
    margin:0;
    padding:0;
    border:0;
    outline:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

body {
    line-height:1;
}

article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section { 
    display:block;
}

nav ul {
    list-style:none;
}

blockquote, q {
    quotes:none;
}

blockquote:before, blockquote:after,
q:before, q:after {
    content:'';
    content:none;
}

a {
    margin:0;
    padding:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

/* change colours to suit your needs */
ins {
    background-color:#ff9;
    color:#000;
    text-decoration:none;
}

/* change colours to suit your needs */
mark {
    background-color:#ff9;
    color:#000; 
    font-style:italic;
    font-weight:bold;
}

del {
    text-decoration: line-through;
}

abbr[title], dfn[title] {
    border-bottom:1px dotted;
    cursor:help;
}

table {
    border-collapse:collapse;
    border-spacing:0;
}

/* change border colour to suit your needs */
hr {
    display:block;
    height:1px;
    border:0;   
    border-top:1px solid #cccccc;
    margin:1em 0;
    padding:0;
}

input, select {
    vertical-align:middle;
}


だらだらと一気に作ったので、効率的では無い構造になってたり
冗長的だったり雑ですが楽しかったのでよしとしますw。

次回はAmazon Alexaを使って声でリモコン操作します。