Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions SmartEVSE-3/src/esp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ char RequiredEVCCID[32] = ""; // R
#include "modbus.h"
#include "meter.h"
#include "evse_bridge.h"
#include "solar_debug_json.h"
#include "mqtt_parser.h"
#include "mqtt_publish.h"
#include "http_api.h"
Expand Down Expand Up @@ -617,6 +618,8 @@ void writeMqttCaCert(const String& cert) {
}

#if MQTT
static void mqttSetSolarDebug(bool enabled); // forward declaration

void mqtt_receive_callback(const String topic, const String payload) {
mqtt_command_t cmd;
if (!mqtt_parse_command(MQTTprefix.c_str(), topic.c_str(), payload.c_str(), &cmd))
Expand Down Expand Up @@ -798,6 +801,10 @@ void mqtt_receive_callback(const String topic, const String payload) {
request_write_settings();
break;

case MQTT_CMD_SOLAR_DEBUG:
mqttSetSolarDebug(cmd.solar_debug);
break;

default:
return;
}
Expand Down Expand Up @@ -1273,6 +1280,36 @@ void mqttSmartEVSEPublishData() {
MQTTclientSmartEVSE.publish(MQTTSmartEVSEprefix + "/PairingPin", PairingPin, true, 0);
MQTTclientSmartEVSE.publish(MQTTSmartEVSEprefix + "/MaxCurrent", String(MaxCurrent * 10), true, 0);
}

// Solar debug MQTT publishing (Issue #66)
// Gated behind SolarDebugEnabled flag — only publishes when enabled.
// Rate-limited: publishes at most once every SOLAR_DEBUG_INTERVAL_MS.
static bool SolarDebugEnabled = false;
static unsigned long SolarDebugLastPublish = 0;
#define SOLAR_DEBUG_INTERVAL_MS 5000

void mqttPublishSolarDebug(void) {
if (!SolarDebugEnabled) return;
if (!MQTTclient.connected) return;

unsigned long now = millis();
if (SolarDebugLastPublish != 0 && (now - SolarDebugLastPublish) < SOLAR_DEBUG_INTERVAL_MS)
return;

evse_solar_debug_t snap;
evse_get_solar_debug(&snap);

char json[384];
if (solar_debug_to_json(&snap, json, sizeof(json)) > 0) {
MQTTclient.publish(MQTTprefix + "/Debug/Solar", json, false, 0);
SolarDebugLastPublish = now;
}
}

static void mqttSetSolarDebug(bool enabled) {
SolarDebugEnabled = enabled;
if (!enabled) SolarDebugLastPublish = 0;
}
#endif


Expand Down
12 changes: 12 additions & 0 deletions SmartEVSE-3/src/evse_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,18 @@ void evse_bridge_unlock(void) {
#endif
}

// ---- Solar debug snapshot reader (spinlock-protected) ----
void evse_get_solar_debug(evse_solar_debug_t *out) {
if (!out) return;
#ifdef SMARTEVSE_VERSION
portENTER_CRITICAL(&evse_sync_spinlock);
#endif
*out = g_evse_ctx.solar_debug;
#ifdef SMARTEVSE_VERSION
portEXIT_CRITICAL(&evse_sync_spinlock);
#endif
}

// ---- Initialization ----
void evse_bridge_init(void) {
#ifdef SMARTEVSE_VERSION
Expand Down
1 change: 1 addition & 0 deletions SmartEVSE-3/src/evse_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void evse_sync_globals_to_ctx(void);
void evse_sync_ctx_to_globals(void);
void evse_bridge_lock(void);
void evse_bridge_unlock(void);
void evse_get_solar_debug(evse_solar_debug_t *out);

#ifdef __cplusplus
}
Expand Down
3 changes: 3 additions & 0 deletions SmartEVSE-3/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ extern void BroadcastCurrent(void);
extern void CheckRFID(void);
extern void mqttPublishData();
extern void mqttSmartEVSEPublishData();
extern void mqttPublishSolarDebug(void);
extern bool MQTTclientSmartEVSE_AppConnected;
extern void DisconnectEvent(void);
extern char EVCCID[32];
Expand Down Expand Up @@ -1110,6 +1111,8 @@ static void timer1s_mqtt_publish(void) {
lastSmartEVSEUpdate = 0;
mqttSmartEVSEPublishData();
}
// Solar debug publishing — rate-limited internally (5s default)
mqttPublishSolarDebug();
}
#endif

Expand Down
13 changes: 13 additions & 0 deletions SmartEVSE-3/src/mqtt_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,18 @@ bool mqtt_parse_command(const char *prefix, const char *topic,
return false;
}

if (match_topic(prefix, topic, "/Set/SolarDebug")) {
out->cmd = MQTT_CMD_SOLAR_DEBUG;
if (strcmp(payload, "1") == 0 || strcmp(payload, "ON") == 0) {
out->solar_debug = true;
return true;
}
if (strcmp(payload, "0") == 0 || strcmp(payload, "OFF") == 0) {
out->solar_debug = false;
return true;
}
return false;
}

return false;
}
2 changes: 2 additions & 0 deletions SmartEVSE-3/src/mqtt_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ typedef enum {
MQTT_CMD_IDLE_TIMEOUT,
MQTT_CMD_MQTT_HEARTBEAT,
MQTT_CMD_MQTT_CHANGE_ONLY,
MQTT_CMD_SOLAR_DEBUG,
} mqtt_cmd_type_t;

// Mode values matching firmware MODE_NORMAL/MODE_SOLAR/MODE_SMART
Expand Down Expand Up @@ -64,6 +65,7 @@ typedef struct {
uint16_t idle_timeout; // MQTT_CMD_IDLE_TIMEOUT (30-300)
uint16_t mqtt_heartbeat; // MQTT_CMD_MQTT_HEARTBEAT (10-300)
bool mqtt_change_only; // MQTT_CMD_MQTT_CHANGE_ONLY (0/1)
bool solar_debug; // MQTT_CMD_SOLAR_DEBUG (0/1)
};
} mqtt_command_t;

Expand Down
51 changes: 51 additions & 0 deletions SmartEVSE-3/src/solar_debug_json.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* solar_debug_json.c - Format evse_solar_debug_t as JSON
*
* Pure C module — no platform dependencies, testable natively.
*/

#include "solar_debug_json.h"
#include <stdio.h>

int solar_debug_to_json(const evse_solar_debug_t *snap, char *buf, size_t bufsz)
{
if (!snap || !buf || bufsz == 0)
return -1;

int n = snprintf(buf, bufsz,
"{"
"\"IsetBalanced\":%d,"
"\"IsetBalanced_ema\":%d,"
"\"Idifference\":%d,"
"\"IsumImport\":%d,"
"\"Isum\":%d,"
"\"MainsMeterImeasured\":%d,"
"\"Balanced0\":%u,"
"\"SolarStopTimer\":%u,"
"\"PhaseSwitchTimer\":%u,"
"\"PhaseSwitchHoldDown\":%u,"
"\"NoCurrent\":%u,"
"\"SettlingTimer\":%u,"
"\"Nr_Of_Phases_Charging\":%u,"
"\"ErrorFlags\":%u"
"}",
(int)snap->IsetBalanced,
(int)snap->IsetBalanced_ema,
(int)snap->Idifference,
(int)snap->IsumImport,
(int)snap->Isum,
(int)snap->MainsMeterImeasured,
(unsigned)snap->Balanced0,
(unsigned)snap->SolarStopTimer,
(unsigned)snap->PhaseSwitchTimer,
(unsigned)snap->PhaseSwitchHoldDown,
(unsigned)snap->NoCurrent,
(unsigned)snap->SettlingTimer,
(unsigned)snap->Nr_Of_Phases_Charging,
(unsigned)snap->ErrorFlags);

if (n < 0 || (size_t)n >= bufsz)
return -1;

return n;
}
31 changes: 31 additions & 0 deletions SmartEVSE-3/src/solar_debug_json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* solar_debug_json.h - Format evse_solar_debug_t as JSON
*
* Pure C module — no platform dependencies, testable natively.
*/

#ifndef SOLAR_DEBUG_JSON_H
#define SOLAR_DEBUG_JSON_H

#include "evse_ctx.h"
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* Format a solar debug snapshot as a JSON object string.
*
* @param snap Pointer to the solar debug snapshot
* @param buf Output buffer
* @param bufsz Size of output buffer
* @return Number of characters written (excluding NUL), or -1 on error
*/
int solar_debug_to_json(const evse_solar_debug_t *snap, char *buf, size_t bufsz);

#ifdef __cplusplus
}
#endif

#endif /* SOLAR_DEBUG_JSON_H */
4 changes: 4 additions & 0 deletions SmartEVSE-3/test/native/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ P1_PARSE_SRC := $(SRC_DIR)/p1_parse.c
MODBUS_LOG_SRC := $(SRC_DIR)/modbus_log.c
OCPP_LOGIC_SRC := $(SRC_DIR)/ocpp_logic.c
OCPP_TELEMETRY_SRC := $(SRC_DIR)/ocpp_telemetry.c
SOLAR_DEBUG_JSON_SRC := $(SRC_DIR)/solar_debug_json.c

# Discover all test files
TEST_SRCS := $(wildcard $(TEST_DIR)/test_*.c)
Expand Down Expand Up @@ -93,6 +94,9 @@ $(BUILD)/test_ocpp_telemetry: $(TEST_DIR)/test_ocpp_telemetry.c $(OCPP_TELEMETRY
$(BUILD)/test_ocpp_iec61851: $(TEST_DIR)/test_ocpp_iec61851.c $(OCPP_LOGIC_SRC) $(SRC_DIR)/ocpp_logic.h include/*.h | $(BUILD)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(TEST_DIR)/test_ocpp_iec61851.c $(OCPP_LOGIC_SRC)

$(BUILD)/test_solar_debug_json: $(TEST_DIR)/test_solar_debug_json.c $(SOLAR_DEBUG_JSON_SRC) $(SRC_DIR)/solar_debug_json.h include/*.h | $(BUILD)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(TEST_DIR)/test_solar_debug_json.c $(SOLAR_DEBUG_JSON_SRC)

# Build each state machine test binary (generic rule)
$(BUILD)/test_%: $(TEST_DIR)/test_%.c $(EVSE_SRC) include/*.h $(SRC_DIR)/*.h | $(BUILD)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(TEST_DIR)/test_$*.c $(EVSE_SRC)
Expand Down
47 changes: 47 additions & 0 deletions SmartEVSE-3/test/native/tests/test_mqtt_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,48 @@ void test_mqtt_change_only_invalid(void) {
TEST_ASSERT_FALSE(mqtt_parse_command(PREFIX, PREFIX "/Set/MQTTChangeOnly", "2", &cmd));
}

// ---- SolarDebug ----

/*
* @feature Solar Debug Telemetry
* @req REQ-SOL-020
* @scenario SolarDebug enable via MQTT
* @given A valid MQTT prefix
* @when Topic is prefix/Set/SolarDebug with payload "1"
* @then The parser returns true with solar_debug = true
*/
void test_solar_debug_enable(void) {
TEST_ASSERT_TRUE(mqtt_parse_command(PREFIX, PREFIX "/Set/SolarDebug", "1", &cmd));
TEST_ASSERT_EQUAL(MQTT_CMD_SOLAR_DEBUG, cmd.cmd);
TEST_ASSERT_TRUE(cmd.solar_debug);
}

/*
* @feature Solar Debug Telemetry
* @req REQ-SOL-020
* @scenario SolarDebug disable via MQTT
* @given A valid MQTT prefix
* @when Topic is prefix/Set/SolarDebug with payload "0"
* @then The parser returns true with solar_debug = false
*/
void test_solar_debug_disable(void) {
TEST_ASSERT_TRUE(mqtt_parse_command(PREFIX, PREFIX "/Set/SolarDebug", "0", &cmd));
TEST_ASSERT_EQUAL(MQTT_CMD_SOLAR_DEBUG, cmd.cmd);
TEST_ASSERT_FALSE(cmd.solar_debug);
}

/*
* @feature Solar Debug Telemetry
* @req REQ-SOL-020
* @scenario SolarDebug rejects invalid payload
* @given A valid MQTT prefix
* @when Topic is prefix/Set/SolarDebug with payload "2"
* @then The parser returns false
*/
void test_solar_debug_invalid(void) {
TEST_ASSERT_FALSE(mqtt_parse_command(PREFIX, PREFIX "/Set/SolarDebug", "2", &cmd));
}

// ---- Unrecognized topic ----

/*
Expand Down Expand Up @@ -1035,6 +1077,11 @@ int main(void) {
RUN_TEST(test_mqtt_change_only_disable);
RUN_TEST(test_mqtt_change_only_invalid);

// SolarDebug
RUN_TEST(test_solar_debug_enable);
RUN_TEST(test_solar_debug_disable);
RUN_TEST(test_solar_debug_invalid);

// Unrecognized
RUN_TEST(test_unrecognized_topic);
RUN_TEST(test_wrong_prefix);
Expand Down
Loading
Loading