Skip to content

Commit 93031fb

Browse files
authored
Feature Custom Button (#137)
* add Custom Button, add colorcustom, also use custombutton color when off, api response custom
1 parent 078cb8b commit 93031fb

File tree

4 files changed

+109
-15
lines changed

4 files changed

+109
-15
lines changed

SmartEVSE-3/include/evse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ const struct {
522522
{"MIN", "MIN Charge Current the EV will accept (per phase)", MIN_CURRENT, 16, MIN_CURRENT},
523523
{"MAX", "MAX Charge Current for this EVSE (per phase)", 6, 80, MAX_CURRENT},
524524
{"PWR SHARE", "Share Power between multiple SmartEVSEs (2-8)", 0, NR_EVSES, LOADBL},
525-
{"SWITCH", "Switch function control on pin SW", 0, 5, SWITCH},
525+
{"SWITCH", "Switch function control on pin SW", 0, 7, SWITCH},
526526
{"RCMON", "Residual Current Monitor on pin RCM", 0, 1, RC_MON},
527527
{"RFID", "RFID reader, learn/remove cards", 0, 5 + (ENABLE_OCPP ? 1 : 0), RFID_READER},
528528
{"EV METER","Type of EV electric meter", 0, EM_CUSTOM, EV_METER},

SmartEVSE-3/src/evse.cpp

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ uint16_t GridRelayMaxSumMains = GRID_RELAY_MAX_SUMMAINS; // M
115115
// When the relay opens its contacts, power will be reduced to 4.2kW
116116
// The relay is only allowed on the Master
117117
bool GridRelayOpen = false; // The read status of the relay
118+
bool CustomButton = false; // The status of the custom button
118119
uint16_t MaxCurrent = MAX_CURRENT; // Max Charge current (A)
119120
uint16_t MinCurrent = MIN_CURRENT; // Minimal current the EV is happy with (A)
120121
uint8_t Mode = MODE; // EVSE mode (0:Normal / 1:Smart / 2:Solar)
@@ -135,7 +136,8 @@ uint16_t MaxCircuit = MAX_CIRCUIT; // M
135136
uint8_t Config = CONFIG; // Configuration (0:Socket / 1:Fixed Cable)
136137
uint8_t LoadBl = LOADBL; // Load Balance Setting (0:Disable / 1:Master / 2-8:Node)
137138
uint8_t Switch = SWITCH; // External Switch (0:Disable / 1:Access B / 2:Access S /
138-
// 3:Smart-Solar B / 4:Smart-Solar S / 5: Grid Relay)
139+
// 3:Smart-Solar B / 4:Smart-Solar S / 5: Grid Relay
140+
// 6:Custom B / 7:Custom S)
139141
// B=momentary push <B>utton, S=toggle <S>witch
140142
uint8_t RCmon = RC_MON; // Residual Current Monitor (0:Disable / 1:Enable)
141143
uint8_t AutoUpdate = AUTOUPDATE; // Automatic Firmware Update (0:Disable / 1:Enable)
@@ -261,6 +263,7 @@ uint8_t ColorOff[3] = {0, 0, 0}; // off
261263
uint8_t ColorNormal[3] = {0, 255, 0}; // Green
262264
uint8_t ColorSmart[3] = {0, 255, 0}; // Green
263265
uint8_t ColorSolar[3] = {255, 170, 0}; // Orange
266+
uint8_t ColorCustom[3] = {0, 0, 255}; // Blue
264267

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

418-
if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
421+
if (CustomButton) { // Blue for Custom, unless configured otherwise
422+
RedPwm = LedPwm * ColorCustom[0] / 255;
423+
GreenPwm = LedPwm * ColorCustom[1] / 255;
424+
BluePwm = LedPwm * ColorCustom[2] / 255;
425+
} else if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
419426
RedPwm = LedPwm * ColorSolar[0] / 255;
420427
GreenPwm = LedPwm * ColorSolar[1] / 255;
421428
BluePwm = LedPwm * ColorSolar[2] / 255;
422429
} else if (Mode == MODE_SMART) { // Green for Smart, unless configured otherwise
423-
RedPwm = LedPwm * ColorNormal[0] / 255;
424-
GreenPwm = LedPwm * ColorNormal[1] / 255;
425-
BluePwm = LedPwm * ColorNormal[2] / 255;
430+
RedPwm = LedPwm * ColorSmart[0] / 255;
431+
GreenPwm = LedPwm * ColorSmart[1] / 255;
432+
BluePwm = LedPwm * ColorSmart[2] / 255;
426433
} else { // Green for Normal, unless configured otherwise
427434
RedPwm = LedPwm * ColorNormal[0] / 255;
428435
GreenPwm = LedPwm * ColorNormal[1] / 255;
@@ -466,6 +473,10 @@ void BlinkLed(void * parameter) {
466473
GreenPwm = 0;
467474
BluePwm = 0;
468475
#endif //ENABLE_OCPP
476+
} else if (Access_bit == 0 && CustomButton) {
477+
RedPwm = ColorCustom[0];
478+
GreenPwm = ColorCustom[1];
479+
BluePwm = ColorCustom[2];
469480
} else if (Access_bit == 0 || State == STATE_MODEM_DENIED) {
470481
RedPwm = ColorOff[0];
471482
GreenPwm = ColorOff[1];
@@ -485,14 +496,18 @@ void BlinkLed(void * parameter) {
485496
LedPwm = ease8InOutQuad(triwave8(LedCount)); // pre calculate new LedPwm value
486497
}
487498

488-
if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
499+
if (CustomButton) { // Blue for Custom, unless configured otherwise
500+
RedPwm = LedPwm * ColorCustom[0] / 255;
501+
GreenPwm = LedPwm * ColorCustom[1] / 255;
502+
BluePwm = LedPwm * ColorCustom[2] / 255;
503+
} else if (Mode == MODE_SOLAR) { // Orange for Solar, unless configured otherwise
489504
RedPwm = LedPwm * ColorSolar[0] / 255;
490505
GreenPwm = LedPwm * ColorSolar[1] / 255;
491506
BluePwm = LedPwm * ColorSolar[2] / 255;
492507
} else if (Mode == MODE_SMART) { // Green for Smart, unless configured otherwise
493-
RedPwm = LedPwm * ColorNormal[0] / 255;
494-
GreenPwm = LedPwm * ColorNormal[1] / 255;
495-
BluePwm = LedPwm * ColorNormal[2] / 255;
508+
RedPwm = LedPwm * ColorSmart[0] / 255;
509+
GreenPwm = LedPwm * ColorSmart[1] / 255;
510+
BluePwm = LedPwm * ColorSmart[2] / 255;
496511
} else { // Green for Normal, unless configured otherwise
497512
RedPwm = LedPwm * ColorNormal[0] / 255;
498513
GreenPwm = LedPwm * ColorNormal[1] / 255;
@@ -2090,6 +2105,12 @@ void CheckSwitch(bool force = false)
20902105
case 5: // Grid relay
20912106
GridRelayOpen = false;
20922107
break;
2108+
case 6: // Custom button B
2109+
CustomButton = !CustomButton;
2110+
break;
2111+
case 7: // Custom button S
2112+
CustomButton = true;
2113+
break;
20932114
default:
20942115
if (State == STATE_C) { // Menu option Access is set to Disabled
20952116
setState(STATE_C1);
@@ -2134,6 +2155,11 @@ void CheckSwitch(bool force = false)
21342155
case 5: // Grid relay
21352156
GridRelayOpen = true;
21362157
break;
2158+
case 6: // Custom button B
2159+
break;
2160+
case 7: // Custom button S
2161+
CustomButton = false;
2162+
break;
21372163
default:
21382164
break;
21392165
}
@@ -2772,6 +2798,12 @@ void mqtt_receive_callback(const String topic, const String payload) {
27722798
OverrideCurrent = 0;
27732799
setMode(MODE_SMART);
27742800
}
2801+
} else if (topic == MQTTprefix + "/Set/CustomButton") {
2802+
if (payload == "On") {
2803+
CustomButton = true;
2804+
} else {
2805+
CustomButton = false;
2806+
}
27752807
} else if (topic == MQTTprefix + "/Set/CurrentOverride") {
27762808
uint16_t RequestedCurrent = payload.toInt();
27772809
if (RequestedCurrent == 0) {
@@ -2862,7 +2894,7 @@ void mqtt_receive_callback(const String topic, const String payload) {
28622894
}
28632895
} else if (topic == MQTTprefix + "/Set/ColorOff") {
28642896
int32_t R, G, B;
2865-
int n = sscanf(payload.c_str(), "%d:%d:%d", &R, &G, &B);
2897+
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);
28662898

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

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

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

28972929
// R,G,B is between 0..255
28982930
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
28992931
ColorSolar[0] = R;
29002932
ColorSolar[1] = G;
29012933
ColorSolar[2] = B;
29022934
}
2935+
} else if (topic == MQTTprefix + "/Set/ColorCustom") {
2936+
int32_t R, G, B;
2937+
int n = sscanf(payload.c_str(), "%d,%d,%d", &R, &G, &B);
2938+
2939+
// R,G,B is between 0..255
2940+
if (n == 3 && (R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
2941+
ColorCustom[0] = R;
2942+
ColorCustom[1] = G;
2943+
ColorCustom[2] = B;
2944+
}
29032945
}
29042946

29052947
// Make sure MQTT updates directly to prevent debounces
@@ -3041,6 +3083,22 @@ void SetupMQTTClient() {
30413083
announce("State", "sensor");
30423084
announce("RFID", "sensor");
30433085
announce("RFIDLastRead", "sensor");
3086+
3087+
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorOff")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorOff"));
3088+
announce("LED Color Off", "text");
3089+
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorNormal")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorNormal"));
3090+
announce("LED Color Normal", "text");
3091+
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorSmart")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorSmart"));
3092+
announce("LED Color Smart", "text");
3093+
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorSolar")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorSolar"));
3094+
announce("LED Color Solar", "text");
3095+
optional_payload = jsna("state_topic", String(MQTTprefix + "/LEDColorCustom")) + jsna("command_topic", String(MQTTprefix + "/Set/ColorCustom"));
3096+
announce("LED Color Custom", "text");
3097+
3098+
optional_payload = jsna("state_topic", String(MQTTprefix + "/CustomButton")) + jsna("command_topic", String(MQTTprefix + "/Set/CustomButton"));
3099+
optional_payload += String(R"(, "options" : ["On", "Off"])");
3100+
announce("Custom Button", "select");
3101+
30443102
#if ENABLE_OCPP
30453103
announce("OCPP", "sensor");
30463104
announce("OCPPConnection", "sensor");
@@ -3097,6 +3155,7 @@ void mqttPublishData() {
30973155
MQTTclient.publish(MQTTprefix + "/ESPTemp", TempEVSE, false, 0);
30983156
MQTTclient.publish(MQTTprefix + "/Mode", Access_bit == 0 ? "Off" : Mode > 3 ? "N/A" : StrMode[Mode], true, 0);
30993157
MQTTclient.publish(MQTTprefix + "/MaxCurrent", MaxCurrent * 10, true, 0);
3158+
MQTTclient.publish(MQTTprefix + "/CustomButton", CustomButton ? "On" : "Off", false, 0);
31003159
MQTTclient.publish(MQTTprefix + "/ChargeCurrent", Balanced[0], true, 0);
31013160
MQTTclient.publish(MQTTprefix + "/ChargeCurrentOverride", OverrideCurrent, true, 0);
31023161
MQTTclient.publish(MQTTprefix + "/Access", StrAccessBit[Access_bit], true, 0);
@@ -3140,6 +3199,7 @@ void mqttPublishData() {
31403199
MQTTclient.publish(MQTTprefix + "/LEDColorNormal", String(ColorNormal[0])+","+String(ColorNormal[1])+","+String(ColorNormal[2]), true, 0);
31413200
MQTTclient.publish(MQTTprefix + "/LEDColorSmart", String(ColorSmart[0])+","+String(ColorSmart[1])+","+String(ColorSmart[2]), true, 0);
31423201
MQTTclient.publish(MQTTprefix + "/LEDColorSolar", String(ColorSolar[0])+","+String(ColorSolar[1])+","+String(ColorSolar[2]), true, 0);
3202+
MQTTclient.publish(MQTTprefix + "/LEDColorCustom", String(ColorCustom[0])+","+String(ColorCustom[1])+","+String(ColorCustom[2]), true, 0);
31433203
}
31443204
#endif
31453205

@@ -4782,6 +4842,7 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
47824842
doc["evse"]["mode"] = Mode;
47834843
doc["evse"]["loadbl"] = LoadBl;
47844844
doc["evse"]["pwm"] = CurrentPWM;
4845+
doc["evse"]["custombutton"] = CustomButton;
47854846
doc["evse"]["solar_stop_timer"] = SolarStopTimer;
47864847
doc["evse"]["state"] = evstate;
47874848
doc["evse"]["state_id"] = State;
@@ -4905,6 +4966,9 @@ static void fn_http_server(struct mg_connection *c, int ev, void *ev_data) {
49054966
doc["color"]["solar"]["R"] = ColorSolar[0];
49064967
doc["color"]["solar"]["G"] = ColorSolar[1];
49074968
doc["color"]["solar"]["B"] = ColorSolar[2];
4969+
doc["color"]["custom"]["R"] = ColorCustom[0];
4970+
doc["color"]["custom"]["G"] = ColorCustom[1];
4971+
doc["color"]["custom"]["B"] = ColorCustom[2];
49084972

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

5023+
if(request->hasParam("custombutton")) {
5024+
CustomButton = request->getParam("custombutton")->value().toInt() > 0;
5025+
doc["custombutton"] = CustomButton;
5026+
}
5027+
49595028
if(request->hasParam("mode")) {
49605029
String mode = request->getParam("mode")->value();
49615030

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

5397+
} else if (mg_http_match_uri(hm, "/color_custom") && !memcmp("POST", hm->method.buf, hm->method.len)) {
5398+
DynamicJsonDocument doc(200);
5399+
5400+
if (request->hasParam("R") && request->hasParam("G") && request->hasParam("B")) {
5401+
int32_t R = request->getParam("R")->value().toInt();
5402+
int32_t G = request->getParam("G")->value().toInt();
5403+
int32_t B = request->getParam("B")->value().toInt();
5404+
5405+
// R,G,B is between 0..255
5406+
if ((R >= 0 && R < 256) && (G >= 0 && G < 256) && (B >= 0 && B < 256)) {
5407+
ColorCustom[0] = R;
5408+
ColorCustom[1] = G;
5409+
ColorCustom[2] = B;
5410+
doc["color"]["custom"]["R"] = ColorCustom[0];
5411+
doc["color"]["custom"]["G"] = ColorCustom[1];
5412+
doc["color"]["custom"]["B"] = ColorCustom[2];
5413+
}
5414+
}
5415+
5416+
String json;
5417+
serializeJson(doc, json);
5418+
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\r\n", json.c_str()); // Yes. Respond JSON
5419+
53285420
} else if (mg_http_match_uri(hm, "/currents") && !memcmp("POST", hm->method.buf, hm->method.len)) {
53295421
DynamicJsonDocument doc(200);
53305422

SmartEVSE-3/src/glcd.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ const char * getMenuItemOption(uint8_t nav) {
918918
const static char StrMotor[] = "Motor";
919919
const static char StrDisabled[] = "Disabled";
920920
const static char StrLoadBl[9][9] = {"Disabled", "Master", "Node 1", "Node 2", "Node 3", "Node 4", "Node 5", "Node 6", "Node 7"};
921-
const static char StrSwitch[6][11] = {"Disabled", "Access B", "Access S", "Sma-Sol B", "Sma-Sol S", "Grid Relay"};
921+
const static char StrSwitch[8][11] = {"Disabled", "Access B", "Access S", "Sma-Sol B", "Sma-Sol S", "Grid Relay", "Custom B", "Custom S"};
922922
const static char StrGrid[2][10] = {"4Wire", "3Wire"};
923923
const static char StrEnabled[] = "Enabled";
924924
const static char StrExitMenu[] = "MENU";

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ SWITCH Set the function of an external switch (pin SW or connector P2)
8484
<Sma-Sol B> A momentary push Button is used to switch between Smart and Solar modes
8585
<Sma-Sol S> A toggle switch is used to switch between Smart and Solar modes
8686
<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.
87+
<Custom B> A momentary push Button can be used by external integrations
88+
<Custom S> A toggle switch can be used by external integrations
8789
8890
RCMON RCM14-03 Residual Current Monitor is plugged into connector P1
8991
<Disabled> The RCD option is not used

0 commit comments

Comments
 (0)