{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "オリジナルの作成:2015/10/31" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arduinoのスケッチを作る\n", "Arduino勉強会/0N-WiFiモジュールその1 でESP-WROOM-02を使ってWebサーバにアクセスできるところまで確認しました。\n", "\n", "今回は、プログラムを書いてESP-WROOM-02を操作してみましょう。\n", "\n", "\n", "### 定数の定義\n", "ATコマンドやSSID、パスワードは#define分で定義しておきます。\n", "\n", "```C++\n", "#define ST_SSID \"<>\"\n", "#define ST_PASSWD \"<<パスワードを指定>>\"\n", "\n", "#define SERVER_ADDR \"49.212.164.205\"\n", "#define SERVER_PORT 80\n", "#define CRLF \"\\r\\n\"\n", "#define CRLF2 \"\\r\\n\\r\\n\"\n", "\n", "#define STATION_MODE 1\n", "```\n", "\n", "\n", "### コマンドを関数にまとめる\n", "以下のATコマンドを関数に整理します。\n", "\n", "- AT+CWMODE\n", "- AT+CWJAP\n", "- AT+CIPSTART\n", "- AT+CIPSEND\n", "\n", "ATコマンドは終わりにCR+LFを追加するためにatCommand関数も一緒に作ります。\n", "\n", "```C++\n", "int atCommand(char *cmd) {\n", " Serial.print(cmd);\n", " Serial.print(CRLF);\n", " return 1;\n", "}\n", "\n", "int atCwMode(int mode) {\n", " sprintf(buf, \"AT+CWMODE=%d\", mode);\n", " return atCommand(buf);\n", "}\n", "\n", "int atCwJap(char *ssid, char *passwd) {\n", " sprintf(buf, \"AT+CWJAP=\\\"%s\\\",\\\"%s\\\"\", ssid, passwd);\n", " return atCommand(buf);\n", "}\n", "\n", "int atCipStart(char *server_addr, int server_port) {\n", " sprintf(buf, \"AT+CIPSTART=\\\"TCP\\\",\\\"%s\\\",%d\", server_addr, server_port);\n", " return atCommand(buf);\n", "}\n", "int atCipSend(char *uri) {\n", " sprintf(buf, \"GET %s HTTP/1.0%s%s\", uri, CRLF, CRLF2);\n", " int len = strlen(buf);\n", " Serial.print(\"AT+CIPSEND=\");\n", " Serial.print(len);\n", " Serial.print(CRLF);\n", " delay(1000);\n", " Serial.print(buf);\n", "\n", "}\n", "```\n", "### 初期化\n", "ESP-WROOM-02の操作の内、以下の処理は1回だけすればよいので初期化(setup) にまとめます。\n", "\n", "- モード設定:\n", "- ルータに接続:\n", "\n", "```C++\n", " atCwMode(STATION_MODE);\n", " delay(1000);\n", " atCwJap(ST_SSID, ST_PASSWD);\n", "```\n", "\n", "### ボタンアクションの追加\n", "タクトスイッチのボタンを押したときに、Sageサーバにアクセスするように スケッチを修正します。\n", "\n", "```C++\n", " if (digitalRead(sw_pin) == LOW) {\n", " pc.println(\"SW pressed\");\n", " delay(500);\n", " atCipStart(SERVER_ADDR, SERVER_PORT);\n", " delay(1000);\n", " atCipSend(\"/hello.html\"); \n", " }\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## テスト\n", "準備ができたので、スケッチを動かしてみます。 コマンドの実行状況がシリアルモニタに表示されるので、プログラムミスや設定ミスがすぐに分かります。\n", "\n", "```\n", "ESP8266IF3tTest\n", "AT+CWMODE=1\n", "\n", "OK\n", "AT+CWJAP=\"<>\",\"<<パスワードを指定>>\"\n", "\n", "WIFI DISCONNECT\n", "WIFI CONNECTED\n", "WIFI GOT IP\n", "\n", "OK\n", "SW pressed\n", "AT+CIPSTART=\"TCP\",\"49.212.164.205\",80\n", "\n", "CONNECT\n", "\n", "OK\n", "AT+CIPSEGET /hello.html HTTP/1.0\n", "Recv 30 bytes\n", "\n", "SEND OK\n", "\n", "+IPD,303:HTTP/1.1 200 OK\n", "Date: Tue, 27 Oct 2015 12:29:51 GMT\n", "Server: Apache\n", "Last-Modified: Sat, 24 Oct 2015 23:49:16 GMT\n", "ETag: \"c005e-34-522e260431c88\"\n", "Accept-Ranges: bytes\n", "Content-Length: 52\n", "Connection: close\n", "Content-Type: text/html; charset=UTF-8\n", "\n", "\n", "\n", "

Hello World!

\n", "\n", "\n", "CLOSED\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## atCommandの変更\n", "これまでatCommand関数が正しく処理できたかを確認していませんでした。\n", "\n", "ATコマンドでは正しく処理できたときにOK、失敗の時にERRORを返します。 そこで、最後に返された文字がKかRで正常修了と異常終了の判定することにします。\n", "\n", "atCommandを以下の様に変更して、再度スケッチを実行してみます。\n", "\n", "処理によって、OKがでるまでの時間が異なるため、atCommandにコマンドを送った後の 待ち時間を入れることにします。\n", "\n", "レスポンスの出力に少し文字の取りこぼしがありますが、何とかHello Worldのメッセージ を受信できるようになりました。\n", "\n", "現段階のスケッチは、以下の通りです。\n", "\n", "```C++\n", "#include \n", "\n", "\n", "#define ST_SSID \"<>\"\n", "#define ST_PASSWD \"<<パスワードを指定>>\"\n", "\n", "#define SERVER_ADDR \"49.212.164.205\"\n", "#define SERVER_PORT 80\n", "#define CRLF \"\\r\\n\"\n", "#define CRLF2 \"\\r\\n\\r\\n\"\n", "\n", "#define STATION_MODE 1\n", "#define WAIT_ONECE 2000\n", "#define OK 'K'\n", "#define ERR 'R'\n", "#define FAIL 'L'\n", "\n", "char buf[128];\n", "\n", "int interval_waits[] = {50, 80, 160, 200, 300, 600, 1200, 1500};\n", "int retry_count = sizeof(interval_waits)/sizeof(int);\n", "int sw_pin = 10;\n", "int sTx_pin = 12;\n", "int sRx_pin = 11;\n", "int c;\n", "\n", "SoftwareSerial pc(sRx_pin, sTx_pin);\n", "\n", "int last_char = -1;\n", "int waitForResponse() {\n", " last_char = -1;\n", " int err_count = 0;\n", " // 返されたデータをすべて読み込む\n", " for (int i = 0; i < retry_count; i++) {\n", " while (Serial.available()) {\n", " while((c = Serial.read()) != -1) { \n", " if (c != '\\r' && c != '\\n')\n", " last_char = c;\n", " // デバッグ用\n", " pc.write(c);\n", " } \n", " }\n", " // OK, ERROR, > , FAIL, CLOSED\n", " if (last_char == OK || last_char == ERR || last_char == ' ' || last_char == 'L' || last_char == 'D') break;\n", " // pc.println(\"wait\");\n", " delay(interval_waits[err_count++]);\n", " }\n", "}\n", "\n", "int atCommand(char *cmd, int wait_time) {\n", " int c;\n", " \n", " Serial.print(cmd);\n", " Serial.print(CRLF);\n", " delay(wait_time);\n", " // 応答を待つ\n", " waitForResponse();\n", " // 最後に返された文字がKまたはRでなかったら、WAIT_ONECEミリ秒待つ\n", " if (last_char != OK && last_char != ERR && last_char != ' ') {\n", " delay(WAIT_ONECE);\n", " pc.println(\"Do wait onece\");\n", " if (Serial.available()) {\n", " while((c = Serial.read()) != -1) { \n", " if (c != '\\r' || c != '\\n')\n", " last_char = c;\n", " // デバッグ用\n", " pc.write(c);\n", " } \n", " }\n", " }\n", " if (last_char == OK)\n", " return 1;\n", " else if (last_char == ERR)\n", " return 0;\n", " else\n", " return -1;\n", "}\n", "\n", "int atCwMode(int mode) {\n", " sprintf(buf, \"AT+CWMODE=%d\", mode);\n", " return atCommand(buf, 0);\n", "}\n", "\n", "int atCwJap(char *ssid, char *passwd) {\n", " sprintf(buf, \"AT+CWJAP=\\\"%s\\\",\\\"%s\\\"\", ssid, passwd);\n", " return atCommand(buf, 2000);\n", "}\n", "\n", "int atCipStart(char *server_addr, int server_port) {\n", " sprintf(buf, \"AT+CIPSTART=\\\"TCP\\\",\\\"%s\\\",%d\", server_addr, server_port);\n", " return atCommand(buf, 500);\n", "}\n", "\n", "int atCipSend(char *uri) {\n", " sprintf(buf, \"GET %s HTTP/1.0%s%s\", uri, CRLF, CRLF2);\n", " int len = strlen(buf);\n", " char tmp[32];\n", " sprintf(tmp, \"AT+CIPSEND=%d%s\", len, CRLF);\n", " // uriを送る\n", " atCommand(tmp, 1000);\n", " Serial.print(buf);\n", " // 応答を待つ\n", " waitForResponse();\n", "}\n", "\n", "void setup() {\n", " pc.begin(9600);\n", " Serial.begin(9600);\n", " while (!Serial) {\n", " ; // wait for serial port to connect. Needed for Leonardo only\n", " }\n", " pinMode(sw_pin, INPUT);\n", " pc.println(\"ESP8266IF3tTest\");\n", " delay(1000);\n", " atCwMode(STATION_MODE);\n", " atCwJap(ST_SSID, ST_PASSWD);\n", "}\n", "\n", "void loop() {\n", " if (pc.available()) {\n", " while((c = pc.read()) != -1)\n", " Serial.write(c);\n", " }\n", "\n", " if (Serial.available()) {\n", " pc.println(\"Got Response\");\n", " while((c = Serial.read()) != -1)\n", " pc.write(c);\n", " }\n", "\n", " if (digitalRead(sw_pin) == LOW) {\n", " pc.println(\"SW pressed\");\n", "\n", " delay(500);\n", " atCipStart(SERVER_ADDR, SERVER_PORT);\n", " atCipSend(\"/hello.html\"); \n", " }\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## クラスにまとめる\n", "取りあえず、HTTPサーバにGET要求を送るだけのクラスEspClientを以下の様に定義し、 作成しました。\n", "\n", "```C++\n", "class EspClient {\n", "public:\n", " EspClient(int rx_pin, int tx_pin);\n", " void begin(int baud);\n", " int atCommand(char *cmd, int wait_time);\n", " int atCwMode(int mode);\n", " int atCwJap(char *ssid, char *passwd);\n", " int atCipStart(char *server_addr, int server_port);\n", " int atCipSend(char *uri);\n", " int atRest();\n", " void atClose();\n", " void println(char *s);\n", "protected:\n", " int waitForResponse();\n", " static const int interval_waits[8];\n", " static const int retry_count;\n", "private:\n", " char buf[256];\n", " int last_char;\n", " SoftwareSerial pc;\n", "};\n", "```\n", "\n", "### クラスのテスト\n", "先ほどのスケッチをEspClientを使った形に修正します。\n", "\n", "```C++\n", "#include \"EspClient.h\"\n", "#include \n", "\n", "\n", "#define ST_SSID \"<>\"\n", "#define ST_PASSWD \"<<パスワードを指定>>\"\n", "#define SERVER_ADDR \"49.212.164.205\"\n", "#define SERVER_PORT 80\n", "\n", "int sw_pin = 10;\n", "int sTx_pin = 12;\n", "int sRx_pin = 11;\n", "\n", "EspClient esp(sRx_pin, sTx_pin);\n", "\n", "void setup() {\n", " pinMode(sw_pin, INPUT);\n", " esp.begin(9600);\n", " esp.println(\"ESP8266IF3tTest\");\n", " esp.atCwMode(STATION_MODE);\n", " esp.atCwJap(ST_SSID, ST_PASSWD);\n", "}\n", "\n", "void loop() {\n", " if (digitalRead(sw_pin) == LOW) {\n", " esp.println(\"SW pressed\");\n", " // チャタリング防止\n", " delay(500);\n", " esp.atCipStart(SERVER_ADDR, SERVER_PORT);\n", " esp.atCipSend(\"/hello.html\"); \n", " }\n", "}\n", "```\n", "\n", "### スケッチのダウンロード\n", "ESPClientTestのスケッチを以下からダウンロードできます。\n", "\n", "- ESPClientTestスケッチ [fileESPClientTest.zip](data/fileESPClientTest.zip)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 0 }