ロゴ ロゴ

【新歓ブログリレー】ESP32でボタンが押された回数をサーバーに送信する

ちょっとしたIoT講座

電算(電子計算機研究会)はIoT講座をやっていたのですが、去年はコロナウイルスの影響でIoT講座を開講できていませんでした。

そこで少しでもIoTに触れてもらうため、この記事ではArduino互換のマイコンであるESP32をつかって簡単なシステムを作ってみます。電算のIoT講座ではラズベリーパイを扱っているので、ネタ被りもしないかなという意図もあります。

やることはWiFiを使ったインターネット接続、サーバーとのソケット通信、割込み処理といったことです。ESP32がない人もコードだけ見てこんな感じなんだと思ってもらえれば幸いです。

ESP32とは

ESP32はArduinoの開発環境で開発できるマイコンです。

ESP32はラズベリーパイほどの性能がなく基本的にOS無しで動かします。しかし、Wi-FiやBluetoothが使える上に省電力で安い(1000円~2000円)です。

Arduino IDEのセットアップ

公式サイトのダウンロードページからダウンロードします。インストーラーをダウンロードがおすすめです。windowsユーザーなら『Windows Win 7 and newer』を選択します。

寄付してみないか?と聞かれますが、寄付しないなら『JUST DOWNLOAD』を押してください。

後はインストーラーを起動して指示に従ってください。

インストールしたらESP32のための設定をします。

ファイル>環境設定を開きます。

追加ボードマネージャのURLを https://dl.espressif.com/dl/package_esp32_index.json にします。

環境設定

ツール>ボード>ボードマネージャを開きます。

ESP32で検索してEspressif Systemsが提供しているものをダウンロードします。

ボードマネージャのダウンロード

ツール>ボードから『ESP32 Dev Module』を選択。

以下の項目もお手持ちのESP32に合わせて変えましょう。

Flash Mode

Flash Frequency

CPU Frequency

Flash Size

Upload Speed

ボードの設定

ボタンを使ってプルアップ回路を作る

記事書く気力が尽きかけてるので丸投げします。下記のページを参考にプルアップ回路を作ってください。図がついていてわかりやすいですね。

Arduino電子工作の基本⑥ スイッチの状態を読み取る

ピンの役割はESP32 Pinout Reference: Which GPIO pins should you use?を参照。

Arduinoのコードを書く

目標を確認します。

ESP32のボタンが押された回数をサーバーに送信する

早速コードを書いていきます。言語はC/C++です。

チェックボタンでコンパイル。矢印ボタンでコンパイルとESP32への書き込みが行われます。

#include <WiFi.h>
#include <WiFiClient.h>
#include <SPI.h>

//サーバーに送るデータのサイズ(byte)
#define PACKET_SIZE 2

#define BUTTON_PIN 35/*ここに対応したピンの番号*/

//サーバーにデータを送る間隔
#define INTERVAL 10000

const byte serverIP[] = { 255, 255, 255, 255 };//ここにサーバーのIPを4byteで
const int serverPort = 8888; //ここにサーバーのポート番号
const char ssid[] = "ここにWiFiのSSID";
const char pass[] = "ここにWiFiのパスワード";

//サーバーに送るデータ
byte packet[PACKET_SIZE] = {};

//送信するかの判定に使う
volatile bool sending_flag = false;
//ボタンが押された数
volatile unsigned short int num = 0;


//ボタンが押されたとき呼ばれる割込み処理
void IRAM_ATTR onButton(){
  ++num;
  sending_flag = true;

  Serial.print("ボタンが押された:");
  Serial.print(num);
  Serial.println("回目");
}


WiFiClient client;

//データを送信
void sendData(){
  sending_flag = false;
  packet[0] = static_cast<byte>(num >> 8);
  packet[1] = static_cast<byte>(num % 0x100);

  client.write(packet, PACKET_SIZE);
}


void setup() {
  Serial.begin(9600);


  //WiFiに接続
  Serial.print("SSID:");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);

  int TTL = 5;

  Serial.print("WiFiに接続中");
  delay(5000);

  while( WiFi.status() != WL_CONNECTED && --TTL>0) {
    WiFi.begin(ssid, pass);
    Serial.print(".");
    delay(5000);
  }

  if( WiFi.status() != WL_CONNECTED) {
    Serial.println("\nWiFiに接続失敗");
    return;
  }

  Serial.print("\nWiFiに接続しました\nIPアドレス:");
  Serial.println(WiFi.localIP());


  //サーバーに接続
  client.connect(serverIP, serverPort);

  TTL = 5;

  Serial.print("Connecting to Server");
  delay(5000);

  while( !client.connected() && --TTL>0) {
    client.connect(serverIP, serverPort);
    Serial.print(".");
    delay(5000);
  }

  if( !client.connected()) {
    Serial.println("\nサーバーに接続失敗");
    return;
  }

  Serial.println("\nサーバーに接続しました");


  //ピンの設定
  pinMode(BUTTON_PIN, INPUT);
  //割込み処理の設定
  attachInterrupt(BUTTON_PIN, onButton, FALLING);
}


void loop() {
  //10秒間隔で送信
  delay(INTERVAL);
  //最後に送ったデータから更新されているなら送信
  if(sending_flag) sendData();
}

内容に関わらずsetup関数は初めに呼ばれ、loop関数はずっと実行され続けます。

個々のコードの意味はコメント通りですね。

WiFiのSSIDとパスワード、サーバーのIPとポート、PIN番号は自分が使うものに書き換えます。

delay関数について

単位はミリ秒

Serial.print関数について

ツール>シリアルモニタからシリアルモニタを開けます。Serial.printはシリアルモニタにメッセージを出力できます。

接続待ちについて

WiFi.beginとClient.connectは実行してすぐ接続が確立されたか判定するとうまくいかなかったので待ち時間を作りました。接続が確立されていなかったら一定回数再試行します。

割込み処理について

下記のページを見てください。

ESP32のFreeRTOS入門 その4 割り込みと通知

サーバーに送るデータについて

Client.write関数はbyte型のデータをサーバーに送ります。引数はbyte[]と配列のサイズです。今回は2byteのデータを送ります。

1byteだと255までしかカウントできないので2byteにして65535までカウントできるようにしました。ビックエンディアンです。

サーバーのコードを書く

言語はPythonです。

import socket


class Receiver:

    ADDR = "ここにサーバーのIP"
    RECEIVE_PORT = 8888 #ここにサーバーのポート番号


    def __init__(self):
        print("Receiver Init")


    def _receiving(self, s_conn):

        while True:
            data = s_conn.recv(2)

            ##受け取ったbyte列を整数型に変換
            num = int.from_bytes(data[0:2], "big")
            print("ボタンが押された回数は", num, "です")


    def start(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind((Receiver.ADDR, Receiver.RECEIVE_PORT))
            s.listen(1)
            while True:
                conn, addr = s.accept()
                try:
                    print("接続しました:", addr)
                    self._receiving(conn)
                finally:
                    conn.close()


if __name__ == "__main__":

    receiver = Receiver()
    receiver.start()

byte型の配列を文字コードとして扱ったり、数値に変換する便利な関数があります。すごい!

実行

実行してみましょう。

1.Arduino IDEからESP32にプログラムのコードを書きこむ

2.自分のパソコンをサーバーとして公開してサーバープログラムを実行

3.Arduino IDEのシリアルモニタを開きESP32のリセットボタンを押してプログラムを実行開始


パソコンからでないとシリアルモニタを見れないのでESP32はパソコンにつなぎます。ESP32は電源に接続したらすぐに書き込まれているプログラムが実行されるので注意。

ESP32のリセットボタンを押すとプログラムが初めから実行され直します。サーバーの準備ができたら、リセットボタンを押して接続開始しましょう。

最後に

お疲れ様でした!

電算ではもっと時間をかけてラズベリーパイでIoT講座をやってました。

カメラで画像認識やってみたり、ICカードリーダーで学生証を読み取ったり、ラジコンつくってみたり好きに出来ます。

今年はIoT講座実施できるといいですね。

参考文献

Tinkercad

ESP32の使い方入門【20個のチュートリアル】

Arduino電子工作の基本⑥ スイッチの状態を読み取る

ESP32のFreeRTOS入門 その4 割り込みと通知

ESP32 Pinout Reference: Which GPIO pins should you use?

自分が使っているノートパソコンについて

Dell Inspiron 14 5000

購入時期 2019年5月
CPU Core i5-8265U
メモリ 8GB×1,DDR4,2666MHz
ストレージ KBG40ZNS256G NVMe TOSHIBA(SSD 256GB)
GPU UHD Graphics 620(CPU内蔵)
ディスプレイ 14.0インチFHD(1920×1080)IPS LEDバックライト Narrow Border デイスプレイ
サイズ 324mm×231mm
重さ 1.5㎏
値段 62,266円
その他 光学ドライブなし タッチパネルなし テンキーなし

情報工1,2年生の間はこれで困ったことはなかったです。ZoomとChromeとVScodeとAcrobat同時に開いても問題ありません。

ただし、大学が出したノートPCの推奨スペックによると、情報工はメモリが16GB必要になるのでこれから困るのかもしれません🥺

Unityとかやると重いので3D系やりたい人はスペック高いとよさげ。Reactのビルドするときもとても時間がかかります。そういうのはデスクトップでやるのが正解かもしれませんが…

ストレージは絶対SSDがいいです。前に使ってたHDD搭載のPCは立ち上げがとても遅く不便でした。SSDの快適さを知ったらHDDには戻れません。

CPUについてですが今はRyzen搭載のノートPCが充実しているのでIntel以外も全然ありだと思います。

Dell製のPCはDELL公式サイトから買うと安くなります。

コメント入力

関連サイト