Post topics, source code that relate to the Arduino Platform

User avatar
By itripn
#87545 I have been unable to POST an application/json payload to AWS API Gateway from the NodeMCU-12e. The same post works from Paw and curl. Here is the curl command:

Code: Select allcurl -d '{"timestamp":"000000000","power": 3.45,"ambientTemp":22.5,"soilTemp":20.8125,"lightLevel":871.4843}' -H "Content-Type: application/json" -X POST https://api.mydomain.com/storereadings


I've changed the domain name, but everything else is the same. I get a 200 from this call, both from curl and from Paw.

Now my Arduino code (I use PlatformIO if that matters):

Code: Select all 

String json;
serializeJson(doc, json);

if ((WiFi.status() == WL_CONNECTED)) {

    HTTPClient http;
    WiFiClientSecure client;

    if (!http.begin(client, "https://api.mydomain.com/storereadings")) {
      Serial.println("BEGIN FAILED...");
    }

    http.addHeader("Content-Type", "application/json");
    int code = http.POST(json);
    if (code < 0) {
      Serial.print("ERROR: ");
      Serial.println(code);
    }
    else {
      // Read response
      http.writeToStream(&Serial);
      Serial.flush();
    }

    // Disconnect
    http.end();
  }
  else {
    Serial.println("****** NO WIFI!!");
  }


I consistently get an ERROR: -1 (connection refused) from the POST call. If I call connected() after the begin() call, and it returns false. I do not get False from begin, it appears to think everything is okay.

The same code uses NTPClient to get the time and that works fine, so I feel reasonably confident that my WiFi is working.

One more bit of detail in case it matters: The POST is to an AWS API Gateway endpoint, implemented as a POST (just passes through data to DynamoDB PutItem). I not currently have Auth or any kind of API key requirement turned on for the service, as you can see in the curl command above.

Thanks for any pointers you can give me, been beating my head against this all day.

Cheers,
Ron
User avatar
By itripn
#87551 As so often happens, because I had spent so many hours trying to figure this out, once I posted I kept going, and eventually found the answer. I needed to inform the client that I am okay without a two-way certificate validation of the connection, by inserting this line after creating the WiFiClientSecure instance:

Code: Select all    client.setInsecure();


Then everything started working. I may circle back and work with the fingerprint code to get bidirectional validation happening, but for now this works.

Hope it helps someone else.

Cheers,
Ron
User avatar
By Drakoky
#87556 Try adding this code line after the declaration of wifi client:

Code: Select allclient.setInsecure();

This make you are able to stablish an insecure connection. But remember, is not secure.

Note:
If you are doing an IoT device, I recommend you use MQTT protocol, is such faster than HTTP, lightweight, you can save memory usage. When I test my ESP turning on a light with HTTP I noticed a small delay, but with MQTT was instant. MQTT is designed for communication M2M, ensures high delivery guarantee. Also, with Node-RED in the server side made more easy programing what you do with the data, I use it as GateWay. HTTP have the advantage that is scalable and can be use it in many context, but for IoT device is better be faster.


If you want to have one secure, you need add more code and certs. I don't know if the certs are necessary, I never try a connection without this, because I using a SSL connection, that means is necessary for me, but try without it.

Code: Select all//add the following libraries
#include <time.h>

#include <FS.h>
#include <LittleFS.h>

#include <CertStoreBearSSL.h>
BearSSL::CertStore certStore;

//A function for sync time, secure connections need synched clock
void setClock() {
// Replace the first 0 with your time zone multiply by 3600, is not necessary the code work for me, but if you want your time zone
// something like 4 * 3600
  configTime(0 * 3600, 0, "pool.ntp.org", "time.nist.gov");  // UTC

  Serial.print(F("Waiting for NTP time sync: "));
  time_t now = time(nullptr);
  while (now < 8 * 3600 * 2) {
    yield();
    delay(500);
    Serial.print(F("."));
    now = time(nullptr);
  }

  Serial.println(F(""));
  struct tm timeinfo;
  gmtime_r(&now, &timeinfo);
  Serial.print(F("Current time: "));
  Serial.println(asctime(&timeinfo)); //print time in 0 time zone
  //Serial.println(ctime(&now)); // print time in your time zone
}

//{...}

//add this in your setup
//you need download and upload the certs to the file system before run these lines
//try without it
  LittleFS.begin();

  int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
  Serial.print(F("Number of CA certs read: "));
  Serial.println(numCerts);
  if (numCerts == 0) {
    Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
    return; // Can't connect to anything w/o certs!
  }
//{...}

//add this in your loop
setClock();

//add BearSSL::
BearSSL::WiFiClientSecure client;

//try without certs
bool mfln = client.probeMaxFragmentLength("https://api.mydomain.com/storereadings", 443, 1024);
    Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
    if (mfln) {
      client.setBufferSizes(1024, 1024);
    }
    client.setCertStore(&certStore);

//your http code start here
HTTPClient http;
//{...}


Download the certs from this script (https://github.com/arduino/esp8266/blob ... mozilla.py), is from official repo of ESP8266.

You need to put certs-from-mozilla.py in the same folder of your project and run from the console. If you are on windows, you need download OpenSSL and add to the path. Git come with "openSSL.exe", you only need to add to the path, I recommend use Git because the official OpenSSL's web page doesn't have an installer for windows, so you need third party web, you have risk of download virus or malware. Certs need one more file add to the path, "ar.exe", this come with the ESP8266 library. You can also put these two .exe in the same folder that your project.

the path is a little bit from Arduino IDE and PlatformIO
Arduino is some thing like:
%userprofile%\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\2.5.0-4-b40a506\xtensa-lx106-elf\bin
PlaformIO is:
%userprofile%\.platformio\packages\toolchain-xtensa\xtensa-lx106-elf\bin

Or, you can you put "ar.exe" in the file search, right clic and open File location.

This will generate a folder called "data" with a "certs.ar" inside. them, just upload File System image with LittleFS from Arduino IDE, I recommend, also use Arduino IDE for upload your sketch, I don't know why but PlatformIO doesn't not upload correctly the LittleFS library and can't find the certs. Here the link to my issue to platformio forums:
https://community.platformio.org/t/is-e ... -ide/14327

This, look too much, just add "setInsecure" for the easy way.
Let me know if something if bad with the code and the instruction.