ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【python】UDP通信の方法【ESP32】

 最近ネタがなかったのでサボっていましたが、今回pythonを使ってUDP通信ができるということを知ったのでその備忘録です。また、以下の記事の応用で、スマホではなくpythonからUDPで値を送ってみました。

rikoubou.hatenablog.com



1:pythonUDP通信を行う方法
 結論から言うと以下のプログラムでできます。(python3系)

・send_udp.py

from __future__ import print_function
import socket
import time
from contextlib import closing

def main():
  host = '192.168.4.1' # IPアドレス
  port = 10000 # ポート番号
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  with closing(sock):
    while True:
      message = 'A'.encode('utf-8') # 送る文字列(ここでは「A」という文字列を送っている)
      print(message)
      sock.sendto(message, (host, port))
      time.sleep(1)
  return

if __name__ == '__main__':
  main()


2:pythonからUDPで送信された内容をESP32で受信する
 先に挙げた記事内容のスケッチほぼそのままです。

・ESP32_UDP.ino

#include <WiFi.h>
#include <WiFiUdp.h>

const char ssid[] = "ESP32_wifi"; // SSID
const char pass[] = "esp32pass";  // password
const int localPort = 10000;      // ポート番号(pythonのポート番号に合わせる)

const IPAddress ip(192, 168, 4, 1);       // IPアドレス(pythonのIPアドレスに合わせる)
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク

WiFiUDP udp;

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

  WiFi.softAP(ssid, pass);           // SSIDとパスの設定
  delay(100);                        // 追記:このdelayを入れないと失敗する場合がある
  WiFi.softAPConfig(ip, ip, subnet); // IPアドレス、ゲートウェイ、サブネットマスクの設定

  Serial.print("AP IP address: ");
  IPAddress myIP = WiFi.softAPIP();
  Serial.println(myIP);

  Serial.println("Starting UDP");
  udp.begin(localPort);  // UDP通信の開始(引数はポート番号)

  Serial.print("Local port: ");
  Serial.println(localPort);
}

void loop() {
  if (udp.parsePacket()) {
    char c = udp.read();
    Serial.println(c); // UDP通信で来た値を表示
  }
}

 ESP32に上記スケッチを書き込むと「ESP32_wifi」という名前のSSIDWiFiが飛ぶようになります。pythonを実行するPCで「ESP32_wifi」に接続し、その後「send_udp.py」を実行するとシリアルモニタに「A」という文字列が1秒ごとに表示されます。


 非常に簡単ですが、スマホだけでなくPCからでもUDP通信ができることがわかったので、何か面白いことに役立てたいと思っています。


・参考資料
Pythonでネットワークプログラミング | saito's memo

【ESP32】Wifiに接続して時刻合わせを行う

 ESP32を使って気温などの値を記録する装置を考える際「いつの値か」というのも記録しておく必要が出てきます。
 そのためにはESP32の時刻合わせをする必要があるので、今回はそれについての備忘録です。


Wifiに接続して時刻合わせを行う
 具体的には以下のスケッチで時刻合わせをすることができます。SSID、PASSWORDは自分のものに書き換えてください。また無線の設定でNTPサーバに繋がるようにしておいてください。

・ESP32_SyncTime.ino

#include <WiFi.h>

const char *ssid = "****";
const char *password = "****";
 
void setup() {
  Serial.begin(115200);
  setupWifi();    // Wifi接続
  syncTime();     // 時刻を合わせる
  Serial.println("setup end!");
}
 
void loop() {
  struct tm timeInfo;
  char s[20];

  getLocalTime(&timeInfo); // 時刻を取得

  // 日付と時刻を文字列として取得
  sprintf(s, "%04d/%02d/%02d %02d:%02d:%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);

  // 曜日も含めた書式に変更
  String dayInfo = s;
  dayInfo = dayInfo.substring(0, 10) + " (" + getDayOfWeek(timeInfo.tm_wday) + ")\n" + dayInfo.substring(11);

  Serial.println(dayInfo);
  delay(10);
}

/**
 * Wifiの設定を行う関数
 */
void setupWifi() {
  // WiFiに接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)  {
    Serial.print(".");
    delay(500);
  }
  Serial.print("Connected to ");
  Serial.println(ssid);
}

/**
 * 時刻合わせを行う関数
 */
void syncTime() {
  configTime(-9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
}

/**
 * 引数の数値を曜日に変換する関数
 */
String getDayOfWeek(int dayNum) {
  String result = "";
  switch (dayNum) {
    case 0:
      result = "Sun";
      break;
    case 1:
      result = "Mon";
      break;
    case 2:
      result = "Tue";
      break;
    case 3:
      result = "Wed";
      break;
    case 4:
      result = "Thu";
      break;
    case 5:
      result = "Fri";
      break;
    case 6:
      result = "Sat";
      break;
  }
  return result;
}

 上記のスケッチを書き込んで実行するとWifiに繋がります。その後、NTPサーバーに接続して時刻合わせが完了すると、シリアルモニタに日付と曜日と時刻が表示されます。

 Wifiに繋がる環境さえあれば時刻合わせができるので色々と使い道がありそうです。


・参考資料
WiFi接続とNTPクライアントの実験(ESP-WROOM-32)

【ESP32】SSID、パスワードをSDカードに入れたファイルから読み込んでWiFi接続を行う

 今までスケッチにWiFiSSIDやパスワードをベタ書きしていましたが、スケッチを書き換えなくてもWiFiの接続先を変えたい場合があるかもしれません。そこで、SDカードにあるファイルを読み込んでWiFiに接続するスケッチを書いてみました。


1:材料
ESP32 DevKitC
マイクロSDカードスロットDIP化キット
・壊れても良いようなmicroSDカード


2:配線
 こちらの真ん中あたりにある画像の通りにESP32とSDカードスロットのピンを接続します。
github.com


3:SDカードにSSID、パスワードを記述したファイルを配置
 ファイル名を「pass.txt」として以下のようなファイルを作成し、SDカードの直下に配置します。
・pass.txt

ssidhogehoge
passwordhoge
※1行目がssid、2行目がpass

 記述形式としては、1行目にSSID、2行目にパスワード、3行目は特に何も記述しなくて良いですが3行目は必ず作成しておいてください。


4:スケッチ
 以下のスケッチをESP32に書き込んで実行すると、pass.txtに書かれたWiFiに接続できます。

#include <WiFi.h>

#include "FS.h"
#include "SD.h"
#include "SPI.h"

// SSIDとPASSを渡すための構造体
typedef struct {
  String ssidInfo;
  String passInfo;
} WifiInfo;

const char* path = "/pass.txt";

void setup() {
  Serial.begin(115200);
  // SDカードがマウントされているかの確認
  if(!SD.begin()){
    Serial.println("Card Mount Failed");
    while (1) {}
  }
  // カードタイプの取得
  uint8_t cardType = SD.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    while (1) {}
  }
  // テキストファイルの読み込み
  WifiInfo result = readFileText(SD, path);

  // 設定された値が正しいかどうかを判定
  if (result.ssidInfo == NULL || result.ssidInfo == "") {
    Serial.println("read file error!");
    while (1) {}
  } else {
    connectWifi(stringTochar(result.ssidInfo), stringTochar(result.passInfo));
  }
}

void loop() {

}

/**
 * SDカードの中にあるファイルを読み込んで中のテキストを抽出する関数
 */
WifiInfo readFileText(fs::FS &fs, const char * path){
  String result[] = {"", ""}; 
  WifiInfo resultInfo = {result[0], result[1]};

  // ファイルを開く
  File file = fs.open(path);
  if (!file){
    Serial.printf("file: %s ", path);
    Serial.println("Failed to open file for reading");
    return resultInfo;
  }

  // ファイルの中身をテキストとして読み込む
  int cnt = 0;
  while(file.available()){
    result[cnt] = result[cnt] + char(file.read());
    if (result[cnt].indexOf('\n') != -1) {
      result[cnt] = result[cnt].substring(0, result[cnt].length()-1);
      cnt++;
      if (cnt == 3) {
        break;
      }
    }
  }
  file.close();
  resultInfo.ssidInfo = result[0];
  resultInfo.passInfo = result[1];
  return resultInfo;
}

 /* Wifi接続関数 */
void connectWifi(char* ssid, char* password) {
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

/**
 * 文字列をchar*に変換する関数
 */
char* stringTochar(String command){
  if(command.length()!=0){
    char *p = const_cast<char*>(command.c_str());
    return p;
  }
}

 pass.txtを書き換えるだけで別のWiFiにも接続できるので、スケッチを書き換えるよりかは面倒が少なくなったと思います。ただセキュリティ面を考えると、一度ESP32の不揮発性領域に書き込んでおくのが一番安全な気がします。


・参考資料
github.com
coderwall.com

【ESP32】ST7735RのTFT液晶にbitmap画像を表示させる

 amazonでESP32で使えそうな液晶を探していたら、このような商品を見つけました。

HiLetgo 1.8インチ ドライバ IC ST7735R SPIインタフェース 解像度128*160 TFT液晶ディスプレイモジュール PCBが付け for Arduino 51

 この液晶にESP32を使ってbitmap画像を表示させてみたので、その備忘録です。


1:材料
ESP32 DevKitC
HiLetgo 1.8インチ ドライバ IC ST7735R SPIインタフェース 解像度128*160 TFT液晶ディスプレイモジュール PCBが付け for Arduino 51
・壊れても良いようなSDカード


2:ライブラリをインストールする
 Arduino IDEを立ち上げ、スケッチ→ライブラリをインクルード→ライブラリを管理を選択し、ライブラリマネージャを立ち上げます。
 検索欄に「Adafruit_GFX」、「Adafruit_ST7735」と入力し、それぞれのライブラリをインストールします。

f:id:rikoubou:20170912152001p:plain
f:id:rikoubou:20170912152014p:plain


3:配線
f:id:rikoubou:20170912153637p:plain

 上の写真のようなピン配置になっているので、以下のようにESP32と繋ぎます。

液晶側ピン ESP32側ピン
LED- GND
LED+ 3V3
SD_CS IO4
MOSI IO23
MISO IO19
SCK IO18
CS IO5
SCK IO18
SDA IO23
A0 IO17
RESET IO16
NC -(繋がない)
NC -(繋がない)
NC -(繋がない)
VCC 3V3
GND GND


4:SDカードにbitmap画像を入れる
 SDカードに128×160のサイズのbitmap画像(ファイル名「test.bmp」)を直下に配置します。bitmapを作成できるソフトは色々ありますが、自分の場合はClipStudioPaintでbmp出力させました。
 SDカードに画像を配置したら、液晶の裏面についているSDカードスロットに差し込みます。


5:サンプルソースを動かす
 スケッチの例→Adafruit ST7735 Library→spitftbitmapのファイルを開き、別名で保存します。
 保存したスケッチを以下のように書き換えます。

/* 33〜35行目 */
#define TFT_CS  5   // Chip select line for TFT display
#define TFT_RST 16  // Reset line for TFT (or see below...)
#define TFT_DC  17  // Data/command line for TFT
.
.
.
  Serial.begin(115200); /* 45行目 */
.
.
.
  bmpDraw("/test.bmp", 0, 0);  /* 61行目 */
.
.
.

 以上のように書き換えたらESP32にスケッチを書き込んでください。test.bmpの画像が液晶に表示されます。

f:id:rikoubou:20170912155710p:plain

 画像が表示されずシリアルモニタに「BMP format not recognized.」と表示された場合は用意したbitmap画像が表示する形式に合っていないので、別のソフトで作成するなどしてください。


 これで液晶に表示させることができるようになりました。600円しないぐらいの液晶でカラーの画像を表示させることができるのでかなりお買い得です。ですが、不良品が送られてくる場合もあるようなので、安い分リスクがあることを考慮しておきましょう。


・参考資料
[ESP32][Arduino] SPI TFT液晶 ST7735 を使ってみる – tech
GitHub - MhageGH/esp32_ST7735_Movie: Movie Player on esp32 and ST7735

【python】MacOSでdocomo音声認識APIを使ってみる

 音声認識をやってみたいなーと思って色々調べたところ、簡単に登録できてかつ無料なAPIであるdocomo音声認識APIというものを見つけました。

dev.smt.docomo.ne.jp

 今回はこのdocomo音声認識APIの登録からMacOSでのpythonを使った簡単なサンプルまでを紹介します。


1:docomoDeveloperSupportのアカウントを作成する
 以下のdocomoDeveloperSupportの新規登録からアカウントを作成します。メールアドレスとパスワードを入力し、返信されたメールにあるリンクをクリックするだけで登録は完了します。
dev.smt.docomo.ne.jp


2:APIの使用申請を行う
dev.smt.docomo.ne.jp

 上記サイトにある「申請する」ボタンをクリックします。

 手順は以下に示すブログ様の「API利用登録」の部分を参考に行ってください。

uepon.hatenadiary.com

 申請が完了するとメールが送られてくるので、docomoDeveloperSupportのマイページから払い出されたAPIキーを確認することができます。


3:homebrewをインストールする
 ターミナルを立ち上げ、以下のサイトにあるコマンドを入力してHomebrewをインストールします。
(※インストール中に「HEAD is now ...」と出た後は10分ほど何も出ませんが、待ち続けます)
Homebrew — macOS 用パッケージマネージャー

以下のように表示されていればHomebrewのインストールは完了です。

==> Installation successful!

==> Homebrew has enabled anonymous aggregate user behaviour analytics.
Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics.html

==> Next steps:
- Run `brew help` to get started
- Further documentation: 
    https://docs.brew.sh


4:portaudioをインストールする
 以下のコマンドを実行します。(少し時間がかかります)

$ brew install portaudio

 15分ほど放置しても一向にインストールが進まないという場合は、3のHomebrewのインストールを行ってHomebrewを最新にしてから実行してみてください。


5:pyaudioをインストールする
 以下のコマンドを実行します。(少し時間がかかります)

$ pip install pyaudio

 実行した際に「Failed building wheel for pyaudio」というエラーが表示された場合は、以下のコマンドを実行します。

$ sudo env LDFLAGS="-L/usr/local/lib" CFLAGS="-I/usr/local/include" pip install pyaudio

 これで必要なライブラリのインストールは完了です。


6:docomo音声認識APIを使用する
 Python 3.5.2でのプログラムです。バージョンが違うと動かない場合もあるので注意してください。

・docomoAPI.py

import requests
import pyaudio
import sys
import time
import wave
 

chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000 # サンプリングレート、マイク性能に依存
RECORD_FILE_PATH = 'voice.wav' # 音声ファイルパス
RECORD_SECONDS = 5 # 録音時間
print ('Please speak! >>>')
 
# pyaudio
p = pyaudio.PyAudio()

# マイク0番を設定
input_device_index = 0

# マイクからデータ取得
stream = p.open(format = FORMAT,
                channels = CHANNELS,
                rate = RATE,
                input = True,
                frames_per_buffer = chunk)
all = []
cnt = RECORD_SECONDS
oneTime = int(RATE / chunk)
for i in range(0, oneTime * int(RECORD_SECONDS)):
    if i % oneTime == 0:
        print(cnt)
        cnt = cnt - 1
    data = stream.read(chunk)
    all.append(data)

stream.close()
data = b''.join(all)
out = wave.open(RECORD_FILE_PATH,'w')
out.setnchannels(1) #mono
out.setsampwidth(2) #16bits
out.setframerate(RATE)
out.writeframes(data)
out.close()

p.terminate()

print ('<<< Record end')

# docomoの音声認識APIに音声ファイルを渡してJSONの結果を受け取る
APIKEY = '[APIキー]'
url = "https://api.apigw.smt.docomo.ne.jp/amiVoice/v1/recognize?APIKEY={}".format(APIKEY)
files = {"a": open(RECORD_FILE_PATH, 'rb'), "v":"on"}
r = requests.post(url, files=files)

print(r.json()['text'])

 上記ファイルを実行すると5秒間録音が行われます。録音された音声ファイルをdocomo音声認識APIに渡し、JSONとして受け取った結果を表示させています。


 以上が今回やってみた音声認識です。portaudioのインストールに苦戦したり、参考にさせていただいたプログラムをそのまま実行するとエラーになったりと色々ありましたが、音声認識への第一歩ができました。

 ESP32でも音声認識できるようにしたいなぁ、とかも考えています。


・参考資料
macOSにpyaudioをインストールする - Qiita
RaspberryPiのマイクで録音した音声をテキスト化する【ヒミツのクマちゃん その3】 - uepon日々の備忘録

【お絵描き】それっぽい炎を描く

 とあるアカウントで投稿された炎の表現をできるだけ再現しようと思い、我流で再現した手法を記録しておきます。使用したソフトはClipStudioPaintのみです。

1:チョークブラシを使ってオレンジと黄色で適当に描く
 以下のように太めのブラシサイズにして適当に描きます。
f:id:rikoubou:20170909112413p:plain


2:ガウスぼかしを使ってぼかす
 今回は60ぐらいでぼかしています。
f:id:rikoubou:20170909112629p:plain


3:炎の部分を範囲選択する
 光の棒みたいなアイコンで炎の外側をクリックして選択し、選択範囲を反転させます。


4:新規レイヤーを追加して選択した範囲をより濃い赤色で塗りつぶす
 3で範囲選択した部分をオレンジよりより濃い色で塗りつぶした新規レイヤーをつくります。
f:id:rikoubou:20170909112742p:plain


5:新規レイヤーをガウスぼかしを使ってぼかす
 かなり強め(ここでは150ぐらい)でぼかします。
f:id:rikoubou:20170909113224p:plain


6:新規レイヤーを炎レイヤーの下におき、背景を黒にする
 これで完成。
f:id:rikoubou:20170909113415p:plain


 以上が我流で生み出した炎の書き方になります。対象物の大きさによってぼかしの度合いを変える必要はありますが、
暗いところで少し光ってる感じの炎なら割とそれっぽく見えそうな感じです。

 ちなみにこの手法を使って作ったgifアニメがこちら。
f:id:rikoubou:20170909113639g:plain


・参考資料

 作画も撮影も素晴らしい技術を持った謎のアニメ団さんのTwitterはフォローしているだけで幸せになれるので、どんどんフォローしようね!

【ESP32】OLEDモジュール(M096P4BL)を使ってみる

 ご無沙汰しております。最近ネタがなくてブログの更新をサボっていました。が、ESP32でOLEDモジュールを使うというネタができたので残しておきます。


1:材料
 今回使ったOLEDは以下のものです。多色表示できるものもあるみたいですが、自分は青色単色のものを選びました。

www.aitendo.com


2:ライブラリのインストール
 「Adafruit-GFX-Library」というライブラリと「Adafruit_SSD1306」というライブラリの二つをインストールします。

 Arduino IDEの「ライブラリをインクルード」→「ライブラリを管理」からライブラリマネージャを立ち上げて、以下のライブラリをインストールします。

f:id:rikoubou:20170906170536p:plain
f:id:rikoubou:20170906170549p:plain


3:配線
 以下のように配線します。

ESP32側 OLED側
3V3 VCC
GND GND
GPIO22 SCL
GPIO21 SDA


4:サンプルを動かす
 配線が終わったらAdafruit_SSD1306ライブラリのサンプルにある「ssd1306_128x64_i2c.ino」を開き、ソースの以下の部分を書き換えます。

.
.
.
void setup()   {                
  Serial.begin(115200); // 58行目:元はSerial.begin(9600);のところをSerial.begin(115200);に書き換える。

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64) 61行目:第二引数を0x3Cに書き換える。
.
.
.

 書き換えが終わったら別名で保存し、ESP32に書き込んでください。すると、様々な図形描写のサンプルが表示されます。


 以上がESP32のi2cでOLEDを使った内容です。これで液晶に文字などを表示することができるようになったので、色々と工作の幅が広がりそうです。

・参考資料
programresource.net