2019-03-01 08:45
| tags: ESP8266 Arduino https
Why / when use this method?
Almost everytime I wanted to use any value / information from any public API in my sketch I end up with a fact that almost all APIs are using HTTPS ONLY (not available via plain HTTP - for example https://www.blockchain.com/api/q) and ESP8266HTTPClient requires server certificate fingerprint hardcoded (or provided somehow) in your sketch to be able to connect. This is not an issue until the certificate on the server is renewed - then you need to update the fingerprint in your sketch, compile and upload it to the device to be able to connect again. Because you have no control over server's certficates (of course) you don't know when it is going to be renewed (you can just guess according to its period of validity), so you need to "wait until it breaks and then fix it", over and over again. But wait - most of my projects are getting "public and non-critical information" (like exchange rates, weather information, bitcoin network hashrate, etc.) from thoose APIs, so it makes almost no sense to fiddle with certificates / fingerprints / all that SSL stuff! There is no "personal" or "sensitive" data transferred, anybody can use the API and see the same information! OK somebody could be able to fake the server and my ESP could read wrong value(s) or information (same way as possible if the API is available over plain HTTP) - but in most cases this is not an issue because I can live with a fact that my ESP display could show wrong values if somebody faked the server (which is unprobable, because nobody have a reason to do that / profit from such hack).
When avoid this method?
Really don't use this when:
any kind of personal or sensitive data are involved / transfered - like passwords, keys, personal messages
faked value / information may cause damage - like turning on all heaters in your house during your 2 weeks long summer vacation because somebody faked the API you are using to control them, or bot which sells all your bitcoins by "mistake" because of faked exchange rate received
How?
Very easily, here is the original Arduino BasicHttpsClient example (with fingerprint):
/**
BasicHTTPSClient.ino
Created on: 20.08.2018
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
// Fingerprint for demo URL, expires on June 2, 2019, needs to be updated well before this date
const uint8_t fingerprint [ 20 ] = { 0x5A , 0xCF , 0xFE , 0xF0 , 0xF1 , 0xA6 , 0xF4 , 0x5F , 0xD2 , 0x11 , 0x11 , 0xC6 , 0x1D , 0x2F , 0x0E , 0xBC , 0x39 , 0x8D , 0x50 , 0xE0 };
ESP8266WiFiMulti WiFiMulti ;
void setup () {
Serial . begin ( 115200 );
// Serial.setDebugOutput(true);
Serial . println ();
Serial . println ();
Serial . println ();
for ( uint8_t t = 4 ; t > 0 ; t -- ) {
Serial . printf ( "[SETUP] WAIT %d... \n " , t );
Serial . flush ();
delay ( 1000 );
}
WiFi . mode ( WIFI_STA );
WiFiMulti . addAP ( "SSID" , "PASSWORD" );
}
void loop () {
// wait for WiFi connection
if (( WiFiMulti . run () == WL_CONNECTED )) {
std :: unique_ptr < BearSSL :: WiFiClientSecure > client ( new BearSSL :: WiFiClientSecure );
client -> setFingerprint ( fingerprint );
HTTPClient https ;
Serial . print ( "[HTTPS] begin... \n " );
if ( https . begin ( * client , "https://jigsaw.w3.org/HTTP/connection.html" )) { // HTTPS
Serial . print ( "[HTTPS] GET... \n " );
// start connection and send HTTP header
int httpCode = https . GET ();
// httpCode will be negative on error
if ( httpCode > 0 ) {
// HTTP header has been send and Server response header has been handled
Serial . printf ( "[HTTPS] GET... code: %d \n " , httpCode );
// file found at server
if ( httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY ) {
String payload = https . getString ();
Serial . println ( payload );
}
} else {
Serial . printf ( "[HTTPS] GET... failed, error: %s \n " , https . errorToString ( httpCode ). c_str ());
}
https . end ();
} else {
Serial . printf ( "[HTTPS] Unable to connect \n " );
}
}
Serial . println ( "Wait 10s before next round..." );
delay ( 10000 );
}
Just replace "client->setFingerprint(fingerprint);" with "client->setInsecure();", you can also comment out the fingerprint constant and thats all!
Its a pitty that there is no official example showing this...
Full example:
/**
BasicHTTPSClientNofingerprint.ino
1.03.2019
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
// Fingerprint for demo URL, expires on June 2, 2019, needs to be updated well before this date
// const uint8_t fingerprint[20] = {0x5A, 0xCF, 0xFE, 0xF0, 0xF1, 0xA6, 0xF4, 0x5F, 0xD2, 0x11, 0x11, 0xC6, 0x1D, 0x2F, 0x0E, 0xBC, 0x39, 0x8D, 0x50, 0xE0};
ESP8266WiFiMulti WiFiMulti ;
void setup () {
Serial . begin ( 115200 );
// Serial.setDebugOutput(true);
Serial . println ();
Serial . println ();
Serial . println ();
for ( uint8_t t = 4 ; t > 0 ; t -- ) {
Serial . printf ( "[SETUP] WAIT %d... \n " , t );
Serial . flush ();
delay ( 1000 );
}
WiFi . mode ( WIFI_STA );
WiFiMulti . addAP ( "SSID" , "PASSWORD" );
}
void loop () {
// wait for WiFi connection
if (( WiFiMulti . run () == WL_CONNECTED )) {
std :: unique_ptr < BearSSL :: WiFiClientSecure > client ( new BearSSL :: WiFiClientSecure );
//client->setFingerprint(fingerprint);
client -> setInsecure ();
HTTPClient https ;
Serial . print ( "[HTTPS] begin... \n " );
if ( https . begin ( * client , "https://jigsaw.w3.org/HTTP/connection.html" )) { // HTTPS
Serial . print ( "[HTTPS] GET... \n " );
// start connection and send HTTP header
int httpCode = https . GET ();
// httpCode will be negative on error
if ( httpCode > 0 ) {
// HTTP header has been send and Server response header has been handled
Serial . printf ( "[HTTPS] GET... code: %d \n " , httpCode );
// file found at server
if ( httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY ) {
String payload = https . getString ();
Serial . println ( payload );
}
} else {
Serial . printf ( "[HTTPS] GET... failed, error: %s \n " , https . errorToString ( httpCode ). c_str ());
}
https . end ();
} else {
Serial . printf ( "[HTTPS] Unable to connect \n " );
}
}
Serial . println ( "Wait 10s before next round..." );
delay ( 10000 );
}
Better example - how to get bitcoin exchange rate from blockchain.info API
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
void setup () {
Serial . begin ( 115200 );
Serial . println ( F ( " \n\r * * * ESP BOOT * * *" ));
Serial . println ( F ( "WiFi begin!" ));
WiFi . mode ( WIFI_STA );
WiFi . begin ( "SSID" , "PASSWORD" );
while ( WiFi . status () != WL_CONNECTED ) {
delay ( 500 );
Serial . print ( "." );
}
Serial . println ( F ( " \n\r WiFi connected!" ));
}
void getpr24h () {
std :: unique_ptr < BearSSL :: WiFiClientSecure > client ( new BearSSL :: WiFiClientSecure );
client -> setInsecure ();
HTTPClient https ;
if ( https . begin ( * client , "https://blockchain.info/q/24hrprice" )) { // HTTPS
Serial . println ( "[HTTPS] GET..." );
int httpCode = https . GET ();
// httpCode will be negative on error
if ( httpCode > 0 ) {
// HTTP header has been send and Server response header has been handled
Serial . printf ( "[HTTPS] GET... code: %d \n " , httpCode );
// file found at server?
if ( httpCode == HTTP_CODE_OK ) {
String payload = https . getString ();
Serial . println ( String ( "[HTTPS] Received payload: " ) + payload );
Serial . println ( String ( "1BTC = " ) + payload + "USD" );
}
} else {
Serial . printf ( "[HTTPS] GET... failed, error: %s \n\r " , https . errorToString ( httpCode ). c_str ());
}
https . end ();
} else {
Serial . printf ( "[HTTPS] Unable to connect \n\r " );
}
}
void loop () {
getpr24h ();
Serial . println ( "Wait 20s before next round to not get banned on API server..." );
delay ( 20000 );
}
Tested using:
Arduino 1.8.7
Arduino core for ESP8266 WiFi chip 2.5.0
Ubuntu Mate 16.04
NodeMCU ESP8266 board
Links
Like my blog? Want to buy me coffee or beer?
LTC (litecoin): LeWzkcV2ArRv7Bi7TmrTpwkp6j2CZSLwfY
BTC (bitcoin): 1LzmUcwHK5Ys4zGPRoxYodjzpJsWiG61JY
DOGE (dogecoin): DQmS6EdDXssriDgSBpQMxYicHTiji6kMhx
ETH (ethereum): 0x387ff39c66e71c454ce5844c188c1a87835d2263
USDT (tether@ETH): 0xa69cae5a1da5ff5fb226e4bc87fe5d0f8c45908a
MANA (decentraland): 0xa69cae5a1da5ff5fb226e4bc87fe5d0f8c45908a
XMR (monero): 4JUdGzvrMFDWrUUwY3toJATSeNwjn54LkCnKBPRzDuhzi5vSepHfUckJNxRL2gjkNrSqtCoRUrEDAgRwsQvVCjZbRxBb8sEWJB1SCCuUEa