LoginSignup
3
1

More than 5 years have passed since last update.

GR-CITRUS+WA-MIKANでHTTPS接続する方法

Last updated at Posted at 2017-01-24

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に以下のファンクションを追加してコンパイルしました。

(※とりあえず動くの第一優先で、動作は確認しましたがメモリあふれとか全然考えていません(^^;)
(※ファームウェアのコンパイルについてはこちらを参考にしてください)

wrbb-v2lib-firm-master\firmware\wrbb_mruby\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)
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1