ESP8266にカラーTFT液晶をSPI接続してみる

esp826607-01.jpg

ESP8266にキャラクタ液晶はちゃんとつながりましたので、今度は先日使ったカラーTFT LCD(aitendo M-Z18SPI-2P)をつないでみました。

環境自体はArduinoですので接続だけで済むかも、と期待したのですが、残念ながらこれに使用するライブラリ(Adafruit_ST7735)はそのままではコンパイルできませんでした。

しかし、幸いなことに既にこのライブラリをESP8266にポーティングしている方がいました。

lcd: add support for the ESP8266. · nzmichaelh/Adafruit-ST7735-Library@999f5ff

この修正は他の修正と競合するらしく、現時点ではまだ本体へ統合されていません。
使ってみるには、以下からこの修正済みライブラリをダウンロードします。

nzmichaelh/Adafruit-ST7735-Library

なお、オリジナルと同様、このライブラリはM-Z18SPI-2Pでは画面にゴミが残りますので、以前に書いた修正をこのライブラリに対して行います。

Arduino(9) Adafruit_ST7735ライブラリの修正(aitendoの1.8型LCD用): 楽しくやろう。

このライブラリを使って、付属のスケッチ例「graphicstest」を動かしてみました。

結線は以下のようになります。
例によって、この図はプログラム書き込み等に必要な共通部分は省略しています。
省略部分はこちらを参照してください。

ESP8266-Z18SPI.png

信号同士の接続は以下のようになっています。

LCD / ESP8266
RST / IO5
A0  / IO4
SDA / IO13
SCK / IO14
CS  / IO15

graphicstestスケッチのソースコードも、この結線を反映するように変更します。
また、画面にゴミが残らない修正を使うため、LCDの種別はBLUETABに変更します。
赤字が変更部分です。
IO13とIO14はデフォルトの結線ですので、記述しなければSDAとSCKに接続されたものとして扱われます。
デフォルトの結線については別記事で紹介しました

// For the breakout, you can use any 2 or 3 pins
// These pins will also work for the 1.8" TFT shield
#define TFT_CS     15
#define TFT_RST    5  // you can also connect this to the Arduino reset
// in which case, set this #define pin to 0!
#define TFT_DC     4

// Option 1 (recommended): must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

// Option 2: use any pins but a little slower!
#define TFT_SCLK 13   // set these to be whatever pins you like!
#define TFT_MOSI 11   // set these to be whatever pins you like!
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

(中略)

void setup(void) {
Serial.begin(9600);
Serial.print("Hello! ST7735 TFT Test");
// SPI.setClockDivider(SPI_CLOCK_DIV128);

// Use this initializer if you're using a 1.8" TFT
tft.initR(INITR_BLUETAB);   // initialize a ST7735S chip, black tab

動作の様子です。
クロックがArduino Pro Mini(3.3v)の10倍ありますので、描画が比べ物にならないほど高速です。

ESP8266版Arduinoでネットから情報を取ってきてLCDに表示する

esp826606-01.jpg

前回、ESP8266でキャラクタ液晶を制御できましたので、ESP8266ならではのWiFi機能を使い、インターネットから情報を取得して表示させてみます。

何か動的な情報で、16字×2行のアルファベットで表示できるもの、ということで、最近動きの慌しい為替レートを表示させてみることにしました。

今回は、回路は前回と全く同じです。

為替レートの情報は、

Foreign exchange rates and currency conversion JSON API

から取得することにしました。

このサイトでは、各種通貨の為替レートをJSON形式で取得できます。
他にも同様のサイトはあるのですが、最近のWebサービスではユーザー登録してAPI Keyを発行してもらう必要があるものがほとんどで、ちょっと実験的に試すために使うにはやや面倒です。
上記のサイトは、一切事前の準備なしに、URLを叩くだけでデータをもらえます。
(その代わり、データの更新頻度はあまり高くないようです。)

ドル円レートの情報を取得する場合、URLは以下のようになります。
(リンクを直接クリックするとエラーになりますので、ブラウザのURL欄へコピー&ペーストしてください。)

http://api.fixer.io/latest?base=USD&symbols=JPY

これに対する応答は以下のような形式になります。

{"base":"USD","date":"2015-08-28","rates":{"JPY":120.84}}

非常にシンプルですね。
なおJSON形式については以下を参照してください。

JSON

今回は、ESP8266から上記のURLにアクセスして、取得したJSONデータを解析(パース)してキャラクタ液晶に表示します。

JSON形式の文字列のパーサは、ArduinoJsonというライブラリを使用しました。

bblanchon/ArduinoJson

ライブラリの追加の仕方は以前紹介した通りです。
ちなみに、このライブラリの作者の方のブログが以下にあります。

[Arduino] JSON library 4.0

なお、私は試していませんが、これ以外にArduinoで使えるJSONパーサのライブラリとしては、aJsonというものもあるようです(以下のリンク)。

interactive-matter/aJson

WiFi関連の処理は、もともとESP8266のArduino対応ライブラリに含まれています。
TCP/IPやDNSもサポートされています。
TCP/IP、特にTCPの実装は大変ですので、すべて実装済みというのは大変ありがたいことです。

HTTPプロトコルは、今回は最低限の実装だけを行います。
HTTPは状態遷移がないので、エラー処理等を行わないのであればプロトコル自体は簡単です。

例えば

http://host.domain/dir/file

というURLにアクセスする際のサーバとクライアントのやり取りは、

(1)クライアントからhost.domainの80番ポートにTCPで接続
(2)以下をサーバへ送信

GET /dir/file HTTP/1.1
Host: host.domain
Connection: close
(空の行)

(3)サーバから以下の形式で応答が返ってくる(正常処理の場合)

HTTP/1.1 200 OK
(いろいろなHTTPヘッダ)
(空の行)
要求されたデータ

(4)サーバから通信が切断される

という流れになります。

スケッチファイルは以下の通りです。
ST7032ライブラリは、前回説明したように修正が必要です。
また、WIFI_SSIDとWIFI_PSKは、ご自身のWIFIのSSIDとパスワードに変更してください。

#include <ESP8266WiFi.h>
#include <Wire.h>
#include <ST7032.h>
#include <ArduinoJson.h>

#define WIFI_SSID "*************"
#define WIFI_PSK "*************"
#define DEST_HOST "api.fixer.io"
#define DEST_PORT 80
#define DEST_URL "/latest?base=USD&symbols=JPY"

//sample json data used in this sketch
// {"base":"USD","date":"2015-08-28","rates":{"JPY":120.84}}

ST7032 lcd;

void setup() {
 const int BUFFER_SIZE = JSON_OBJECT_SIZE(4) + JSON_ARRAY_SIZE(1);
 StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
 
 lcd.begin(16, 2);
 lcd.setContrast(40);
 Serial.begin(115200);
 Serial.println("");
 delay(10);

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

 WiFiClient client;
 String line;

 if (!client.connect(DEST_HOST, DEST_PORT)) {
   Serial.println("connection failed");
   return;
 }
 
 client.print(String("GET ") + DEST_URL + " HTTP/1.1\r\n" +
              "Host: " + DEST_HOST + "\r\n" +
              "Connection: close\r\n\r\n");
 delay(10);

 //get rid of the HTTP headers
 while(client.available()){
   line = client.readStringUntil('\r');
   Serial.print(line);
   line.trim();
   if (line.length() == 0) {
     break;
   }
 }

 //get http content
 String buffer="";
 while(client.available()){
   line = client.readStringUntil('\r');
   line.trim();
   buffer.concat(line);
 }

 //parse json data
 char json[buffer.length() + 1];
 buffer.toCharArray(json, sizeof(json));
 Serial.println(json);
 JsonObject& root = jsonBuffer.parseObject(json);
 if (!root.success()) {
   Serial.println("parseObject() failed");
   return;
 }
 const char* date = root["date"];
 Serial.println(date);
 const char* base = root["base"];
 Serial.println(base);
 JsonObject& rates = root["rates"];
 rates.printTo(Serial);
 Serial.println();
 const char* rate = rates["JPY"];
 Serial.println(rate);
 Serial.println();
 
 Serial.println("closing connection");

 lcd.setCursor(0, 0);
 lcd.print(date);
 lcd.setCursor(0, 1);
 lcd.print("JPY/");
 lcd.print(base);
 lcd.print(": ");
 lcd.print(rate);
}

void loop() {
}

データを一度取得してLCDに表示するだけですので、すべての処理はsetup()の中で行っています。
HTTPヘッダは、内容を確認せずに読み飛ばすだけで、サーバからエラーが返ってきても対応はしません。

デバッグ用に、シリアルポートにいろいろ出力していますが、参考までにその出力例を以下に示します。

....
WiFi connected
IP address:
192.168.0.106

HTTP/1.1 200 OK
Server: nginx/1.4.6 (Ubuntu)
Date: Sat, 29 Aug 2015 15:44:10 GMT
Content-Type: application/json
Content-Length: 57
Connection: close
Status: 200 OK
X-Content-Type-Options: nosniff
{"base":"USD","date":"2015-08-28","rates":{"JPY":120.84}}
2015-08-28
USD
{"JPY":120.84}
120.84

closing connection

ESP8266+Arduino IDE+I2C+ST7032液晶ディスプレイを使う

esp826605-01.jpg

ESP8266(WROOM-02)がArduino IDEで動くようになりましたので、手始めにキャラクタ液晶をつないでみました。

使ったのは、以前Arduino Pro Miniで使用した、aitendoの激安液晶です。
ソフトウェアも、同じく

I2C液晶のArduinoライブラリ – ST7032 | オレ工房

を使わせていただきました。

ライブラリ(ST7032.cpp)は微小な修正が必要でした。
このファイルは、ドキュメントフォルダの中の

 Arduino\libraries\arduino_ST7032-master\ST7032.cpp

に格納されています。

修正は、ST7032.cppの30行過ぎのあたり、以下の赤い部分を追加しました。

#include "ST7032.h"
#include "Arduino.h"
#include <Wire.h>
#if defined(__AVR__)
#include <avr/pgmspace.h>
#endif

配線は、下図のようになります。
これはこちらの回路からの差分です。
Arduinoのスケッチ書き込みに必要な回路は省略しています。

esp826605-02.png

以上で、上記のST7032ライブラリに付属しているサンプルスケッチが動作しました。

なお、上の配線はI2CのSDA・SCLがそれぞれIO4・IO5になっていますが、このマッピングはESP8266のボードによって異なります。
今回は、Arduinoe IDEのボードメニューから”Generic ESP8266 Module”を選択しており、このボードでのピン配置に従います。

このピン配置の情報は、先日Arduino IDEに追加したボード情報の中で記述されています。
ファイルは、
 %USERPROFILE%\AppData\Roaming\Arduino15\packages\esp8266\hardware\esp8266\1.6.5-947-g39819f0\variants\generic

というフォルダの中の”pins_arduino.h”です。
(%USERPROFILE%には、”C:\Users\ユーザ名”が入ります。
また、”1.6.5-947-g39819f0″の部分はIDEのバージョンによって変わってきそうです。)

このファイルでは、I2CおよびSPIのピン配置が以下のように定義されています。

static const uint8_t SDA = 4;
static const uint8_t SCL = 5;

static const uint8_t SS    = 15;
static const uint8_t MOSI  = 13;
static const uint8_t MISO  = 12;
static const uint8_t SCK   = 14;

static const uint8_t BUILTIN_LED = 1;

static const uint8_t A0 = 17;

ちなみに、Generic以外に定義されているボードは、現時点では

adafruit
nodemcu
thing
wifio

の4つでした。

なお「thing」はSparkfunの出しているESP8266ボードですが、このボードの解説ページ(以下のリンク)は分かりやすくて良いと思います。

ESP8266 Thing Hookup Guide – learn.sparkfun.com

ESP8266でArduinoを動かす場合、簡単なスケッチでもファイルサイズは200KB超と結構大きなものができてきますが、上の解説によると、WiFiや通信機能を動作させるために、裏で行う様々な処理のためのコードが含まれているそうです。