ESP8266 Client connexion via a webscoket (no library, only AT commands)

Viewed 190

I am working on websocket implementation on client side using AT commands.
I've taken this as a baseline and read this.
Hardware : ESP8266+arduino uno.

Code :

String cmd = "AT+CIPSTART=\"TCP\",\""; cmd += SERVER_IP; cmd += "\",81"; //Start a TCP connection.  to server SERVER_IP on port 81
if (!sendCommand(cmd, "OK", CONTINUE))
  return;
delay(2000);

if (!sendCommand("AT+CIPSTATUS", "OK", CONTINUE))// Check for TCP Connection status.
  return;

cmd = "GET 192.168.43.228:81 HTTP/1.1\r\n";
cmd += "Host: 192.168.43.228:81\r\n";
cmd += "Upgrade: websocket\r\n";
cmd += "Connection: Upgrade\r\n\r\n";
if (!sendCommand("AT+CIPSEND=" + String(cmd.length()), ">", CONTINUE)) {
  sendCommand("AT+CIPCLOSE", "", CONTINUE);
  Serial.println("Connection timeout.");
  return;

}
sendCommand(cmd, "OK", CONTINUE);// Send data to server.
delay(1000);
readResponseData("");

Log on arduino side :

ESP8266 Demo
AT+RST

OK
bBֆ@⸮Sc⸮⸮⸮ȤS⸮⸮⸮ɥ⸮⸮⸮⸮H⸮
[System Ready
, Vendo:⸮ݹ⸮⸮⸮ɹcom]
AT+GMR

0018000902

OK

AT+CWMODE?

+CWMODE:1

OK

AT+CWMODE=1

no change

AT+CWMODE=1

no change

AT+CIPMUX=0

OK

AT+CWJAP="AndroidAP","xxxx"

OK
Connected to WiFi.

AT+CWJAP="AndroidAP","xxxxx"

OK

AT+CWSAP=?

no this fun
AT+CIFSR

192.168.43.29

OK
Module is ready.

AT+CIPSTART="TCP","192.168.43.228",81

OK

Linked
AT+CIPSTATUS

STATUS:3
+CIPSTATUS:0,"TCP","192.168.43.228",81,0

OK

AT+CIPSEND=100

>
 GET 192.168.43.228:81 HTTP/1.1
Host: 192.168.43.228:81
Upgradewrong syntax

ERROR

SEND OK


OK
Unlink

Server side :

WiFi connected
IP address:
192.168.43.228
Server available at ws://192.168.43.228:81
Accepted new web sockets client at index 0

--

...and nothing else from the server which is supposed sending a message as soon as it accepts the websocket.

Please give me some insight :)

1 Answers

I found one solution.

In fact I was not aware about all the details of RFC4566

After a successful handshake, you need to be inline with the frame requirement. It is not just as raw text :)

For a limited use case (i.e. length<125) here is the overview of the solution:

   char str[] = "Hello";
    char buf[125];
    bool toMask = true;
    uint8_t extra = toMask ? 6 : 2;
    buildFrame(str, buf, WS_OPCODE_TEXT, toMask);   
    ESP8266SendData(0, buf, str.length() + extra);

I've tested this to get:

void buildFrame(char *str, uint8_t *ret, uint8_t opcode, bool maskon) {

  uint8_t mask[4];
  int size = strlen(str);

  // Opcode; final fragment
  ret[0] = opcode | WS_FIN;

  ret[1] = (uint8_t) (size) | (maskon == true ? WS_MASK : 0);

  if (maskon) {
    mask[0] = random(0, 256);
    mask[1] = random(0, 256);
    mask[2] = random(0, 256);
    mask[3] = random(0, 256);

    for (int i = 2; i < 4 + 2; ++i) {
      ret[i] = mask[i - 2];
    }

    for (int i = 6; i < size + 6; i++) {
      ret[i] = str[i - 6] ^ mask[(i - 6) % 4];
    }
  }
  else {
    for (int i = 2; i < size + 2; ++i) {
      ret[i] = (uint8_t) str[i - 2];
    }
  }
}

For frame decoding :

uint8_t extractPayload(String buffer, String& payload) {
// here only payloads whose size < 125

  bool FIN = buffer[0] & WS_FIN;
  uint8_t opcode = buffer[0]  & ~WS_FIN;
  bool hasMask = buffer[1] & WS_MASK;
  uint8_t len = buffer[1]  & ~WS_MASK;
  uint8_t mask[4];
  if (hasMask) {
    mask[0] = buffer[2];
    mask[1] = buffer[3];
    mask[2] = buffer[4];
    mask[3] = buffer[5];
    for (int i = 6; i < len + 6; i++) {
      payload += (char) (buffer[i] ^ mask[(i - 6) % 4]);
    }
  }
  else {
    payload = buffer.substring(2);
  }

  Serial.print("FIN = "); Serial.println(FIN, HEX);
  Serial.print("Opcode = "); Serial.println(opcode, HEX);
  Serial.print("Mask = "); Serial.println(hasMask, HEX);
  Serial.print("Payload length = "); Serial.println(len, HEX);
  
  return opcode;
}
Related