Hi everyone,
I'm working on implementing an API request to fetch specific variables from my solar inverter. As a hardware engineer with limited software experience, I've relied on AI assistance to write the code. Please bear with me as this has been quite a challenge.
I've spent hours trying various approaches, including different libraries, but my code still doesn't compile. The latest issue I'm encountering is a compilation error: "MD5 has not been declared." I have gone back to AI and said you haven't declared MD5, so it tries again with "Given the issues with using the MD5
library and declarations, let's approach this with an alternative method that should be reliable and straightforward. We will use a simpler library setup and ensure that we include the correct libraries and methods." using the GitHub - tzikis/ArduinoMD5: The first easily-embeddable MD5 library for Arduino method doesn't work either.
Could someone help me identify where things are going wrong?
Thank you!
G
#include <WiFi.h>
#include <ArduinoHttpClient.h>
#include <ArduinoJson.h>
#include <MD5.h> // Include the ArduinoMD5 library
// WiFi credentials
const char* ssid = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
// SolisCloud API credentials
const char* apiUrl = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
const char* keyID = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
const char* keySecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
// Inverter details
const char* inverterID = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Replace with your inverter ID
const char* inverterSN = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Replace with your inverter SN
WiFiClient wifi;
HttpClient client = HttpClient(wifi, "www.xxxxxxxxxxxxxxxxxxxxxxxxxxx.com", 13333);
void setup() {
Serial.begin(115200);
connectToWiFi();
}
void loop() {
if (WiFi.status() == WL_CONNECTED) {
String batterySOC = getInverterData("batteryCapacitySoc");
String pvPower = getInverterData("pac");
Serial.print("Battery SOC: ");
Serial.println(batterySOC);
Serial.print("PV Power: ");
Serial.println(pvPower);
} else {
Serial.println("WiFi Disconnected");
}
// Wait for 60 seconds before the next request
delay(60000);
}
void connectToWiFi() {
Serial.print("Connecting to WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("\nWiFi connected");
}
String getInverterData(String dataField) {
// Create request URL
String requestUrl = String(apiUrl) + "/v1/api/inverterDetail";
// Create JSON body
String body = "{\"id\":\"" + String(inverterID) + "\",\"sn\":\"" + String(inverterSN) + "\"}";
// Create MD5 of the body
uint8_t* hash = MD5::make_hash(body);
char md5String[33];
MD5::make_digest(md5String, hash, 16);
String contentMD5 = String(md5String);
// Get the current GMT time
time_t now;
struct tm timeinfo;
char timeStr[30];
time(&now);
gmtime_r(&now, &timeinfo);
strftime(timeStr, sizeof(timeStr), "%a, %d %b %Y %H:%M:%S GMT", &timeinfo);
// Create authorization header
String canonicalizedResource = "/v1/api/inverterDetail";
String stringToSign = "POST\n" + contentMD5 + "\napplication/json;charset=UTF-8\n" + String(timeStr) + "\n" + canonicalizedResource;
String sign = hmacSha1(keySecret, stringToSign);
String authorization = "API " + String(keyID) + ":" + base64Encode(sign);
// Configure HTTP headers
client.beginRequest();
client.post("/v1/api/inverterDetail");
client.sendHeader("Content-MD5", contentMD5);
client.sendHeader("Content-Type", "application/json;charset=UTF-8");
client.sendHeader("Date", timeStr);
client.sendHeader("Authorization", authorization);
client.sendHeader("Content-Length", body.length());
client.beginBody();
client.print(body);
client.endRequest();
int statusCode = client.responseStatusCode();
String response = client.responseBody();
if (statusCode != 200) {
Serial.print("Error on sending POST: ");
Serial.println(statusCode);
return "";
}
// Parse JSON response to get the required data field
DynamicJsonDocument doc(1024);
deserializeJson(doc, response);
return doc["data"][dataField].as<String>();
}
String hmacSha1(String key, String data) {
const int BLOCK_SIZE = 64;
uint8_t keyBlock[BLOCK_SIZE];
memset(keyBlock, 0, BLOCK_SIZE);
if (key.length() > BLOCK_SIZE) {
uint8_t* hash = MD5::make_hash(key);
memcpy(keyBlock, hash, 16);
} else {
memcpy(keyBlock, key.c_str(), key.length());
}
uint8_t o_key_pad[BLOCK_SIZE];
uint8_t i_key_pad[BLOCK_SIZE];
for (int i = 0; i < BLOCK_SIZE; i++) {
o_key_pad[i] = keyBlock[i] ^ 0x5c;
i_key_pad[i] = keyBlock[i] ^ 0x36;
}
uint8_t* innerHash = MD5::make_hash(i_key_pad, BLOCK_SIZE, data.c_str(), data.length());
uint8_t* finalHash = MD5::make_hash(o_key_pad, BLOCK_SIZE, innerHash, 16);
char hashOutput[33];
MD5::make_digest(hashOutput, finalHash, 16);
return String(hashOutput);
}
String base64Encode(String data) {
const char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
String encodedString;
int i = 0;
int j = 0;
uint8_t charArray3[3];
uint8_t charArray4[4];
for (int pos = 0; pos < data.length(); pos++) {
charArray3[i++] = data[pos];
if (i == 3) {
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;
for (i = 0; i < 4; i++) {
encodedString += base64Chars[charArray4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
charArray3[j] = '\0';
}
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;
for (j = 0; j < i + 1; j++) {
encodedString += base64Chars[charArray4[j]];
}
while ((i++ < 3)) {
encodedString += '=';
}
}
return encodedString;
}