#include <Arduino.h>
/*
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <HTTPClient.h>
#endif
*/
// #include <WiFiClientSecureBearSSL.h>
// Replace with your network credentials
const char* ssid = "SzeretlekMiki";
const char* password = "LolkaBolka";
String payload;
boolean apiPayloadOK = false;
// First DB update after 10 minutes... 600000 millis
// Every other repeating 3 hours... 10800000 millis
unsigned int nextSolarApiUpdateMillis = millis() + 600000;
const unsigned int solarApiUpdateDelay = 10800000;
// First RTDB update after 30 sec... 30000 millis
// Every other repeating 2 seconds... 2000 millis
unsigned int nextJkBmsUpdateMillis = millis() + 30000;
const unsigned int jkBmsUpdateDelay = 2000;
String lastHeaderDate = "";
uint8_t frameStart = 0x4E;
uint8_t frameEnd = 0x68;
uint8_t firstCode = 0x80;
uint8_t nextCode = 0x80;
uint8_t currCode = 0x79;
uint16_t currCodeIdx = 30;
uint8_t bytesToRead = 0;
bool firstCodeFound = false;
uint8_t highData = 0;
uint8_t lowData = 0;
uint16_t wordData = 0;
uint8_t currValue = 0;
bool chargeBool = false;
int16_t tempr1 = 0;
int16_t tempr2 = 0;
int16_t tempr3 = 0;
float libCurrent = 0.0f;
float packVoltage = 29.0f;
uint8_t soc = 100;
const uint8_t TestJKReplyFrameCh[] PROGMEM = { 0x4E, 0x57, 0x01, 0x06, 0x00, 0x00,
0x00, 0x00, 0x06, 0x00, 0x01, 0x79, 0x15, 0x01, 0x10, 0x3B,
0x02, 0x10, 0x46, 0x03, 0x10, 0x44, 0x04, 0x10, 0x3D, 0x05,
0x10, 0x3B, 0x06, 0x10, 0x3E, 0x07, 0x10, 0x31,
0x80, 0x00, 0x0C,
0x81, 0x00, 0x10,
0x82, 0x00, 0x12,
0x83, 0x0B, 0x5D,
0x84, 0x83, 0xDB, // Charging... 9.9A
// 0x84, 0x05, 0x88, // Dis-Charging... 14,2 A
0x85, 0x63,
0x86, 0x02,
0x87, 0x00, 0xC1,
0x89, 0x00, 0x00, 0x59, 0xE7,
0x8A, 0x00, 0x07,
0x8B, 0x00, 0x00,
0x8C, 0x00, 0x03,
0x8E, 0x0B, 0x7C,
0x8F, 0x07, 0xB6,
0x90, 0x10, 0x68,
0x91, 0x10, 0x36,
0x92, 0x00, 0x05,
0x93, 0x0B, 0x04,
0x94, 0x0B, 0x22,
0x95, 0x00, 0x05,
0x96, 0x01, 0x2C,
0x97, 0x00, 0x7B,
0x98, 0x01, 0x2C,
0x99, 0x00, 0x58,
0x9A, 0x00, 0x1E,
0x9B, 0x0D, 0xAC,
0x9C, 0x00, 0x1E,
0x9D, 0x01,
0x9E, 0x00, 0x50,
0x9F, 0x00, 0x46,
0xA0, 0x00, 0x64,
0xA1, 0x00, 0x64,
0xA2, 0x00, 0x14,
0xA3, 0x00, 0x37,
0xA4, 0x00, 0x3C,
0xA5, 0xFF, 0xFB,
0xA6, 0x00, 0x01,
0xA7, 0xFF, 0xEC,
0xA8, 0xFF, 0xF6,
0xA9, 0x07,
0xAA, 0x00, 0x00, 0x00, 0x77,
0xAB, 0x01,
0xAC, 0x01,
0xAD, 0x03, 0xA4,
0xAE, 0x01,
0xAF, 0x01,
0xB0, 0x00, 0x0A,
0xB1, 0x14,
0xB2, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, 0x00, 0x00, 0x00,
0xB3, 0x00,
0xB4, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x20, 0x55, 0x73,
0xB5, 0x32, 0x33, 0x30, 0x37,
0xB6, 0x00, 0x0C, 0x22, 0x62,
0xB7, 0x31, 0x31, 0x2E, 0x58, 0x57, 0x5F, 0x53, 0x31, 0x31, 0x2E, 0x32, 0x36, 0x31, 0x5F, 0x5F,
0xB8, 0x00,
0xB9, 0x00, 0x00, 0x00, 0x77,
0xBA, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x64,
0x61, 0x4A, 0x4B, 0x2D, 0x47, 0x61, 0x6D, 0x6D, 0x61, 0x31, 0x00,
0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x4A,
0xD4 };
/*
Full data:4E57010600000000060001791501103B02104603104404103D05103B06103E07103180000C810010820012830B5D8483DB856386028700C189000059E78A00078B00008C00038E0B7C8F07B6901068911036920005930B04940B2295000596012C97007B98012C9900589A001E9B0DAC9C001E9D019E00509F0046A00064A10064A20014A30037A4003CA5FFFBA60001A7FFECA8FFF6A907AA00000077AB01AC01AD03A4AE01AF01B0000AB114B231323334353600000000B300B4496E707574205573B532333037B6000C2262B731312E58575F5331312E3236315F5FB800B900000077BA496E707574205573657264614A4B2D47616D6D6131000000C001000000006800004AD4
cell count:7
avg cell voltage: 4,157 V
min cell: [7] 4,145 V
max cell: [2] 4,166 V
cell diff: 0,021 V
mos temp: 12 C | t1: 16 C | t2: 18 C
pack voltage: 29,09 V
charging: True
RAW current: 337,6 A
ABS current: 9,9 A
AVG current: 10,4 A
capacity: 99 %
warning: False
pack capacity: 119 Ah
available capacity: 117,8 Ah
time left: 0 Hrs 6 Mins
c-rate: 0,09
*/
uint16_t swap(uint16_t aWordToSwapBytes) {
return ((aWordToSwapBytes << 8) | (aWordToSwapBytes >> 8));
}
int16_t getJKTemperature(uint16_t aJKRAWTemperature) {
// uint16_t tTemperature = swap(aJKRAWTemperature);
uint16_t tTemperature = 0 + aJKRAWTemperature;
if (tTemperature <= 100) {
return tTemperature;
}
return 100 - tTemperature;
}
/*
* Charge is positive, discharge is negative
*/
int16_t getCurrent(uint16_t aJKRAWCurrent) {
// uint16_t tCurrent = swap(aJKRAWCurrent);
uint16_t tCurrent = 0 + aJKRAWCurrent;
if (tCurrent == 0 || (tCurrent & 0x8000) == 0x8000) {
// Charge
return (tCurrent & 0x7FFF);
}
// discharge
return tCurrent * -1;
}
String getPowerGuide() {
if(tempr1 < 4 || tempr2 < 4 || tempr3 < 4 ) {
return "DECHIGH";
} else if (soc > 90 && libCurrent > -8.0f ) {
return "INC";
} else if (soc < 90 || libCurrent < -10.0f ) {
return "DEC";
} else if (soc < 70 || libCurrent < -30.0f || packVoltage < 26.6f) {
return "DECHIGH";
} else return "STAY";
}
void setup() {
Serial.begin(115200);
//Serial.setDebugOutput(true);
Serial.println();
Serial.println(" HELLLO WORLD ESP8266... ");
delay(1000);
Serial.println();
int frameSize = sizeof(TestJKReplyFrameCh);
Serial.println(" Frame size: " + frameSize );
for (uint16_t i = 0; i < (frameSize + 1); ++i) {
Serial.print(" ");
currValue = TestJKReplyFrameCh[i];
if(i<100) Serial.print(" ");
if(i<10) Serial.print(" ");
Serial.print(i);
Serial.print(" 0x");
if(currValue<0x10) Serial.print("0");
Serial.print(currValue, HEX);
Serial.print(", ");
Serial.println(currValue);
if (currValue == frameStart && i < 5) {
Serial.println(" -- FRAME START --- ");
chargeBool = false;
tempr1 = 0;
tempr2 = 0;
tempr3 = 0;
libCurrent = 0.0f;
packVoltage = 29.0f;
soc = 100;
firstCodeFound = false;
bytesToRead = 0;
}
if (currValue == frameEnd && nextCode > 0x86) {
Serial.println(" -- FRAME END --- ");
if(chargeBool) {
Serial.print(" Charging at Amps: ");
Serial.print(libCurrent);
} else {
Serial.print(" Dis-Charging at Amps: ");
Serial.println(libCurrent);
}
Serial.print(", Pack Voltage: ");
Serial.print(packVoltage);
Serial.println("V ");
Serial.print(" AVG-Cell Voltage: ");
Serial.print(packVoltage / 7.0f);
Serial.print("V, AVG Temperature: ");
Serial.print((tempr1 + tempr1 + tempr3)/3);
Serial.print(", SOC: ");
Serial.print(soc);
Serial.println(" % ");
String tojSONnode = "{ \"SOC\": " + String(soc) + ", \"Volts\": " + String(packVoltage) + ", \"Amps\": " + String(libCurrent) + ", \"charging\": " + String(chargeBool) + ", \"temps\": [" + tempr1 +", " + tempr2 +", " + tempr3 + "] , \"hint\" : \"" + getPowerGuide() + "\", \"sec\" : " + millis()/1000 + " }";
Serial.println(tojSONnode);
if(chargeBool) {
Serial.print("MSGB [CHA:");
} else {
Serial.print("MSGB [DCH:");
}
Serial.print(libCurrent);
Serial.print("] [AVGC:");
Serial.print(packVoltage / 7.0f);
Serial.print("V] [TMPS:");
Serial.print(tempr1, DEC);
Serial.print(",");
Serial.print(tempr2, DEC);
Serial.print(",");
Serial.print(tempr3, DEC);
Serial.print("] [SOC:");
Serial.print(soc);
Serial.print("%] [");
Serial.print(getPowerGuide());
Serial.println("] MSGE ");
if(chargeBool) {
Serial.print("CHR ");
} else {
Serial.print("DCR ");
}
Serial.print(libCurrent);
Serial.print("A ");
Serial.print(packVoltage / 7.0f);
Serial.println("V ");
Serial.print(soc);
Serial.print("% ");
Serial.print(getPowerGuide());
Serial.print(" ");
Serial.print(tempr1);
Serial.print(",");
Serial.print(tempr2);
Serial.print(",");
Serial.print(tempr3);
Serial.println("C");
break;
}
if (currValue == firstCode) {
firstCodeFound = true;
Serial.println(" -- FIRST CODE --- ");
}
if (firstCodeFound && currValue==nextCode && bytesToRead == 0) {
currCodeIdx = i;
if(currCode == 0x79) {
currCode = 0x80;
bytesToRead = 2;
nextCode = 0x81;
} else if(currCode == 0x80) {
currCode = 0x81;
bytesToRead = 2;
nextCode = 0x82;
// 99 = 99 degree Celsius, 100 = 100, 101 = -1, 140 = -40
Serial.print(" Parsed data for code 0x80: ");
Serial.print(wordData);
Serial.print(" as 16bit and low byte is: ");
Serial.print(lowData);
int16_t parsedTemperature = getJKTemperature(wordData);
Serial.print(" Temperature1 in C: ");
Serial.println(parsedTemperature);
tempr1 = 0 + parsedTemperature;
} else if(currCode == 0x81) {
currCode = 0x82;
bytesToRead = 2;
nextCode = 0x83;
// 99 = 99 degree Celsius, 100 = 100, 101 = -1, 140 = -40
Serial.print(" Parsed data for code 0x81: ");
Serial.print(wordData);
Serial.print(" as 16bit and low byte is: ");
Serial.print(lowData);
int16_t parsedTemperature = getJKTemperature(wordData);
Serial.print(" Temperature2 in C: ");
Serial.println(parsedTemperature);
tempr2 = 0 + parsedTemperature;
}
else if(currCode == 0x82) {
currCode = 0x83;
bytesToRead = 2;
nextCode = 0x84;
// 99 = 99 degree Celsius, 100 = 100, 101 = -1, 140 = -40
Serial.print(" Parsed data for code 0x82: ");
Serial.print(wordData);
Serial.print(" as 16bit and low byte is: ");
Serial.print(lowData);
int16_t parsedTemperature = getJKTemperature(wordData);
Serial.print(" Temperature3 in C: ");
Serial.println(parsedTemperature);
tempr3 = 0 + parsedTemperature;
} else if(currCode == 0x83) {
currCode = 0x84;
bytesToRead = 2;
nextCode = 0x85;
// Battery10Millivolt
Serial.print(" Parsed data for code 0x83: ");
Serial.print(wordData);
Serial.print(" as 16bit and low byte is: ");
Serial.println(lowData);
Serial.print(" Voltage: ");
packVoltage = wordData / 100.0f;
Serial.print(packVoltage);
Serial.println("V ");
delay(5);
Serial.print(" C-AVG: ");
Serial.print(packVoltage / 7.0f);
Serial.println("V ");
} else if(currCode == 0x84) {
currCode = 0x85;
bytesToRead = 1;
nextCode = 0x86;
// Current values start with 200mA at 240 mA real current
// -> 410 -> 620 -> 830mA@920mA real -> 1030@1080mA real
// -> 1.33 => Resolution is 0.21A
// Highest bit: 0=Discharge, 1=Charge -> depends on ProtocolVersionNumber
Serial.print(" Parsed data for code 0x84: ");
Serial.print(wordData);
Serial.print(" as 16bit and low byte is: ");
Serial.println(lowData);
Serial.print(" Lib Current calc: ");
libCurrent = getCurrent(wordData) / 100.0f;
Serial.print(libCurrent);
if (libCurrent > 0) {
chargeBool = true;
Serial.println (" CHARGING ");
} else {
chargeBool = false;
Serial.println(" DIS-CHARGING ");
}
/*
if(highData & 128) {
chargeBool = true;
highData = highData - 128;
uint16_t newWord = lowData | (highData & 0x00FF);
float ampsCalculated = newWord / 100.0f + 0.2f;
Serial.print(" RAW Amps calculated ");
Serial.print(newWord);
Serial.print(" Charging at Amps: ");
Serial.println(ampsCalculated);
} else {
float ampsCalculated = wordData / 100.0f + 0.2f;
Serial.print(" DisCharging at Amps: ");
Serial.println(ampsCalculated);
}*/
} else if(currCode == 0x85) {
currCode = 0x86;
bytesToRead = 1;
nextCode = 0x87;
// Current values start with 200mA at 240 mA real current
// -> 410 -> 620 -> 830mA@920mA real -> 1030@1080mA real
// -> 1.33 => Resolution is 0.21A
// Highest bit: 0=Discharge, 1=Charge -> depends on ProtocolVersionNumber
Serial.print(" Parsed data for code 0x85: ");
Serial.println(lowData);
Serial.print(" SOC: ");
Serial.print(lowData);
Serial.println(" % ");
soc = 0 + lowData;
}
} else
if (firstCodeFound && bytesToRead != 0) {
currCodeIdx = i;
if(currCode == 0x80) {
if(bytesToRead == 2) {
highData = currValue;
wordData = currValue << 8;
lowData = 0;
bytesToRead--;
} else if (bytesToRead == 1) {
lowData = currValue;
wordData = wordData | (currValue & 0x00FF);
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x80 remaining 0 to read Should not happen... ");
}
} else if(currCode == 0x81) {
if(bytesToRead == 2) {
highData = currValue;
wordData = currValue << 8;
lowData = 0;
bytesToRead--;
} else if (bytesToRead == 1) {
lowData = currValue;
wordData = wordData | (currValue & 0x00FF);
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x81 remaining 0 to read Should not happen... ");
}
} else if(currCode == 0x82) {
if(bytesToRead == 2) {
highData = currValue;
wordData = currValue << 8;
lowData = 0;
bytesToRead--;
} else if (bytesToRead == 1) {
lowData = currValue;
wordData = wordData | (currValue & 0x00FF);
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x82 remaining 0 to read Should not happen... ");
}
} else if(currCode == 0x83) {
if(bytesToRead == 2) {
highData = currValue;
wordData = currValue << 8;
lowData = 0;
bytesToRead--;
} else if (bytesToRead == 1) {
lowData = currValue;
wordData = wordData | (currValue & 0x00FF);
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x83 remaining 0 to read Should not happen... ");
}
} else if(currCode == 0x84) {
if(bytesToRead == 2) {
highData = currValue;
wordData = currValue << 8;
lowData = 0;
bytesToRead--;
} else if (bytesToRead == 1) {
lowData = currValue;
wordData = wordData | (currValue & 0x00FF);
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x84 remaining 0 to read Should not happen... ");
}
} else if(currCode == 0x85) {
if (bytesToRead == 1) {
lowData = currValue;
bytesToRead--;
} else if (bytesToRead == 0) {
Serial.println("0x84 remaining 0 to read Should not happen... ");
}
}
}
delay(5);
}
/*
//Connect to Wi-Fi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
*/
}
void loop() {
/*
if (millis() > nextSolarApiUpdateMillis) {
getApiPayload();
delay(50);
if(apiPayloadOK) {
patchRTDBPayload();
delay(30);
Serial.println("RTDB payload: ");
Serial.println(payload);
}
apiPayloadOK = false;
nextSolarApiUpdateMillis = millis() + solarApiUpdateDelay;
}
if ( (millis() + solarApiUpdateDelay + 1000) < nextSolarApiUpdateMillis ) {
nextSolarApiUpdateMillis = millis() + solarApiUpdateDelay + 1000;
} // To workaround millis rollover
*/
if (millis() > nextJkBmsUpdateMillis) {
getJkFame();
updateJkDisplay();
patchJkRTDBPayload();
patchJkRTDbHistoryPayload();
sendToChagerLora();
nextJkBmsUpdateMillis = millis() + jkBmsUpdateDelay;
}
if ( (millis() + jkBmsUpdateDelay + 1000) < nextJkBmsUpdateMillis ) {
nextJkBmsUpdateMillis = millis() + jkBmsUpdateDelay + 1000;
} // To workaround millis rollover
}
void getJkFame() {
}
void updateJkDisplay() {
}
void patchJkRTDBPayload() {
}
void patchJkRTDbHistoryPayload() {
// use lastHeaderDate as key on patch
}
void sendToChagerLora() {
}
/*
void patchRTDBPayload() {
// wait for WiFi connection
if ((WiFi.status() == WL_CONNECTED)) {
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
// Ignore SSL certificate validation
client->setInsecure();
//create an HTTPClient instance
HTTPClient https;
//Initializing an HTTPS communication using the secure client
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, "https://solar-charge-b514d-default-rtdb.europe-west1.firebasedatabase.app/solarvalues/jkbms/testvalues/kutya.json")) { // HTTPS
Serial.print("[HTTPS] PATCH...\n");
https.addHeader("Content-Type", "application/json");
// start connection and send HTTP header
int httpCode = https.PATCH("" + payload);
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTPS] PATCH... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
payload = "" + https.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
lastHeaderDate = https.header("date");
Serial.print(" Header date: ");
Serial.println(lastHeaderDate);
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
}
Serial.println();
Serial.println(" END OF payload GET ");
}
void getApiPayload() {
// wait for WiFi connection
if ((WiFi.status() == WL_CONNECTED)) {
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
// Ignore SSL certificate validation
client->setInsecure();
//create an HTTPClient instance
HTTPClient https;
//Initializing an HTTPS communication using the secure client
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, "https://thingproxy.freeboard.io/fetch/https%3A%2F%2Fapi.forecast.solar%2Festimate%2F46.245%2F20.1288%2F70%2F40%2F3")) { // HTTPS
Serial.print("[HTTPS] GET...\n");
https.addHeader("Content-Type", "application/json");
// 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) {
payload = "" + https.getString();
Serial.println(payload);
if(payload.indexOf("\"message\":{\"code\":0,\"type\":\"success\"") > 0) {
Serial.println(" SUCCESS!!! ");
apiPayloadOK = true;
}
}
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
lastHeaderDate = https.header("date");
Serial.print(" Header date: ");
Serial.println(lastHeaderDate);
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
}
Serial.println();
Serial.println(" END OF payload GET ");
}
*/