Skip to content

Commit

Permalink
Feature Custom Button (#137)
Browse files Browse the repository at this point in the history
* add Custom Button, add colorcustom, also use custombutton color when off, api response custom
  • Loading branch information
daninden authored Oct 19, 2024
1 parent 078cb8b commit 93031fb
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 15 deletions.
2 changes: 1 addition & 1 deletion SmartEVSE-3/include/evse.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ const struct {
{"MIN", "MIN Charge Current the EV will accept (per phase)", MIN_CURRENT, 16, MIN_CURRENT},
{"MAX", "MAX Charge Current for this EVSE (per phase)", 6, 80, MAX_CURRENT},
{"PWR SHARE", "Share Power between multiple SmartEVSEs (2-8)", 0, NR_EVSES, LOADBL},
{"SWITCH", "Switch function control on pin SW", 0, 5, SWITCH},
{"SWITCH", "Switch function control on pin SW", 0, 7, SWITCH},
{"RCMON", "Residual Current Monitor on pin RCM", 0, 1, RC_MON},
{"RFID", "RFID reader, learn/remove cards", 0, 5 + (ENABLE_OCPP ? 1 : 0), RFID_READER},
{"EV METER","Type of EV electric meter", 0, EM_CUSTOM, EV_METER},
Expand Down
118 changes: 105 additions & 13 deletions SmartEVSE-3/src/evse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ uint16_t GridRelayMaxSumMains = GRID_RELAY_MAX_SUMMAINS; // M
// When the relay opens its contacts, power will be reduced to 4.2kW
// The relay is only allowed on the Master
bool GridRelayOpen = false; // The read status of the relay
bool CustomButton = false; // The status of the custom button
uint16_t MaxCurrent = MAX_CURRENT; // Max Charge current (A)
uint16_t MinCurrent = MIN_CURRENT; // Minimal current the EV is happy with (A)
uint8_t Mode = MODE; // EVSE mode (0:Normal / 1:Smart / 2:Solar)
Expand All @@ -135,7 +136,8 @@ uint16_t MaxCircuit = MAX_CIRCUIT; // M
uint8_t Config = CONFIG; // Configuration (0:Socket / 1:Fixed Cable)
uint8_t LoadBl = LOADBL; // Load Balance Setting (0:Disable / 1:Master / 2-8:Node)
uint8_t Switch = SWITCH; // External Switch (0:Disable / 1:Access B / 2:Access S /
// 3:Smart-Solar B / 4:Smart-Solar S / 5: Grid Relay)
// 3:Smart-Solar B / 4:Smart-Solar S / 5: Grid Relay
// 6:Custom B / 7:Custom S)
// B=momentary push <B>utton, S=toggle <S>witch
uint8_t RCmon = RC_MON; // Residual Current Monitor (0:Disable / 1:Enable)
uint8_t AutoUpdate = AUTOUPDATE; // Automatic Firmware Update (0:Disable / 1:Enable)
Expand Down Expand Up @@ -261,6 +263,7 @@ uint8_t ColorOff[3] = {0, 0, 0}; // off
uint8_t ColorNormal[3] = {0, 255, 0}; // Green
uint8_t ColorSmart[3] = {0, 255, 0}; // Green
uint8_t ColorSolar[3] = {255, 170, 0}; // Orange
uint8_t ColorCustom[3] = {0, 0, 255}; // Blue

//#define FW_UPDATE_DELAY 30 //DINGO TODO // time between detection of new version and actual update in seconds
#define FW_UPDATE_DELAY 3600 // time between detection of new version and actual update in seconds
Expand Down Expand Up @@ -415,14 +418,18 @@ void BlinkLed(void * parameter) {
if (LedCount > 230) LedPwm = WAITING_LED_BRIGHTNESS; // LED 10% of time on, full brightness
else LedPwm = 0;

if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
if (CustomButton) { // Blue for Custom, unless configured otherwise
RedPwm = LedPwm * ColorCustom[0] / 255;
GreenPwm = LedPwm * ColorCustom[1] / 255;
BluePwm = LedPwm * ColorCustom[2] / 255;
} else if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
RedPwm = LedPwm * ColorSolar[0] / 255;
GreenPwm = LedPwm * ColorSolar[1] / 255;
BluePwm = LedPwm * ColorSolar[2] / 255;
} else if (Mode == MODE_SMART) { // Green for Smart, unless configured otherwise
RedPwm = LedPwm * ColorNormal[0] / 255;
GreenPwm = LedPwm * ColorNormal[1] / 255;
BluePwm = LedPwm * ColorNormal[2] / 255;
RedPwm = LedPwm * ColorSmart[0] / 255;
GreenPwm = LedPwm * ColorSmart[1] / 255;
BluePwm = LedPwm * ColorSmart[2] / 255;
} else { // Green for Normal, unless configured otherwise
RedPwm = LedPwm * ColorNormal[0] / 255;
GreenPwm = LedPwm * ColorNormal[1] / 255;
Expand Down Expand Up @@ -466,6 +473,10 @@ void BlinkLed(void * parameter) {
GreenPwm = 0;
BluePwm = 0;
#endif //ENABLE_OCPP
} else if (Access_bit == 0 && CustomButton) {
RedPwm = ColorCustom[0];
GreenPwm = ColorCustom[1];
BluePwm = ColorCustom[2];
} else if (Access_bit == 0 || State == STATE_MODEM_DENIED) {
RedPwm = ColorOff[0];
GreenPwm = ColorOff[1];
Expand All @@ -485,14 +496,18 @@ void BlinkLed(void * parameter) {
LedPwm = ease8InOutQuad(triwave8(LedCount)); // pre calculate new LedPwm value
}

if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
if (CustomButton) { // Blue for Custom, unless configured otherwise
RedPwm = LedPwm * ColorCustom[0] / 255;
GreenPwm = LedPwm * ColorCustom[1] / 255;
BluePwm = LedPwm * ColorCustom[2] / 255;
} else if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
RedPwm = LedPwm * ColorSolar[0] / 255;
GreenPwm = LedPwm * ColorSolar[1] / 255;
BluePwm = LedPwm * ColorSolar[2] / 255;
} else if (Mode == MODE_SMART) { // Green for Smart, unless configured otherwise
RedPwm = LedPwm * ColorNormal[0] / 255;
GreenPwm = LedPwm * ColorNormal[1] / 255;
BluePwm = LedPwm * ColorNormal[2] / 255;
RedPwm = LedPwm * ColorSmart[0] / 255;
GreenPwm = LedPwm * ColorSmart[1] / 255;
BluePwm = LedPwm * ColorSmart[2] / 255;
} else { // Green for Normal, unless configured otherwise
RedPwm = LedPwm * ColorNormal[0] / 255;
GreenPwm = LedPwm * ColorNormal[1] / 255;
Expand Down Expand Up @@ -2090,6 +2105,12 @@ void CheckSwitch(bool force = false)
case 5: // Grid relay
GridRelayOpen = false;
break;
case 6: // Custom button B
CustomButton = !CustomButton;
break;
case 7: // Custom button S
CustomButton = true;
break;
default:
if (State == STATE_C) { // Menu option Access is set to Disabled
setState(STATE_C1);
Expand Down Expand Up @@ -2134,6 +2155,11 @@ void CheckSwitch(bool force = false)
case 5: // Grid relay
GridRelayOpen = true;
break;
case 6: // Custom button B
break;
case 7: // Custom button S
CustomButton = false;
break;
default:
break;
}
Expand Down Expand Up @@ -2772,6 +2798,12 @@ void mqtt_receive_callback(const String topic, const String payload) {
OverrideCurrent = 0;
setMode(MODE_SMART);
}
} else if (topic == MQTTprefix + "/Set/CustomButton") {
if (payload == "On") {
CustomButton = true;
} else {
CustomButton = false;
}
} else if (topic == MQTTprefix + "/Set/CurrentOverride") {
uint16_t RequestedCurrent = payload.toInt();
if (RequestedCurrent == 0) {
Expand Down Expand Up @@ -2862,7 +2894,7 @@ void mqtt_receive_callback(const String topic, const String payload) {
}
} else if (topic == MQTTprefix + "/Set/ColorOff") {
int32_t R, G, B;
int n = sscanf(payload.c_str(), "%d:%d:%d", &R, &G, &B);
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);

// R,G,B is between 0..255
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
Expand All @@ -2872,7 +2904,7 @@ void mqtt_receive_callback(const String topic, const String payload) {
}
} else if (topic == MQTTprefix + "/Set/ColorNormal") {
int32_t R, G, B;
int n = sscanf(payload.c_str(), "%d:%d:%d", &R, &G, &B);
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);

// R,G,B is between 0..255
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
Expand All @@ -2882,7 +2914,7 @@ void mqtt_receive_callback(const String topic, const String payload) {
}
} else if (topic == MQTTprefix + "/Set/ColorSmart") {
int32_t R, G, B;
int n = sscanf(payload.c_str(), "%d:%d:%d", &R, &G, &B);
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);

// R,G,B is between 0..255
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
Expand All @@ -2892,14 +2924,24 @@ void mqtt_receive_callback(const String topic, const String payload) {
}
} else if (topic == MQTTprefix + "/Set/ColorSolar") {
int32_t R, G, B;
int n = sscanf(payload.c_str(), "%d:%d:%d", &R, &G, &B);
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);

// R,G,B is between 0..255
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
ColorSolar[0] = R;
ColorSolar[1] = G;
ColorSolar[2] = B;
}
} else if (topic == MQTTprefix + "/Set/ColorCustom") {
int32_t R, G, B;
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);

// R,G,B is between 0..255
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
ColorCustom[0] = R;
ColorCustom[1] = G;
ColorCustom[2] = B;
}
}

// Make sure MQTT updates directly to prevent debounces
Expand Down Expand Up @@ -3041,6 +3083,22 @@ void SetupMQTTClient() {
announce("State", "sensor");
announce("RFID", "sensor");
announce("RFIDLastRead", "sensor");

optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorOff")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorOff"));
announce("LED Color Off", "text");
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorNormal")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorNormal"));
announce("LED Color Normal", "text");
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorSmart")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorSmart"));
announce("LED Color Smart", "text");
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorSolar")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorSolar"));
announce("LED Color Solar", "text");
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorCustom")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorCustom"));
announce("LED Color Custom", "text");

optional_payload = jsna("state_topic", String(MQTTprefix + "/CustomButton")) + jsna("command_topic", String(MQTTprefix + "/Set/CustomButton"));
optional_payload += String(R"(, "options" : ["On", "Off"])");
announce("Custom Button", "select");

#if ENABLE_OCPP
announce("OCPP", "sensor");
announce("OCPPConnection", "sensor");
Expand Down Expand Up @@ -3097,6 +3155,7 @@ void mqttPublishData() {
MQTTclient.publish(MQTTprefix + "/ESPTemp", TempEVSE, false, 0);
MQTTclient.publish(MQTTprefix + "/Mode", Access_bit == 0 ? "Off" : Mode > 3 ? "N/A" : StrMode[Mode], true, 0);
MQTTclient.publish(MQTTprefix + "/MaxCurrent", MaxCurrent * 10, true, 0);
MQTTclient.publish(MQTTprefix + "/CustomButton", CustomButton ? "On" : "Off", false, 0);
MQTTclient.publish(MQTTprefix + "/ChargeCurrent", Balanced[0], true, 0);
MQTTclient.publish(MQTTprefix + "/ChargeCurrentOverride", OverrideCurrent, true, 0);
MQTTclient.publish(MQTTprefix + "/Access", StrAccessBit[Access_bit], true, 0);
Expand Down Expand Up @@ -3140,6 +3199,7 @@ void mqttPublishData() {
MQTTclient.publish(MQTTprefix + "/LEDColorNormal", String(ColorNormal[0])+","+String(ColorNormal[1])+","+String(ColorNormal[2]), true, 0);
MQTTclient.publish(MQTTprefix + "/LEDColorSmart", String(ColorSmart[0])+","+String(ColorSmart[1])+","+String(ColorSmart[2]), true, 0);
MQTTclient.publish(MQTTprefix + "/LEDColorSolar", String(ColorSolar[0])+","+String(ColorSolar[1])+","+String(ColorSolar[2]), true, 0);
MQTTclient.publish(MQTTprefix + "/LEDColorCustom", String(ColorCustom[0])+","+String(ColorCustom[1])+","+String(ColorCustom[2]), true, 0);
}
#endif

Expand Down Expand Up @@ -4782,6 +4842,7 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
doc["evse"]["mode"] = Mode;
doc["evse"]["loadbl"] = LoadBl;
doc["evse"]["pwm"] = CurrentPWM;
doc["evse"]["custombutton"] = CustomButton;
doc["evse"]["solar_stop_timer"] = SolarStopTimer;
doc["evse"]["state"] = evstate;
doc["evse"]["state_id"] = State;
Expand Down Expand Up @@ -4905,6 +4966,9 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
doc["color"]["solar"]["R"] = ColorSolar[0];
doc["color"]["solar"]["G"] = ColorSolar[1];
doc["color"]["solar"]["B"] = ColorSolar[2];
doc["color"]["custom"]["R"] = ColorCustom[0];
doc["color"]["custom"]["G"] = ColorCustom[1];
doc["color"]["custom"]["B"] = ColorCustom[2];

String json;
serializeJson(doc, json);
Expand Down Expand Up @@ -4956,6 +5020,11 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
doc["disable_override_current"] = "OK";
}

if(request->hasParam("custombutton")) {
CustomButton = request->getParam("custombutton")->value().toInt() > 0;
doc["custombutton"] = CustomButton;
}

if(request->hasParam("mode")) {
String mode = request->getParam("mode")->value();

Expand Down Expand Up @@ -5325,6 +5394,29 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
serializeJson(doc, json);
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\r\n", json.c_str()); // Yes. Respond JSON

} else if (mg_http_match_uri(hm, "/color_custom") && !memcmp("POST", hm->method.buf, hm->method.len)) {
DynamicJsonDocument doc(200);

if (request->hasParam("R") && request->hasParam("G") && request->hasParam("B")) {
int32_t R = request->getParam("R")->value().toInt();
int32_t G = request->getParam("G")->value().toInt();
int32_t B = request->getParam("B")->value().toInt();

// R,G,B is between 0..255
if ((R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
ColorCustom[0] = R;
ColorCustom[1] = G;
ColorCustom[2] = B;
doc["color"]["custom"]["R"] = ColorCustom[0];
doc["color"]["custom"]["G"] = ColorCustom[1];
doc["color"]["custom"]["B"] = ColorCustom[2];
}
}

String json;
serializeJson(doc, json);
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\r\n", json.c_str()); // Yes. Respond JSON

} else if (mg_http_match_uri(hm, "/currents") && !memcmp("POST", hm->method.buf, hm->method.len)) {
DynamicJsonDocument doc(200);

Expand Down
2 changes: 1 addition & 1 deletion SmartEVSE-3/src/glcd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ const char * getMenuItemOption(uint8_t nav) {
const static char StrMotor[] = "Motor";
const static char StrDisabled[] = "Disabled";
const static char StrLoadBl[9][9] = {"Disabled", "Master", "Node 1", "Node 2", "Node 3", "Node 4", "Node 5", "Node 6", "Node 7"};
const static char StrSwitch[6][11] = {"Disabled", "Access B", "Access S", "Sma-Sol B", "Sma-Sol S", "Grid Relay"};
const static char StrSwitch[8][11] = {"Disabled", "Access B", "Access S", "Sma-Sol B", "Sma-Sol S", "Grid Relay", "Custom B", "Custom S"};
const static char StrGrid[2][10] = {"4Wire", "3Wire"};
const static char StrEnabled[] = "Enabled";
const static char StrExitMenu[] = "MENU";
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ SWITCH Set the function of an external switch (pin SW or connector P2)
<Sma-Sol B> A momentary push Button is used to switch between Smart and Solar modes
<Sma-Sol S> A toggle switch is used to switch between Smart and Solar modes
<Grid Relay> A relay, provided by your energy provider, is connected; when the relay is open, power usage is limited to 4.2kW, as per par 14a of the Energy Industry Act.
<Custom B> A momentary push Button can be used by external integrations
<Custom S> A toggle switch can be used by external integrations
RCMON RCM14-03 Residual Current Monitor is plugged into connector P1
<Disabled> The RCD option is not used
Expand Down

0 comments on commit 93031fb

Please sign in to comment.