SOLVED: HTTP SSL Post via WifiNINA, Uno Wifi R2: Open Tesla Model 3 Charge Port

  • Connect Arduino to Computer
  • close serial monitor if open.
  • Upload "Firmware Updater" sketch to Arduino (Examples->WifiNINA->tools->Firmware Uploader)
  • From IDE menu, Tools-> Wifi 101/ WifiNINA Firmware Updater
  • In Updater, click "Add domain", then copy and paste in the server which you want to connect to, click OK
  • Previous step tends to make wifi module not work, to fix: Click Update Firmware (takes a while).
  • In your code, use WifiSSLClient to instantiate your wifi client, OR use client.connectSSL(server,port).
  • In your code, DO NOT PUT "https://" in your server name!!!! just "domain.com" or whatever the case may be.
    [/UPDATE]

Hello,

I am looking for a little help in getting my POST request to work properly on Arduino (Uno Wifi Rev 2). Thank you in advance for any and all help!
When this post request is successful, it should open up the charge port door on my car.
This post request works properly when sent via the Postman application, but not from Arduino.
I am expecting the charge port to open, and to receive an HTTP 200 response with body:

{"response":{"reason":"","result":true}}

The sample GET request works properly to search google.com from the Arduino.
I am open to other implementations (I saw an example of how to send an RF signal instead HERE), but would prefer to just use the Uno Wifi Rev 2 since I already have it on hand.

Here is the raw post request from the Postman event logs (vehicle ID and bearer token are secrets I've obscured):

POST /api/1/vehicles/{id}/command/charge_port_door_open HTTP/1.1
User-Agent: Autocharger
Authorization: Bearer {token}
Postman-Token: 9059933b-9181-478a-a5e5-6e3dcdcacf19
Host: owner-api.teslamotors.com

and response

HTTP/1.1 200 OK
Server: nginx
Date: Sat, 20 Jun 2020 05:19:56 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 40
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-TXID: d289e5243d2a3fba2c374824074234d1
ETag: W/"f67eec105dd6522783a1f1bacc52723a"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: d289e5243d2a3fba2c374824074234d1
X-Runtime: 0.247008
{"response":{"reason":"","result":true}}

Here is my Arduino code, minimally adapted from the WifiNINA web client sample. Content type is commented out because I am not sending a body.

#include <SPI.h>
#include <WiFiNINA.h>

#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
char bearer[] = BEARER; // account auth token
char id[] = VEHICLE_ID; // vehicle identification for http requests
int keyIndex = 0;            // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char server[] = "https://owner-api.teslamotors.com";    // name address for Google (using DNS)
char host[] = "owner-api.teslamotors.com";

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
WiFiClient client;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  if (client.connect(server, 80)) {
    // Make a HTTP request:
    client.print("POST /api/1/vehicles/");
    client.print(id);
    client.println("/command/charge_port_door_open HTTP/1.1");
//    client.println("Content-type: application/json"); not sending body
    client.println("User-Agent: Autocharger");
    client.print("Authorization: Bearer ");
    client.println(bearer);
    client.print("Host: ");
    client.println(host);
    client.println("Connection: close"); //optionally: keep-alive
    client.println();
  }

  
}

void loop() {
  // if there are incoming bytes available
  // from the server, read them and print them:
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();

    // do nothing forevermore:
    while (true);
  }
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

Here is what my Serial Monitor outputs:

Attempting to connect to SSID: Jesus Saves
Connected to wifi
SSID: Jesus Saves
IP Address: 192.168.1.247
signal strength (RSSI):-55 dBm

Starting connection to server...

disconnecting from server.

So I found out that I needed to use the wifi SSL client, tried that with no success, then found out I needed to download the SSL cert to the board using the firmware uploader.

After a few unsuccessful attempts with multiple sketches open, I finally got a successful message. However, now I only receive the following error when running any sketch with wifi:

Communication with WiFi module failed!

Any suggestions?

Following this post, I reinstalled firmware and reset all my hardware, which solved the previous wifi module error, but now the problem I'm getting is that I can't connect to the server (using SSL). Google still works, but the owner-api.teslamotors.com server I can't connect to. However, using normal web client, it still connects, but returns blank.

When I tried to reinstall the SSL cert, it corrupts the WiFi module with the same error as before.

Any advice on where to go from here?

Also, should I request moving to a different Topic? I see plenty of questions with quick answers; is this not the right place for my question?

After multiple cert upload and firmware flash cycles, I think my uno wifi rev 2 has the cert required (confirmation notice received), but I don't know if reflashing firmware to actually get Wifi Module operational erases it.

I saw a similar problem to mine at this github issue, where the resolution was to simply upload the SSL certificate, but that doesn't seem to have helped me, so I'm not sure where to go at all...

This section of the board seems to get less scrutiny than others. Best place to be (for traffic) is probably Project guidance. Most relevant is networking... You can ask the moderator to move your post.

That said, there are people that look in here regularly and it would appear that your problem is obscure enough that not many of us can help. I've never used https on an Arduino so these questions may not be helpful:

Are you at a place where you can get a secure connection going to other https sites?

Have you considered looking at the library and see if you can add the Tesla certs there?

Thanks, Wild Bill. Even getting a single reply helps me feel like I'm not alone, physically because of COVID and electronically, too!

I didn't see your post until late yesterday now, and your second to last comment is where I'd spent most of the day: just trying to get an SSL connection to any HTTPS site.

I finally found out that unlike the Postman request, the Arduino library SHOULD NOT include "HTTPS://" in the server name.

What I found really odd is that even specifying the IP address, I was not having any luck. So for a long time I thought there was something wrong with the code, and dug far into the libraries, trying to debug with printing variables everywhere.

But no, it was just removing the HTTPS! Rather non-intuitive to me, but glad to have it working!

As far as adding the tesla certs directly, I'm not nearly knowledgeable enough to even start figuring out where to put that in the wifi (or any other) library hardcoded as opposed to on the WifiNINA chip.

Working code: (needs bearer token, SSID, password, etc. in arduino_secrets.h tab, as in wifi client example)

/*
  Web client

  This sketch connects to a website (http://www.google.com)
  using the WiFi module.

  This example is written for a network using WPA encryption. For
  WEP or WPA, change the Wifi.begin() call accordingly.

  This example is written for a network using WPA encryption. For
  WEP or WPA, change the Wifi.begin() call accordingly.

  Circuit:
   Board with NINA module (Arduino MKR WiFi 1010, MKR VIDOR 4000 and UNO WiFi Rev.2)

  created 13 July 2010
  by dlf (Metodo2 srl)
  modified 31 May 2012
  by Tom Igoe
*/


#include <SPI.h>
#include <WiFiNINA.h>

#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
char bearer[] = BEARER; // account auth token
char id[] = VEHICLE_ID; // vehicle identification for http requests
char tr1[] = TOKEN_REQ_1;
char tr2[] = TOKEN_REQ_2;
char tr3[] = TOKEN_REQ_3;
char tr4[] = TOKEN_REQ_4;
char tr5[] = TOKEN_REQ_5;
int keyIndex = 0;            // your network key Index number (needed only for WEP)
int port = 443;

int status = WL_IDLE_STATUS;
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(205.234.31.120);  // numeric IP for Tesla (from Postman and using ping in Linux)
char server[] = "owner-api.teslamotors.com";    // name address for Tesla (DNS)

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
WiFiSSLClient client;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();


    openPort();
//  getVehicles();
}

void openPort() { //also disengages latch

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  if (client.connectSSL(server, port)) {
    Serial.println("connected to server");
    // Make a HTTP request:
    client.print("POST /api/1/vehicles/");
    client.print(id);
    client.println("/command/charge_port_door_open HTTP/1.1");
    //    client.println("Content-type: application/json"); not sending body
    client.println("User-Agent: Autocharger");
    client.print("Authorization: Bearer "); client.println(bearer);
    client.print("Host: "); client.println(server);
    client.println("Connection: keep-alive"); //optionally: keep-alive
    client.println();
    Serial.println("finished POST request");
  } else {
    Serial.println("didn't connect");
  }

}



void getVehicles () {
  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  if (client.connectSSL(server, port)) {
    Serial.println("connected to server");
    // Make a HTTP request:
    client.print("GET /api/1/vehicles"); client.println(" HTTP/1.1");
    client.println("User-Agent: Autocharger");
    client.print("Authorization: Bearer ");
    client.println(bearer);
    client.print("Host: "); client.println(server);
    client.println("Connection: close"); // close or keep alive
    client.println();


  } else {
    Serial.println("connection failed");
  }
}

void loop() {


  // if there are incoming bytes available
  // from the server, read them and print them:
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
    //    Serial.print("read something");
  }

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();

    // do nothing forevermore:
    while (true);
  }
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

Glad you got it working. One little change and the dam burst apparently!