arduinoHttpClient

I have installed ArduinoHttpClient but when I try to compile I get

This output :slight_smile: C:\Users\peter\OneDrive\Documents\Arduino\httptest\httptest.ino:2:10: fatal error: Arduino_HTTP_Client.h: No such file or directory
#include <Arduino_HTTP_Client.h> // <-- The new, modern HTTP client library
^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
exit status 1

Compilation error: Arduino_HTTP_Client.h: No such file or directory

As you can see it is installed.

Here is my program

#include <WiFiS3.h>
#include <Arduino_HTTP_Client.h> 
#include "secret.h"
#include <SPI.h>
#include <SD.h>

// --- Configuration ---
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
const char serverAddress[] = "192.168.0.9"; 
int port = 443;
// --------------------

// Create the client objects
WiFiClient wifi;
HttpClient client = HttpClient(wifi, serverAddress, port);

int counter = 1;
const int chipSelect = 10;

void setup() {
  Serial.begin(115200);
  while (!Serial);

  Serial.println("--- Starting Secure Test with Arduino_HTTP_Client ---");

  // The new library requires SSL to be enabled explicitly
  client.sslEnable();

  // Connect to Wi-Fi
  WiFi.begin(ssid, pass);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to WiFi network!");

  IPAddress ip = WiFi.localIP();
  Serial.print("Arduino IP Address: ");
  Serial.println(ip);

  if (!SD.begin(chipSelect)) {
    Serial.println("SD card initialization failed!");
    return;
  }
  Serial.println("SD card initialization successful.");
}

void loop() {
  if (counter > 10) {
    Serial.println("Test finished.");
    while (true);
  }

  Serial.print("\n--- Sending Request #");
  Serial.print(counter);
  Serial.println(" ---");

  String postData = "logdata=" + String(counter) + "&device=UNO_R4_SECURE";
  String contentType = "application/x-www-form-urlencoded";
  
  // Make the POST request
  client.post("/arduino_data/save_data.php", contentType, postData);

  // Read the response
  int statusCode = client.responseStatusCode();
  String response = client.responseBody();

  Serial.print("Server Status Code: ");
  Serial.println(statusCode);
  Serial.print("Server Response: ");
  Serial.println(response);

  // Log to SD card
  File logFile = SD.open("datalog.txt", FILE_WRITE);
  if (logFile) {
    logFile.print("Request: ");
    logFile.print(counter);
    logFile.print(", Status: ");
    logFile.println(statusCode);
    logFile.close();
  } else {
    Serial.println("Error writing to SD card.");
  }

  counter++;
  delay(10000);
}

I think I am close and I got it to work with just http to my Synology nas and it sent data and posted back 200. I just need help with that final push to https. I can’t find any other suggested libraries in the r4 documentation. Assistance would be greatly appreciated..

Where did you get the name for the header file? The library uses ArduinoHttpClient.h

I am a novice so don’t understand your question. I have been scratching around the internet.

Try changing this to

#include <ArduinoHttpClient.h>

It fixed one and added another Compilation error: 'class HttpClient' has no member named 'sslEnable'

client.sslEnable();

#include <WiFiS3.h>
#include <WiFiSSLClient.h>
#include <ArduinoHttpClient.h>
#include "secret.h"
#include <SPI.h>
#include <SD.h>

// --- Configuration ---
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
const char serverAddress[] = "192.168.0.9"; 
int port = 443;
// --------------------

// Use SSL-enabled client
WiFiSSLClient wifiSSL;
HttpClient client = HttpClient(wifiSSL, serverAddress, port);

int counter = 1;
const int chipSelect = 10;

void setup() {
  Serial.begin(115200);
  while (!Serial);

  Serial.println("--- Starting Secure Test with Arduino_HTTP_Client ---");

  // Connect to Wi-Fi
  WiFi.begin(ssid, pass);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to WiFi network!");

  IPAddress ip = WiFi.localIP();
  Serial.print("Arduino IP Address: ");
  Serial.println(ip);

  if (!SD.begin(chipSelect)) {
    Serial.println("SD card initialization failed!");
    return;
  }
  Serial.println("SD card initialization successful.");
}

void loop() {
  if (counter > 10) {
    Serial.println("Test finished.");
    while (true);
  }

  Serial.print("\n--- Sending Request #");
  Serial.print(counter);
  Serial.println(" ---");

  String postData = "logdata=" + String(counter) + "&device=UNO_R4_SECURE";
  String contentType = "application/x-www-form-urlencoded";
  
  // Make the POST request
  client.post("/arduino_data/save_data.php", contentType, postData);

  // Read the response
  int statusCode = client.responseStatusCode();
  String response = client.responseBody();

  Serial.print("Server Status Code: ");
  Serial.println(statusCode);
  Serial.print("Server Response: ");
  Serial.println(response);

  // Log to SD card
  File logFile = SD.open("datalog.txt", FILE_WRITE);
  if (logFile) {
    logFile.print("Request: ");
    logFile.print(counter);
    logFile.print(", Status: ");
    logFile.println(statusCode);
    logFile.close();
  } else {
    Serial.println("Error writing to SD card.");
  }

  counter++;
  delay(10000);
}

I am just trying this.

Getting HTTPS to work with a local IP address like this will be difficult. Generally, certificates are issued to match named hosts, e.g. "forum.arduino.cc". Apparently, they will also work with unique public IPs: they are tied to that site specifically. But there are thousands (if not millions) of 192.168.0.9 out there; a Certificate Authority cannot issue a cert for that.

You might be able to create a self-signed certificate for your Synology. Then use that with setCACert so that the R4 can validate and trust it. This is on the WiFiSSLClient, which is the layer "before" the HTTP client. You can either connect securely or not; if yes, then you can perform HTTP manually just to see if it works.

wifiSSL.println("GET / HTTP/1.0");  // request-line
wifiSSL.println(); // blank line after request headers (there were none), and no body
uint8_t buf[80];
while (wifiSSL.connected()) {
  if (wifiSSL.available() > 0) {
    auto len = wifiSSL.read(buf, sizeof(buf));
    Serial.write(buf, len);
  }
}

I don't have R4 yet, but almost all other platforms I had to do with have something to ask the SSL layer to ignore remote site certificate validation (i.e. no CN check, no issuing CA validation, accepting any self-signed certificate), so it isn't used for remote server identity, it's just to encrypt the communication. I wonder if the library has such kind of setting somwhere...

If you need to send data from within to your nas what do people do, do they just use http with no security? It sounds like i am setting myself up to fail.

Yeah -- don't see that with the WiFiSSLClient that's part of WiFiS3 on R4; nothing obvious like setInsecure. This might be the flip side of having dozens of certs baked into the firmware, so you often don't have to think about them at all.

The threat model is different: on the internet, your packets might be going to the other side of the world. Locally, they're going from one room in your home to another (maybe), taking one hop on your WiFi router. Is the intruder already in your local network? Then encrypting "helps". Are they spoofing the endpoint? Then encrypting without validating that endpoint's cert is pointless.

I have an old Synology box, and it appears that they have their own self-signed cert. There are various ways to extract this by connecting with a browser, where you should be confronted with a warning that the self-signed cert is not trusted. The key facts are the Common Name (CN)

        Subject: C = TW, ST = Taiwan, L = Taipei, O = Synology Inc., OU = FTP Team,
                 CN = synology.com, emailAddress = product@synology.com

and in the Extensions, with the Subject Alternative Name.

        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                email:product@synology.com

Compare that to the cert for here, forum.arduino.cc

        Subject: CN = forum.arduino.cc
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:forum.arduino.cc

When you connect here by host name, that name matches both the CN and the SAN listed in the cert -- either would work, and they happen to be the same -- so the user agent can proceed to validate the rest of the certificate chain.

You can create a self-signed cert using an IP as a SAN. Here it is with Go

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"os"
	"time"
)

func main() {
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			Organization: []string{"My Private Subnet"},
			CommonName:   "My Synology",
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(365 * 24 * time.Hour), // Valid for 1 year

		KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		DNSNames:    []string{"localhost"},
		IPAddresses: []net.IP{net.IPv4(192, 168, 0, 9)},
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
	if err != nil {
		panic(err)
	}

	certOut, err := os.Create("cert.pem")
	if err != nil {
		panic(err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.Create("key.pem")
	if err != nil {
		panic(err)
	}
	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
	keyOut.Close()
}

That generates cert.pem and key.pem, which you would import with UI that hopefully does not differ much from these instructions:

(You don't need a CSR, since you created the cert yourself. Follow the import steps at the end.) The setup can then be validated with curl

$ curl -i --cacert cert.pem https://192.168.0.9

As long as you don't get

curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

and do get some kind of HTTP response (the -i switch includes the headers) then it worked. Then you need to put that cert.pem content in your sketch and use it before attempting to connect

const char *MY_SELF_SIGNED_CERT = R"(
-----BEGIN CERTIFICATE-----
MIIDIjCCAgqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMRowGAYDVQQKExFNeSBQ
cml2YXRlIFN1Ym5ldDEUMBIGA1UEAxMLTXkgU3lub2xvZ3kwHhcNMjUwOTE3MjEw
... bunch of lines omitted here, but the sketch will need the whole thing
B99cc05m1hzdXVDtFOc9O9gUZASsPKOrIeP1bIpMjHKxs+tjhaTmblNkVGfqzhWJ
sNPbqsM3pOyF31FankFbT4jHYmeI0Uj+xvkDps6L07ePHh0/lp8=
-----END CERTIFICATE-----
)";
wifiSSL.setCACert(MY_SELF_SIGNED_CERT);

This is very interesting; I will explore more at the weekend but meanwhile, thank you for your contributions. I hadn’t thought about poeple hacking my wifi so this information is extremely usefull.

You're absolutely right. For any reason, they omitted that for R4.
So the only way to have it to work is by loading the server self-signed certificate on CACert, as you told the OP.
Good to know, thanks.