ESP8266をWifiモデムとして使う

ライブラリを使わないSocket通信(サーバ編)


ESP8266用のライブラリはいくつかありますが、所詮シリアル通信でATコマンドを投げているだけなので、
ライブラリを使わずに通信してみることにしました。

今回は、Raspberryがクライアント、Arduino+ESP-01がサーバーの動作を紹介します。
Raspberry側のプログラムはこちらと同じものを使いま す。

Arduino側のスケッチは以下の通りです。
Arduinoをサーバーとして動かす場合、自分のIPアドレスは固定できたほうが便利です。
IPアドレスを固定する方法もこちらと同じです。
SSIDにはアクセスポイントのSSID、PASSWORDにはそのパスワード、SERVERには自分のIPアドレスを指定してください。

/*
 * ESP8266 TCP Server with AT Command
 *  
 * ESP8266----------ATmega
 * ESP8266_RX    -> TX(D2)
 * ESP8266_TX    -> RX(D3)
 *
 */

#include <SoftwareSerial.h>

#define rxPin 2    // PD2
#define txPin 3    // PD3

#define SSID        "APのSSID"
#define PASSWORD    "APのパスワード"
#define SERVER      "192.168.10.50"
#define PORT        "9876"
#define INTERVAL    5000
#define DEBUG       0

//answer strings from ESP
char str_buffer[64];
//command strings to ESP
char cmd[64];

SoftwareSerial myESP8266(rxPin, txPin); // RX, TX

//Wait for specific input string until timeout runs out
bool waitForString(char* input, uint8_t length, unsigned int timeout) {

  unsigned long end_time = millis() + timeout;
//  int current_byte = 0;
  char current_byte = 0;
  uint8_t index = 0;

   while (end_time >= millis()) {
    
      if(myESP8266.available()) {
        
        //Read one byte from serial port
        current_byte = myESP8266.read();
        Serial.print(current_byte);
        if (current_byte != -1) {
          //Search one character at a time
          if (current_byte == input[index]) {
            index++;
            
            //Found the string
            if (index == length) {              
              return true;
            }
          //Restart position of character to look for
          } else {
            index = 0;
          }
        }
      }
  }  
  //Timed out
  return false;
}

//Remove all bytes from the receive buffer
void clearBuffer() {
  while (myESP8266.available())
    myESP8266.read();
  Serial.println("");

}

//Print send string
void sendCommand(char* buff) {
  Serial.print(buff);
  Serial.println("-->");
  myESP8266.println(buff);
  myESP8266.flush();
}

//Print error
void errorDisplay(char* buff) {
  Serial.print("Error:");
  Serial.println(buff);
  while(1) {}
}

//Receive Message
uint8_t readResponse(int id, char * buf,int sz_buf, int timeout) {
  int len=0;
  int flag=0;
  int datalen;
  long int time = millis();

  // id < 0  +IPD,nn:ReceiveData
  // id = 0  +IPD,0,nn:ReceiveData
  // id > 0  +IPD,id,nn:ReceiveData
  while( (time+timeout) > millis()) {
    while(myESP8266.available())  {
      char current_byte = myESP8266.read(); // read the next character.
      Serial.print(current_byte);
      if (flag == 0) {
        if (current_byte != 0x2c) continue;
        flag++;
        if (id < 0) flag++;
      } else if (flag == 1) {
        if (current_byte != 0x2c) continue;
        flag++;
      } else if (flag == 2) {
        if (current_byte == 0x3a) { // :
          datalen=atoi(buf);
          flag++;
          len=0;
        } else {
          buf[len++]=current_byte;
          buf[len]=0;
        }
      } else {
        buf[len++]=current_byte;
        if (len == sz_buf) return -len;
        buf[len]=0;
        if (len == datalen) return len;
      }
    } // end while
  } // end while
  return -len;
}

// Read Response
int waitConnect(int timeout) {
  int len=0;
  long int time = millis();
  char current_byte = 0;
  bool endFlag = 0;
 
  while( (time+timeout) > millis()) {
    while(myESP8266.available())  {
      char current_byte = myESP8266.read(); // read the next character.
      Serial.print(current_byte);

      if (current_byte == 0x0d) { // CR
          
      } else if (current_byte == 0x0a) { // LF
//        Serial.print("len=");
//        Serial.println(len);
        if (len == 0) continue;
//        Serial.print("str_buffer=[");
//        Serial.print(str_buffer);
//        Serial.println("]");
        for(int i=0;i<len;i++) {
          if (str_buffer[i] == 0x2c) {
            if (strcmp(&str_buffer[i+1],"CONNECT") != 0) return -1; // No Connect
            str_buffer[i]=0;
            return atoi(str_buffer); // Connect ID
          } // end if
        } // end for

      } else {
        str_buffer[len++]=current_byte;
        if (len == 64) return -1;
        str_buffer[len]=0;
      } // end if
    } // end while
  } // end while

  return -1;
}
void setup(void)
{
  Serial.begin(9600);

  //Make sure ESP8266 is set to 4800
  myESP8266.begin(4800);
  delay(100);

  //Local echo off
  sendCommand("ATE0");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("ATE0 Fail");
  }
  clearBuffer();

  //Disconnect from AP
  sendCommand("AT+CWQAP");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CWQAP Fail");
  }
  clearBuffer();

  //Set Wifi Station mode
  sendCommand("AT+CWMODE=1");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CWMODE fail");
  }
  clearBuffer();

  //Set IP address of Station
  sprintf(cmd, "AT+CIPSTA_CUR=\"%s\"", SERVER);
  sendCommand(cmd);
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CIPSTA_CUR fail");
  }
  clearBuffer();

  //Connect to AP
  sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"", SSID, PASSWORD);
  sendCommand(cmd);
  if (!waitForString("OK", 2, 10000)) {
    errorDisplay("AT+CWJAP Fail");
  }
  clearBuffer();

  //Get local IP address
  sendCommand("AT+CIPSTA?");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CIPSTA? Fail");
  }
  clearBuffer();

  //Enable multi connections
  sendCommand("AT+CIPMUX=1");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CIPMUX Fail");
  }
  clearBuffer();

  //Configure as TCP server
  sprintf(cmd, "AT+CIPSERVER=1,%s", PORT);
  sendCommand(cmd);
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CIPSERVER Fail");
  }
  clearBuffer();


}

void loop(void) {
  char smsg[64];
  char rmsg[64];
  short rlen;
  short sz_smsg;
  int id;

  //Wait CONNECT
  id=waitConnect(10000);
//  Serial.print("id=");
//  Serial.println(id);
  if (id >= 0) {
    //Read data
    rlen = readResponse(id, rmsg, sizeof(rmsg), 5000);
    clearBuffer();
//    Serial.print("rmsg=[");
//    Serial.print(rmsg);
//    Serial.println("]");

    memset (smsg,0,sizeof(smsg));
    for (int i=0; i< rlen; i++) {
      if(isalpha(rmsg[i])) {
        smsg[i] = toupper(rmsg[i]);
      } else {
        smsg[i] = rmsg[i];
      }
    }
    sz_smsg=strlen(smsg);

    //Send AT+CIPSEND=id,nn
    sprintf(cmd,"AT+CIPSEND=%d,%d",id,sz_smsg);
    sendCommand(cmd);
    if (!waitForString(">", 1, 1000)) {
      errorDisplay("AT+CIPSEND Fail");
    } else {
      clearBuffer();
      //Send data
      sendCommand(smsg);
      waitForString("OK", 2, 1000);
      clearBuffer();
    }
  }
}

実行結果は以下の様にライブラリを使った時よりも良好で、取りこぼしは皆無です。
どうやらライブラリがバグっているみたいです。


このように、シリアル(UART)通信ができるマイコンなら、どんなマイコンでもESP8266をWifiモデムとして使って
ネットワーク通信を行うことができます。
Arduino以外のマイコンを入手したら試してみたいと思います。

次回
はUNOからのMQTT通信を紹介します。