Using a new (April 2024) UNO R4 with wifi and the WiFiS3 library WiFiSSLClient I am unable to connect to some some servers over port 443. The included google SSL example works perfectly. Connecting to Apple's SSL weatherkit.apple.com works perfectly. Trying to connect to api.netatmo.com fails only with a no connection after calling sslClient.connect("api.netatmo.com", 443). Using Python on a Macintosh I can connect and talk to api.netatmo.com perfectly with no issues.
There are several other conversations here with the same problem and no resolution.
Things I have done so far:
Made sure the board was using the latest firmware.
Made sure the latest cert was loaded via the IDE.
Connected to howsmyssl.com and the board seems to be using TLS 1.2
Checked the certificates on api.netatmo.com and it's using TLS 1.2
Added extensive debugging statements all over the wifi library to catch errors.
Can't see anything that stands out in Wireshark.
Waved a dead chicken over the board, several times.
Tried uploading a cert downloaded from Chrome for the site api.netatmo.com using .setCACert() before trying to connect. That did not return any error - but I'm not certain I was using the correct cert - still no connection.
Below is a simple test with minimum code to display the problem. Just add your SSID/PASSWORD and uncomment whatever server you want to connect to.
Any ideas?
#include "WiFiS3.h"
#include "WiFiSSLClient.h"
char ssid[] = "SSID";
char pass[] = "PASSWORD";
WiFiSSLClient sslClient;
void setup() {
Serial.begin(115200);
delay(1000); // delay to let the serial port get organized
Serial.println("Starting ---------------------------------------------");
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed");
while (true); // don't continue
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Upgrade firmware");
}
Serial.print("Connecting to: ");
Serial.print(ssid);
Serial.print(" address: ");
WiFi.begin(ssid, pass);
Serial.println(WiFi.localIP()); // WiFi.begin always comes back connected - this seems to make it wait until we can do something
while (WiFi.status() != WL_CONNECTED) {
Serial.print("not connected - ");
Serial.println(WiFi.status());
}
Serial.print("Setup finished:\n");
}
//char *server = "weatherkit.apple.com";
char *server = "api.netatmo.com";
bool getWeather() {
Serial.print("Starting connection to: ");
Serial.println(server);
if (sslClient.connect(server, 443)) {
Serial.print("Connected to: ");
Serial.println(server);
}
else {
Serial.println("Connection failed");
}
return true;
}
void loop() {
getWeather();
while(1);
}
Here is a solution and I will leave this here for future travelers since it does not seem to be addressed anywhere on earth and it's a doozy to figure out. The problem is as of April 2024 the default certificate in the UNO R4 won't work via SSL/TLS with api.netatmo.com (and numerous other servers). What you have to do is get the certificate for the server you are trying to connect to and upload it to the Arduino before you try to make the connect.
A somewhat easy way to do that is using Chrome (other browsers can probably do this too) - go to the server you want to connect to. Press F12. Then press "View Certificate". When that dialog box opens, press the "Details" tab. Select the Root certificate at the top of the list and then press Export and save the certificate somewhere.
Now open that certificate with a text editor. The certificate is in a format that Arduino won't understand. So to fix that - go to this handy link - CONVERT SSL CERTIFICATE TO ARDUINO ESP VARIABLE
Paste in the text contents of the certificate, scroll down and press Run. Then copy the results of that - and paste it in your Arduino code. Now all you have to do is call sslClient.setCACert(root_ca) before you do the connect.
This works great if all you want to do is call one server. But if you want to call more than one server you are going to have to do this certificate dance for each server that the stock UNO R4 certificate does not match. In order to call api.netatmo.com then weatherkit.apple.com I had to load the certificate for each one even though weatherkit.apple.com works fine with the stock certificate. Looking at the WiFiSSLClient.cpp code, all setCACert() does is set a pointer - later on when an attempt to connect is made the connect code looks to see if that pointer is NULL or not. If it's not NULL it tries to load the certificate in the wifi modem. Otherwise it sends a "" to the modem. Calling setCACert() with a NULL causes everything to go off the rails and the board has to be power cycled before it will connect to anything again. Perhaps someone knows the correct way - for now this is working even though it's gobbling up memory.
Here is the modified sample code that calls each server, disconnects, then loops back around -
#include "WiFiS3.h"
#include "WiFiSSLClient.h"
char ssid[] = "SSID";
char pass[] = "PASSWORD";
WiFiSSLClient sslClient;
const char* netatmo_ca =
"-----BEGIN CERTIFICATE-----\n" \
"MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx\n" \
"EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT\n" \
"EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp\n" \
"ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz\n" \
"NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH\n" \
"EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE\n" \
"AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw\n" \
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD\n" \
"E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH\n" \
"/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy\n" \
"DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh\n" \
"GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR\n" \
"tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA\n" \
"AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n" \
"FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX\n" \
"WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu\n" \
"9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr\n" \
"gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo\n" \
"2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO\n" \
"LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI\n" \
"4uJEvlz36hz1\n" \
"-----END CERTIFICATE-----\n";
const char* apple_ca =
"-----BEGIN CERTIFICATE-----\n" \
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\n" \
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\n" \
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\n" \
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\n" \
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\n" \
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\n" \
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\n" \
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\n" \
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n" \
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\n" \
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\n" \
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\n" \
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\n" \
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\n" \
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\n" \
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\n" \
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\n" \
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n" \
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n" \
"-----END CERTIFICATE-----\n";
void setup() {
Serial.begin(115200);
delay(1000); // delay to let the serial port get organized
Serial.println("Starting ---------------------------------------------");
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed");
while (true); // don't continue
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Upgrade firmware");
}
Serial.print("Connecting to: ");
Serial.print(ssid);
Serial.print(" address: ");
WiFi.begin(ssid, pass);
Serial.println(WiFi.localIP()); // WiFi.begin always comes back connected - this seems to make it wait until we can do something
while (WiFi.status() != WL_CONNECTED) {
Serial.print("not connected - ");
Serial.println(WiFi.status());
}
Serial.print("Setup finished:\n");
}
//char *server = "weatherkit.apple.com";
char *server = "api.netatmo.com";
bool getWeather() {
Serial.print("Starting connection to: ");
Serial.println(server);
if (sslClient.connect(server, 443)) {
Serial.print("Connected to: ");
Serial.println(server);
sslClient.stop();
}
else {
Serial.println("Connection failed");
}
return true;
}
void loop() {
static int theCount = 0;
sslClient.setCACert(netatmo_ca);
server = "api.netatmo.com";
getWeather();
sslClient.setCACert(apple_ca);
server = "weatherkit.apple.com";
getWeather();
Serial.print(++theCount);
Serial.println(" ---------------------------------");
delay(5000);
}
This was really helpful! I struggled with this for a couple of days and couldn't understand what was going on. This way I can now proceed with my project. Thanks!!
Glad you got it working. I finally gave up on the Netatmo API. It was too unreliable. I found using Apple's weather server works well enough and when I want to see the rain gauge I just use Netatmo's web portal. And get assaulted by ads for more Netatmo products.
What's peculiar is that the cert that works is supposedly part of the bundle of over 140 certs baked into the (latest) R4 firmware: Go Daddy G2. The line lengths are different, so the breaks are in a different place, but that doesn't matter. You can take the text there; using a raw string makes it an easy copy&paste in either direction, with no other tooling/conversion needed
godaddy.com uses the same root CA, and without setCACert, setting the server there instead connects successfully. You mentioned other servers don't work with R4; do you recall them? Do they all have the same root?
Regarding clearing setCACert, you could instead try separate instances of WiFiSSLClient: ones with a specific cert for whatever reason, and the fallback that uses the bundle.
What is Apple's Weather Server? Is it possible to access it from API? Yeah my next step I need to get the reauthorization to work with the refresh_key. Still I have not managed to get it to work even with Postman. It seems Netatmo need to get their act together with their API's.
This solution is working well. Nevertheless, certificates have dealine, so if i don't want to download sketch each time a certificate is out of date, I think it would be better to Upload SSL root certificates in Arduino IDE. And i don't success to done it on my Arduino R4 wifi as it fails each time i try to do it. My firmware is up to date and i use Arduino IDE 2.3.4. Does everybody have an idea on how to upload SSL rott certificate on that board ? Many thanks for all contributers
Root certs last at least 20 to 25 years, so you wouldn't be doing it that often. The Uploader works for me, but I had to close the Serial Monitor. (Didn't know that until I checked the docs -- something the UI should manage, or at least say in dialog box.)
But the problem seems to be that certain sites fail even when their root is part of the cert bundle that is baked in the firmware (linked in my post #5 above). I reapplied the 0.4.1 firmware and ran
Two of the seven failed, indicated by NOPE. Then I tried those failing sites in the Uploader, which supposedly succeeded, but there is no change. The other sites also continued to work. To determine if there is any real effect, you'd have to find a site whose root is not part of the bundle.
Thanks for your answer.
You say that certificate last 20 to 25 years. Nevertheless on open-meteo.com when i look at the validity duration in the "view certificate" on the root page, it says that the certificate has been emitted "jeudi 28 novembre 2024 à 16:29:56" and the expiration date is "mercredi 26 février 2025 à 17:29:54". So, I believe that past the 26th of February, the certifivate i use will not be longer valid. That's why i ask for a better way than using setCACert(). And even with Serial Monitor closed i can't upload any certificate on my Uno R4 Wifi.
The root Certificate Authority -- it's not always in the name, but often is -- GTS Root R4. That's Google Trust Services LLC, perhaps a distinct legal entity from Google. Both the Subject and Issuer are the same. Their cert is valid between June 21, 2016 and the same day in 2036: twenty years.
The intermediate CA is named WE1; maybe "Western Europe 1"? The Organization is also GTS (but no LLC for whatever reason). Their cert was issued by GTS Root R4, and good for about five years. WE1 is the issuer for the leaf cert.
The (simplified) way this works is if you trust the roots, you can trust the chain to the leaf. That's why the function has that name: you set the cert of the (root) CA, not the (leaf) site, even if that works.
Google is even pushing to have the leaf certs valid for only 90 days, as industry-wide practice to improve security.
(Unfortunately, I have no other suggestions regarding the Uploader.)
Oh: I added "open-meteo.com" to the sketch, and it worked; no setCACert needed. It uses the same root CA as arduino.cc
Apple's weatherkit is really good. You can use it from an Apple device or from a web page. I've been using it since April with hardly any problems other than there was a spell for a few weeks where sometimes the Arduino won't connect to it. No telling what the problem was there. One downside might be that you have to have an Apple Developer Program membership and that's $99 a year. It can pull all kinds of weather information. I've been using the weatherkit REST API with the UNO R4 and it's drop dead simple. The Web Token can have an expiration date thats 100s of years into the future so you don't have to keep messing with that. I gave up on getting the Netatmo to work with the R4 - or anything else. They have some real bozo code going on there. I just needed to read a rain gauge in the backyard, so I just do that with the web page now. For my Arduino weather display I just use mostly Apple's API and the National Weather Service for textual forecasts and watches / warnings. And best of all no certificate nightmare swappa-thons.