简体   繁体   中英

Can't connect ESP32 to websocket server deployed to Heroku

This is my ESP32 code,

#include <WiFi.h>
#include <WebSocketClient.h>
#include <ArduinoJson.h> 
const char* ssid     = "###";
const char* password = "###";
 
char path[] = "/";
char host[] = "https://hidden-thicket-03510.herokuapp.com";
 
WebSocketClient webSocketClient;
WiFiClient client;

int timer=0;
void connnect(){
   if (client.connect(host,443)) {
    Serial.println("Connected");
  } else {
    Serial.println("Connection failed.");
  }
 
  webSocketClient.path = path;
  webSocketClient.host = host;
  if (webSocketClient.handshake(client)) {
    Serial.println("Handshake successful");
  } else {
    Serial.println("Handshake failed.");
  }
}
void setup() {
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
 
  delay(5000);
 
 connnect();

 if (client.connected()) {
   webSocketClient.sendData("Info to be echoed back");
 }
 
}
 
void loop() {
  String data;
  
    if (client.connected()) {

    webSocketClient.getData(data);
    Serial.println(data);
    int data_len = data.length() + 1; 
    char char_array[data_len];
    data.toCharArray(char_array, data_len);
    StaticJsonDocument<1200> doc;

    DeserializationError err=deserializeJson(doc,char_array);

    const char* a=doc["message"];
    
    if (data_len > 1) {
      Serial.print("Received data: ");
      Serial.println(a);
    }
    
  } else {
    Serial.println("Client disconnected.");
    connnect();
  }
 
  delay(3000);
 
}

This is the server code running on Heroku

var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
let port =process.env.PORT || 5000;
server.listen(port, function() {
    console.log((new Date()) + ' Server is listening on port 5000');
});

wsServer = new WebSocketServer({
    httpServer: server
});
const clients={}
wsServer.on('request', request=> { 
    var connection = request.accept(null, request.origin);
    console.log((new Date()) + ' Connection accepted.');
    
    const clientId=guid();
    clients[clientId]={
        "connection":connection
    };

    // connection.sendUTF("JSON.stringify(payload)")
    connection.on('open',()=>{console.log("opened")})

    connection.on('message', message => {
        console.log(message);
        var a=JSON.stringify({'message':'sdaed'})
        connection.send(a)
        
    });

    connection.on('close', (reasonCode, description) =>{
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });

    const payload={
        "method":"connect",
        "clientId":clientId
    }
    
    
});



const guid=()=> {
    const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);     
    return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
  }


When I run the server locally on my PC, ESP32 connects to the server. But when I deploy my server to Heroku and try to connect the ESP32 handshake fail occurs. This is the output on the serial monitor I get

Connected
Waiting...
Waiting...
Waiting...
Handshake failed.
Client disconnected.
Connected
Waiting...
Waiting...
Waiting...
Handshake failed.
Client disconnected.
Connected
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
Handshake failed.

There is no problem with the deployment to server because when I use a nodejs websocket client(example from the repo) it successfully connects to the deployed Heroku server. I have tried changing port number to 80 and using 'http://hidden-thicket-03510.herokuapp.com' on the Arduino script, but it still didn't work.

Why is my ESP32 able to connect to the locally running server, but not able to connect when deployed to the server

PS - the above server is still working if you want to test it out.

Any help will be appreciated!!

That's because you're connecting to a TLS endpoint but the WiFiClient class (which you use to create the underlying connection) does not implement TLS. The WebSocketClient class expects to receive a working data pipe, but instead it gets a TCP connection where TLS must be set up.

Espressif has provided a TCP+TLS implementation WiFiClientSecure so use this instead of WiFiClient.

Note that unlike garden variety desktop OS-s and browsers, the ESP does not include any root certificates by default. Hence it cannot verify the remote server and will refuse to connect. You have to manually add the server's certificate to your ESP32 project - either the CA, intermediate, or leaf (CA is probably the best choice). Documentation on how to do that is in the project's README.

To get the certificates associated with your server, I use openssl :

openssl s_client -showcerts -connect hidden-thicket-03510.herokuapp.com:443

I see that your CA cert is probably the one named "DigiCert High Assurance EV Root CA". Copy the last PEM block (the base64 starting with -----BEGIN CERTIFICATE----- and ending with -----END CERTIFICATE----- , inclusive) in the output, paste it to file "ca.pem" and add this to your ESP project per instructions.

To verify that this file is the CA certificate, run

openssl x509 -in ca.pem -text -noout

Output should include Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM