RECAIUSに繋げようとしたらAPIがSSLのため、GR-CITRUS+WA-MIKANでは簡単にはSSL接続できませんでした。
なぜかというとGR-CITRUSに用意されているWiFiクラスのWiFi.httpGetとWiFi.httpPostがSSLに対応していないからです。
以下は他の方法でSSL接続を試みたときのメモです。
(1) ATコマンドを直接たたく
WiFi.atを使ってESP8266のATコマンドを直接たたく方法です。ただ接続したいだけならこの方法で問題ありません。しかしこの方法では戻り値がコンソールに出るだけで値が保存できません。データを取得したい場合はこの方法では不十分です。
#GR-CITRUS Version 2.28
#ESP8266を一度停止させる(リセットと同じ)
pinMode(5,1)
digitalWrite(5,0) # LOW:Disable
delay 500
digitalWrite(5,1) # LOW:Disable
delay 500
Usb = Serial.new(0,115200)
if( System.useWiFi() == 0)then
Usb.println "WiFi Card can't use."
System.exit()
end
Usb.println "WiFi Ready"
Usb.println "WiFi disconnect"
Usb.println WiFi.disconnect
Usb.println "WiFi Mode Setting"
Usb.println WiFi.setMode 1 #Station-Mode
Usb.println "WiFi connecting"
Usb.println WiFi.connect("SSID","PASSWARD")
Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig
Usb.println "WiFi multiConnect Set"
Usb.println WiFi.multiConnect 1
Usb.println WiFi.at('AT+GMR',1)
Usb.println WiFi.at('CIFSR',0).to_s
#####
host = "try-api.recaius.jp"
url = "/auth/v2/tokens"
Usb.println WiFi.at('AT+CIPMUX=0',1).to_s
Usb.println WiFi.at('AT+CIPSSLSIZE=4096',1).to_s
Usb.println WiFi.at('AT+CIPSTART="SSL","' + host + '",443',1).to_s
#Body部分作成
speechsynthesis_id="xxxxxx"
speechsynthesis_password="xxxxxx"
body ='{"speech_synthesis":{"service_id":"' + speechsynthesis_id + '","password":"' + speechsynthesis_password + '"}}'
#ヘッダー部分作成
s = "POST #{url} HTTP/1.1"
s += "\r\n"
s += "User-Agent: GR-CITRUS/2.28"
s += "\r\n"
s += "Host: #{host}"
s += "\r\n"
s += "Accept: */*"
s += "\r\n"
s += "Content-Type: application/json"
s += "\r\n"
s += "Content-Length: #{body.length.to_s}"
s += "\r\n"
s += "\r\n"
s += body
s += "\r\n"
Usb.println s
Usb.println WiFi.at('AT+CIPSEND='+s.bytesize.to_s,1).to_s
delay(100)
Usb.println WiFi.at(s,1).to_s
delay(500)
Usb.println "WiFi CLOSE"
Usb.println WiFi.at('AT+CIPCLOSE',1).to_s
(2) GR-CITRUSのmrubyのソースを修正するパターン
SSLで接続し取得したデータをSDカードに保存する方法です。この方法ではGR-CITRSのファームウェアを再構築する必要があります。sWiFi.cppに以下のファンクションを追加してコンパイルしました。
(※とりあえず動くの第一優先で、動作は確認しましたがメモリあふれとか全然考えていません(^^;)
(※ファームウェアのコンパイルについてはこちらを参考にしてください)
//**************************************************
// https POSTをSDカードに保存します: WiFi.httpsPostToSD
// WiFi.httpsPostToSD( Filename,URL, Headers, data )
// Filename: 保存するファイル名
// URL: URL
// Headers: ヘッダに追記する文字列の配列
// data: 送信するデータ
// 戻り値は以下のとおり
// 0: 失敗
// 1: 成功
// 2: SDカードが使えない
// ... 各種エラー
//**************************************************
void at(char *atcmd)
{
char *s = atcmd;
int len = strlen(s);
for(int i=0; i<254; i++){
if( i >= len){ break; }
WiFiData[i] = s[i];
}
WiFiData[len] = 0;
RbSerial[WIFI_SERIAL]->println((const char*)WiFiData);
//OK 0d0a か ERROR 0d0aが来るまで WiFiData[]に読むか、指定されたシリアルポートに出力します
getData(WIFI_WAIT_MSEC);
}
mrb_value mrb_wifi_postSSLtoSD(mrb_state *mrb, mrb_value self)
{
mrb_value vFname, vURL, vHeaders, vData;
const char *tmpFilename = "wifitmp.tmp";
char *strData, *strFname, *strURL;
char buf[256] = {'\0'};
char sData[1024] = {'\0'};
File fp;
int len = 0;
int sla = 0;
int koron = 0;
int sHeader = 0;
int sBody = 0;
int cnt = 0 ;
//SDカードが利用可能か確かめます
if (!sdcard_Init(mrb)){
return mrb_fixnum_value(2);
}
//既にファイルがあれば消す
if (SD.exists(tmpFilename)){
SD.remove(tmpFilename);
}
int n = mrb_get_args(mrb, "SSAS", &vFname, &vURL, &vHeaders, &vData);
strFname = RSTRING_PTR(vFname);
strURL = RSTRING_PTR(vURL);
strData = RSTRING_PTR(vData);
sBody = strlen(strData);
//1行目を生成
{
strcpy(sData, "POST /");
//URLからドメインを分割する
len = strlen(strURL);
sla = len;
koron = 0;
for(int i=0; i<len; i++){
if(strURL[i] == '/'){
sla = i;
break;
}
if(strURL[i] == ':'){
koron = i;
}
}
if(koron == 0){
koron = sla;
}
cnt = 6;
for(int i=sla + 1; i<len; i++){
sData[cnt] = strURL[i];
cnt++;
}
sData[cnt] = 0;
strcat(sData, " HTTP/1.1\r\n");
}
//Hostヘッダを生成
{
strcat(sData, "Host: ");
cnt = strlen(sData);
for(int i=0; i<koron; i++){
sData[cnt] = strURL[i];
cnt++;
}
sData[cnt] = 0;
strcat(sData, "\r\n");
}
//Content-Lengthを付けます
{
strcat(sData, "Content-Length: ");
sprintf((char*)WiFiData,"%d", sBody);
strcat(sData, (char*)WiFiData);
strcat(sData, "\r\n");
}
//ヘッダ情報を付けます
{
int n = RARRAY_LEN( vHeaders );
mrb_value hes;
for (int i=0; i<n; i++) {
hes = mrb_ary_ref(mrb, vHeaders, i);
//ヘッダの追記
strcat(sData, RSTRING_PTR(hes));
strcat(sData, "\r\n");
}
}
//改行のみの行を追加する
strcat(sData, "\r\n");
//送信データサイズ取得
sHeader = strlen(sData);
len = sHeader + sBody;
Serial.print("Save Temp hppts Header and Body: ");
Serial.println((const char*)WiFiData);
//****** AT+CIPSTARTコマンド ******
//デバック用
buf[0] = '\0';
strcat(buf, "AT+CIPMUX=1\r\n");
strcat(buf, "AT+CIPSSLSIZE=4096\r\n");
Serial.print(buf);
at("AT+CIPMUX=1");
at("AT+CIPSSLSIZE=4096");
//WiFiData[]に、ドメインとポート番号を取得
for(int i=0; i<sla; i++){
WiFiData[i] = strURL[i];
if(i == koron){
WiFiData[i] = 0;
}
}
WiFiData[sla] = 0;
buf[0] = '\0';
strcat(buf, "AT+CIPSTART=4,\"SSL\",\"");
strcat(buf, (const char*)WiFiData);
strcat(buf, "\",");
if( koron < sla){
strcat(buf, (const char*)&WiFiData[koron + 1]);
}
else{
strcat(buf, "443");
}
Serial.println(buf);
at(buf);
//****** AT+CIPSEND コマンド ******
buf[0] = '\0';
strcat(buf, "AT+CIPSEND=4,");
sprintf((char*)WiFiData,"%d", len);
strcat(buf, (const char*)WiFiData);
Serial.println(buf);
at(buf);
//****** 送信データ受付モードになったので、データを送信する ******
{
//ヘッダを送信する
RbSerial[WIFI_SERIAL]->print((const char*)sData);
//Serial.print(sData);
//ボディを送信する
RbSerial[WIFI_SERIAL]->print((const char*)strData);
//Serial.print(strData);
//OK 0d0a か ERROR 0d0aが来るまで WiFiData[]に読むか、指定されたシリアルポートに出力します
getData(WIFI_WAIT_MSEC);
if( !(WiFiData[strlen((const char*)WiFiData)-2] == 'K' || WiFiData[strlen((const char*)WiFiData)-3] == 'K')){
return mrb_fixnum_value( 0 );
}
Serial.print("Send Finish: ");
Serial.print((const char*)WiFiData);
}
//****** 送信終了 ******
//****** 受信開始 ******
if( !(fp = SD.open(tmpFilename, FILE_WRITE)) ){
return mrb_fixnum_value( 6 );
}
unsigned long times;
unsigned int wait_msec = WIFI_WAIT_MSEC;
unsigned char recv[2];
times = millis();
#if BOARD == BOARD_GR
int led = digitalRead(PIN_LED0) | ( digitalRead(PIN_LED1)<<1) | (digitalRead(PIN_LED2)<<2)| (digitalRead(PIN_LED3)<<3);
#else
int led = digitalRead(RB_LED);
#endif
while(true){
//wait_msec 待つ
if(millis() - times > wait_msec){
break;
}
while(len = RbSerial[WIFI_SERIAL]->available())
{
//LEDを点灯する
#if BOARD == BOARD_GR
digitalWrite(PIN_LED0, HIGH);
digitalWrite(PIN_LED1, HIGH);
digitalWrite(PIN_LED2, HIGH);
digitalWrite(PIN_LED3, HIGH);
#else
digitalWrite(RB_LED, HIGH);
#endif
for(int i=0; i<len; i++){
recv[0] = (unsigned char)RbSerial[WIFI_SERIAL]->read();
fp.write( (unsigned char*)recv, 1);
}
times = millis();
wait_msec = 1000; //データが届き始めたら、1sec待ちに変更する
//LEDを消灯する
#if BOARD == BOARD_GR
digitalWrite(PIN_LED0, LOW);
digitalWrite(PIN_LED1, LOW);
digitalWrite(PIN_LED2, LOW);
digitalWrite(PIN_LED3, LOW);
#else
digitalWrite(RB_LED, LOW);
#endif
}
}
fp.flush();
fp.close();
//****** 受信終了 ******
Serial.println("Recv Finish");
//受信データに '\r\n+\r\n+IPD,4,****:'というデータがあるので削除します
int ret = CutGarbageData("\r\n+IPD,4,", tmpFilename, strFname);
if(ret != 1){
return mrb_fixnum_value( 7 );
}
//****** AT+CIPCLOSE コマンド ******
at("AT+CIPCLOSE=4");
Serial.println((const char*)WiFiData);
#if BOARD == BOARD_GR
digitalWrite(PIN_LED0, led & 1);
digitalWrite(PIN_LED1, (led >> 1) & 1);
digitalWrite(PIN_LED2, (led >> 2) & 1);
digitalWrite(PIN_LED3, (led >> 3) & 1);
#else
digitalWrite(RB_LED, led);
#endif
return mrb_fixnum_value( 1 );
}
最後にhttpsPostToSDをmruby用のmofule_functionの行を追加しておく。
mrb_define_module_function(mrb, wifiModule, "httpPostSD", mrb_wifi_postSD, MRB_ARGS_REQ(3));
mrb_define_module_function(mrb, wifiModule, "httpsPostToSD", mrb_wifi_postSSLtoSD, MRB_ARGS_REQ(4)); //←追加
使い方
#!mruby
#GR-CITRUS Version 2.28
#ESP8266を一度停止させる(リセットと同じ)
pinMode(5,1)
digitalWrite(5,0) # LOW:Disable
delay 500
digitalWrite(5,1) # LOW:Disable
delay 500
Usb = Serial.new(0,115200)
if( System.useWiFi() == 0)then
Usb.println "WiFi Card can't use."
System.exit()
end
Usb.println "WiFi Ready"
Usb.println "WiFi Get Version"
Usb.println WiFi.version
Usb.println "WiFi disconnect"
Usb.println WiFi.disconnect
Usb.println "WiFi Mode Setting"
Usb.println WiFi.setMode 1 #Station-Mode
Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig
Usb.println "WiFi connecting"
Usb.println WiFi.connect("SSID","PASSWARD")
Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig
heds=["User-Agent: grcitrus","Content-Type: application/json","Accept: */*"]
data='{"speech_synthesis":{"service_id":"xxxxxx","password":"xxxxxxx"}}'
Usb.println WiFi.httpsPostToSD("recaius.htm","try-api.recaius.jp/auth/v2/tokens", heds,data).to_s
Usb.println "WiFi disconnect"
Usb.println WiFi.disconnect
結果
-------- コンソールに接続しました --------
Save Temp hppts Header and Body: 79
AT+CIPMUX=1
AT+CIPSSLSIZE=4096
AT+CIPSTART=4,"SSL","try-api.recaius.jp",443
pre httpsServer Connect:
AT+CIPSEND=4,227
Send Finish: >
Recv 227 bytes
SEND OK
Recv Finish
4,CLOSED
OK
1
WiFi disconnect
OK
-------- コンソールから切断されました --------
RECAIUS.HTMが以下で保存される。
HTTP/1.1 201 Created
Date: Mon, 23 Jan 2017 03:34:23 GMT
Content-Type: application/json
Content-Length: 65
Connection: keep-alive
Server:
X-Frame-Options: DENY
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS, DELETE
Access-Control-Allow-Headers: content-type, X-TOKEN
{"expiry_sec":600,"token":"xxxxxxxxxx"}
(3) Serial経由で直接ATコマンドをたたく
(1)とほとんど同じですが、これならデータも受け取れます。
ただし、レスポンスのタイミングの調整をどうしようかと悩み中
#!mruby
#GR-CITRUS Version 2.28
class MIKAN
def initialize(params)
@bps = params[:bps] || 115200
@txrxnum = params[:txrxnum] || 3
@esp = Serial.new(@txrxnum.to_i,@bps.to_i)
delay(1000)
end
def bps
@bps
end
def txrxnum
@txrxnum
end
def cmd(command)
resultstr=""
@esp.println command
delay(100)
while(@esp.available() > 0) do
resultstr += @esp.read()
delay(100)
end
resultstr
end
def read
resultstr=""
while(@esp.available() > 0) do
resultstr += @esp.read()
delay(100)
end
resultstr
end
end
#ESP8266を一度停止させる(リセットと同じ)
pinMode(5,1)
digitalWrite(5,0) # LOW:Disable
delay 500
digitalWrite(5,1) # LOW:Disable
delay 500
Usb = Serial.new(0,115200)
mikan = MIKAN.new(txrxnum:3,bps:115200)
Usb.println "GR-CITRUS & WA-MIKAN"
Usb.println (mikan.cmd "AT")
#Usb.println (mikan.cmd "AT+RST")
Usb.println (mikan.cmd "AT+CIFSR")
Usb.println (mikan.cmd "AT+GMR")
Usb.println (mikan.cmd "AT+CWMODE=1")
Usb.println (mikan.cmd 'AT+CWJAP="SSID","PASSWARD"')
delay(10000)
host = "try-api.recaius.jp"
url = "/auth/v2/tokens"
Usb.println (mikan.cmd "AT+CIPSSLSIZE=4096")
Usb.println (mikan.cmd 'AT+CIPSTART="SSL","' + host + '",443')
body='{"speech_synthesis":{"service_id":"xxxxx","password":"xxxxxx"}}'
#ヘッダー部分作成
s = "POST #{url} HTTP/1.1"
s += "\r\n"
s += "User-Agent: GR-CITRUS/2.28"
s += "\r\n"
s += "Host: #{host}"
s += "\r\n"
s += "Accept: */*"
s += "\r\n"
s += "Content-Type: application/json"
s += "\r\n"
s += "Content-Length: #{body.length.to_s}"
s += "\r\n"
s += "\r\n"
s += body
s += "\r\n"
Usb.println s
Usb.println (mikan.cmd "AT+CIPSEND=#{s.length}")
delay(500)
Usb.println (mikan.cmd s)