File syn_ocpp.c¶
File List > proto > syn_ocpp.c
Go to the documentation of this file
#include "syn_ocpp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *status_to_str(SYN_OCPP_ChargePointStatus status)
{
switch (status) {
case SYN_OCPP_STATUS_AVAILABLE:
return "Available";
case SYN_OCPP_STATUS_PREPARING:
return "Preparing";
case SYN_OCPP_STATUS_CHARGING:
return "Charging";
case SYN_OCPP_STATUS_SUSPENDED_EV:
return "SuspendedEV";
case SYN_OCPP_STATUS_SUSPENDED_EVSE:
return "SuspendedEVSE";
case SYN_OCPP_STATUS_FINISHING:
return "Finishing";
case SYN_OCPP_STATUS_RESERVED:
return "Reserved";
case SYN_OCPP_STATUS_UNAVAILABLE:
return "Unavailable";
case SYN_OCPP_STATUS_FAULTED:
return "Faulted";
default:
return "Unavailable";
}
}
/* ── EVSE Client Implementation ─────────────────────────────────────── */
SYN_Status syn_ocpp_init(SYN_OCPP_Client *client)
{
if (client == NULL)
return SYN_INVALID_PARAM;
memset(client, 0, sizeof(*client));
client->registration_status = SYN_OCPP_REGISTRATION_PENDING;
client->heartbeat_interval_sec = 60U;
client->heartbeat_timer_ms = 60000U;
client->connector_id = 1U;
client->connector_status = SYN_OCPP_STATUS_AVAILABLE;
client->active_transaction_id = -1;
return SYN_OK;
}
SYN_Status syn_ocpp_set_callbacks(SYN_OCPP_Client *client, SYN_OCPP_RegistrationHandler reg_cb,
SYN_OCPP_AuthorizationHandler auth_cb,
SYN_OCPP_StartTxHandler start_tx_cb,
SYN_OCPP_RemoteStartHandler remote_start_cb,
SYN_OCPP_RemoteStopHandler remote_stop_cb, void *user_ctx)
{
if (client == NULL)
return SYN_INVALID_PARAM;
client->reg_cb = reg_cb;
client->auth_cb = auth_cb;
client->start_tx_cb = start_tx_cb;
client->remote_start_cb = remote_start_cb;
client->remote_stop_cb = remote_stop_cb;
client->user_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_ocpp_format_boot_notification(SYN_OCPP_Client *client,
const SYN_OCPP_ChargePointInfo *info, char *out_buf,
size_t max_len, size_t *out_len)
{
if (client == NULL || info == NULL || out_buf == NULL || out_len == NULL || max_len < 64U) {
return SYN_INVALID_PARAM;
}
client->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"BootNotification\",{"
"\"chargePointVendor\":\"%s\","
"\"chargePointModel\":\"%s\","
"\"chargePointSerialNumber\":\"%s\","
"\"firmwareVersion\":\"%s\"}]",
(unsigned int)client->message_counter,
info->charge_point_vendor ? info->charge_point_vendor : "SyntropicOS",
info->charge_point_model ? info->charge_point_model : "EVSE-v1",
info->serial_number ? info->serial_number : "SN-12345",
info->firmware_version ? info->firmware_version : "1.0.0");
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_heartbeat(SYN_OCPP_Client *client, char *out_buf, size_t max_len,
size_t *out_len)
{
if (client == NULL || out_buf == NULL || out_len == NULL || max_len < 32U) {
return SYN_INVALID_PARAM;
}
client->message_counter++;
int written = snprintf(out_buf, max_len, "[2,\"%u\",\"Heartbeat\",{}]",
(unsigned int)client->message_counter);
/* LCOV_EXCL_START: Defensive snprintf truncation check */
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
/* LCOV_EXCL_STOP */
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_status_notification(SYN_OCPP_Client *client, uint32_t connector_id,
SYN_OCPP_ChargePointStatus status,
const char *error_code, char *out_buf,
size_t max_len, size_t *out_len)
{
if (client == NULL || out_buf == NULL || out_len == NULL || max_len < 64U) {
return SYN_INVALID_PARAM;
}
client->connector_id = connector_id;
client->connector_status = status;
client->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"StatusNotification\",{"
"\"connectorId\":%u,"
"\"status\":\"%s\","
"\"errorCode\":\"%s\"}]",
(unsigned int)client->message_counter, (unsigned int)connector_id,
status_to_str(status), error_code ? error_code : "NoError");
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_authorize(SYN_OCPP_Client *client, const char *id_tag, char *out_buf,
size_t max_len, size_t *out_len)
{
if (client == NULL || id_tag == NULL || out_buf == NULL || out_len == NULL || max_len < 48U) {
return SYN_INVALID_PARAM;
}
client->message_counter++;
int written = snprintf(out_buf, max_len, "[2,\"%u\",\"Authorize\",{\"idTag\":\"%s\"}]",
(unsigned int)client->message_counter, id_tag);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_start_transaction(SYN_OCPP_Client *client, uint32_t connector_id,
const char *id_tag, uint32_t meter_start_wh,
char *out_buf, size_t max_len, size_t *out_len)
{
if (client == NULL || id_tag == NULL || out_buf == NULL || out_len == NULL || max_len < 96U) {
return SYN_INVALID_PARAM;
}
client->tx_start_energy_wh = meter_start_wh;
snprintf(client->active_id_tag, sizeof(client->active_id_tag), "%s", id_tag);
client->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"StartTransaction\",{"
"\"connectorId\":%u,"
"\"idTag\":\"%s\","
"\"meterStart\":%u,"
"\"timestamp\":\"2026-08-06T12:00:00Z\"}]",
(unsigned int)client->message_counter, (unsigned int)connector_id,
id_tag, (unsigned int)meter_start_wh);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_stop_transaction(SYN_OCPP_Client *client, int32_t transaction_id,
uint32_t meter_stop_wh, const char *reason,
char *out_buf, size_t max_len, size_t *out_len)
{
if (client == NULL || out_buf == NULL || out_len == NULL || max_len < 96U) {
return SYN_INVALID_PARAM;
}
client->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"StopTransaction\",{"
"\"transactionId\":%d,"
"\"meterStop\":%u,"
"\"timestamp\":\"2026-08-06T12:30:00Z\","
"\"reason\":\"%s\"}]",
(unsigned int)client->message_counter, (int)transaction_id,
(unsigned int)meter_stop_wh, reason ? reason : "Local");
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
client->active_transaction_id = -1;
return SYN_OK;
}
SYN_Status syn_ocpp_format_meter_values(SYN_OCPP_Client *client, uint32_t connector_id,
const SYN_OCPP_MeterValues *values, char *out_buf,
size_t max_len, size_t *out_len)
{
if (client == NULL || values == NULL || out_buf == NULL || out_len == NULL || max_len < 128U) {
return SYN_INVALID_PARAM;
}
client->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"MeterValues\",{"
"\"connectorId\":%u,"
"\"transactionId\":%d,"
"\"meterValue\":[{"
"\"timestamp\":\"2026-08-06T12:15:00Z\","
"\"sampledValue\":["
"{\"value\":\"%u\",\"unit\":\"Wh\"},"
"{\"value\":\"%u\",\"unit\":\"V\"},"
"{\"value\":\"%u\",\"unit\":\"A\"},"
"{\"value\":\"%u\",\"unit\":\"Percent\"}]}]}]",
(unsigned int)client->message_counter, (unsigned int)connector_id,
(int)client->active_transaction_id, (unsigned int)values->energy_wh,
(unsigned int)values->voltage_v, (unsigned int)values->current_a,
(unsigned int)values->soc_percent);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_process_message(SYN_OCPP_Client *client, const char *in_buf, size_t in_len,
char *out_resp, size_t max_resp_len, size_t *out_resp_len)
{
if (client == NULL || in_buf == NULL || in_len < 5U)
return SYN_INVALID_PARAM;
if (out_resp_len != NULL)
*out_resp_len = 0U;
if (in_buf[0] != '[')
return SYN_ERROR;
uint8_t msg_type = (uint8_t)(in_buf[1] - '0');
if (msg_type == SYN_OCPP_MSG_TYPE_CALLRESULT) {
if (strstr(in_buf, "\"Accepted\"") != NULL) {
client->registration_status = SYN_OCPP_REGISTRATION_ACCEPTED;
if (client->reg_cb != NULL) {
client->reg_cb(SYN_OCPP_REGISTRATION_ACCEPTED, client->heartbeat_interval_sec,
client->user_ctx);
}
if (client->auth_cb != NULL && strstr(in_buf, "idTagInfo") != NULL) {
client->auth_cb(client->active_id_tag, SYN_OCPP_AUTH_ACCEPTED, client->user_ctx);
}
}
const char *tx_ptr = strstr(in_buf, "\"transactionId\":");
if (tx_ptr != NULL) {
int tx_id = atoi(tx_ptr + 16);
if (tx_id > 0) {
client->active_transaction_id = tx_id;
if (client->start_tx_cb != NULL) {
client->start_tx_cb(tx_id, SYN_OCPP_AUTH_ACCEPTED, client->user_ctx);
}
}
}
return SYN_OK;
}
if (msg_type == SYN_OCPP_MSG_TYPE_CALL && out_resp != NULL && out_resp_len != NULL &&
max_resp_len >= 32U) {
char msg_id[32] = {0};
const char *id_start = strchr(in_buf, '"');
if (id_start != NULL) {
const char *id_end = strchr(id_start + 1, '"');
if (id_end != NULL && (size_t)(id_end - id_start - 1) < sizeof(msg_id)) {
memcpy(msg_id, id_start + 1, id_end - id_start - 1);
}
}
if (strstr(in_buf, "\"RemoteStartTransaction\"") != NULL) {
bool ok = false;
if (client->remote_start_cb != NULL) {
ok = client->remote_start_cb(client->connector_id, "REMOTE_TAG", client->user_ctx);
} else {
ok = true;
}
int written = snprintf(out_resp, max_resp_len, "[3,\"%s\",{\"status\":\"%s\"}]",
msg_id[0] ? msg_id : "1", ok ? "Accepted" : "Rejected");
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
if (strstr(in_buf, "\"RemoteStopTransaction\"") != NULL) {
bool ok = false;
if (client->remote_stop_cb != NULL) {
ok = client->remote_stop_cb(client->active_transaction_id, client->user_ctx);
} else {
ok = true;
}
int written = snprintf(out_resp, max_resp_len, "[3,\"%s\",{\"status\":\"%s\"}]",
msg_id[0] ? msg_id : "1", ok ? "Accepted" : "Rejected");
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
int written = snprintf(out_resp, max_resp_len, "[3,\"%s\",{}]", msg_id[0] ? msg_id : "1");
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
return SYN_OK;
}
void syn_ocpp_tick(SYN_OCPP_Client *client, uint32_t dt_ms, char *out_hb_buf, size_t max_len,
size_t *out_len)
{
if (client == NULL)
return;
if (out_len != NULL)
*out_len = 0U;
if (client->heartbeat_timer_ms > dt_ms) {
client->heartbeat_timer_ms -= dt_ms;
} else {
client->heartbeat_timer_ms = (client->heartbeat_interval_sec > 0U)
? (client->heartbeat_interval_sec * 1000U)
: 60000U;
if (out_hb_buf != NULL && out_len != NULL && max_len >= 32U) {
(void)syn_ocpp_format_heartbeat(client, out_hb_buf, max_len, out_len);
}
}
}
/* ── CSMS Server Implementation ─────────────────────────────────────── */
SYN_Status syn_ocpp_server_init(SYN_OCPP_Server *server)
{
if (server == NULL)
return SYN_INVALID_PARAM;
memset(server, 0, sizeof(*server));
server->next_transaction_id = 1000;
return SYN_OK;
}
SYN_Status syn_ocpp_server_set_callbacks(SYN_OCPP_Server *server,
SYN_OCPP_ServerBootHandler boot_cb,
SYN_OCPP_ServerAuthorizeHandler auth_cb,
SYN_OCPP_ServerStartTxHandler start_tx_cb, void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->boot_cb = boot_cb;
server->auth_cb = auth_cb;
server->start_tx_cb = start_tx_cb;
server->user_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_ocpp_server_format_remote_start(SYN_OCPP_Server *server, uint32_t connector_id,
const char *id_tag, char *out_buf, size_t max_len,
size_t *out_len)
{
if (server == NULL || id_tag == NULL || out_buf == NULL || out_len == NULL || max_len < 64U) {
return SYN_INVALID_PARAM;
}
server->message_counter++;
int written =
snprintf(out_buf, max_len,
"[2,\"%u\",\"RemoteStartTransaction\",{"
"\"connectorId\":%u,"
"\"idTag\":\"%s\"}]",
(unsigned int)server->message_counter, (unsigned int)connector_id, id_tag);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_server_format_remote_stop(SYN_OCPP_Server *server, int32_t transaction_id,
char *out_buf, size_t max_len, size_t *out_len)
{
if (server == NULL || out_buf == NULL || out_len == NULL || max_len < 48U) {
return SYN_INVALID_PARAM;
}
server->message_counter++;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"RemoteStopTransaction\",{"
"\"transactionId\":%d}]",
(unsigned int)server->message_counter, (int)transaction_id);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_server_process_message(SYN_OCPP_Server *server, const char *in_buf,
size_t in_len, char *out_resp, size_t max_resp_len,
size_t *out_resp_len)
{
if (server == NULL || in_buf == NULL || out_resp == NULL || out_resp_len == NULL ||
in_len < 5U || max_resp_len < 64U) {
return SYN_INVALID_PARAM;
}
*out_resp_len = 0U;
if (in_buf[0] != '[')
return SYN_ERROR;
uint8_t msg_type = (uint8_t)(in_buf[1] - '0');
if (msg_type != SYN_OCPP_MSG_TYPE_CALL)
return SYN_OK;
char msg_id[32] = {0};
const char *id_start = strchr(in_buf, '"');
if (id_start != NULL) {
const char *id_end = strchr(id_start + 1, '"');
if (id_end != NULL && (size_t)(id_end - id_start - 1) < sizeof(msg_id)) {
memcpy(msg_id, id_start + 1, id_end - id_start - 1);
}
}
/* 1. BootNotification */
if (strstr(in_buf, "\"BootNotification\"") != NULL) {
SYN_OCPP_RegistrationStatus st = SYN_OCPP_REGISTRATION_ACCEPTED;
uint32_t interval = 60U;
if (server->boot_cb != NULL) {
SYN_OCPP_ChargePointInfo info = {"StationVendor", "StationModel", "SN-1", "v1.0"};
st = server->boot_cb(&info, &interval, server->user_ctx);
}
int written = snprintf(out_resp, max_resp_len,
"[3,\"%s\",{"
"\"status\":\"%s\","
"\"currentTime\":\"2026-08-06T12:00:00Z\","
"\"interval\":%u}]",
msg_id[0] ? msg_id : "1",
(st == SYN_OCPP_REGISTRATION_ACCEPTED) ? "Accepted" : "Rejected",
(unsigned int)interval);
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
/* 2. Authorize */
if (strstr(in_buf, "\"Authorize\"") != NULL) {
SYN_OCPP_AuthorizationStatus auth_st = SYN_OCPP_AUTH_ACCEPTED;
if (server->auth_cb != NULL) {
auth_st = server->auth_cb("TAG-1", server->user_ctx);
}
int written = snprintf(
out_resp, max_resp_len, "[3,\"%s\",{\"idTagInfo\":{\"status\":\"%s\"}}]",
msg_id[0] ? msg_id : "1", (auth_st == SYN_OCPP_AUTH_ACCEPTED) ? "Accepted" : "Invalid");
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
/* 3. StartTransaction */
if (strstr(in_buf, "\"StartTransaction\"") != NULL) {
int32_t tx_id = server->next_transaction_id++;
if (server->start_tx_cb != NULL) {
int32_t user_tx = server->start_tx_cb(1, "TAG-1", 0, server->user_ctx);
if (user_tx > 0)
tx_id = user_tx;
}
int written = snprintf(out_resp, max_resp_len,
"[3,\"%s\",{"
"\"transactionId\":%d,"
"\"idTagInfo\":{\"status\":\"Accepted\"}}]",
msg_id[0] ? msg_id : "1", (int)tx_id);
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
/* Generic response for Heartbeat / StatusNotification / MeterValues / StopTransaction */
int written = snprintf(out_resp, max_resp_len, "[3,\"%s\",{}]", msg_id[0] ? msg_id : "1");
if (written > 0 && (size_t)written < max_resp_len)
*out_resp_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_display_message(SYN_OCPP_Client *client,
const SYN_OCPP_DisplayMessage *msg, char *out_buf,
size_t max_len, size_t *out_len)
{
if (client == NULL || msg == NULL || out_buf == NULL || out_len == NULL || max_len < 32U)
return SYN_INVALID_PARAM;
uint32_t msg_id = ++client->message_counter;
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"DisplayMessage\",{\"message\":{\"id\":%u,\"header\":\"%s\","
"\"content\":\"%s\",\"displayTimeSec\":%u}}]",
(unsigned int)msg_id, (unsigned int)msg->message_id,
msg->header ? msg->header : "", msg->content ? msg->content : "",
(unsigned int)msg->display_time_s);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_v2g_energy_transfer(SYN_OCPP_Client *client, SYN_OCPP_V2GMode mode,
int32_t power_limit_w, char *out_buf, size_t max_len,
size_t *out_len)
{
if (client == NULL || out_buf == NULL || out_len == NULL || max_len < 32U)
return SYN_INVALID_PARAM;
client->v2g_mode = mode;
uint32_t msg_id = ++client->message_counter;
const char *mode_str = (mode == SYN_OCPP_V2G_SCHEDULED) ? "Scheduled" : "Dynamic";
int written = snprintf(out_buf, max_len,
"[2,\"%u\",\"V2GEnergyTransfer\",{\"v2gMode\":\"%s\","
"\"powerLimitW\":%d}]",
(unsigned int)msg_id, mode_str, (int)power_limit_w);
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}
SYN_Status syn_ocpp_format_call_error(const char *msg_id, const char *error_code,
const char *error_description, char *out_buf, size_t max_len,
size_t *out_len)
{
if (msg_id == NULL || error_code == NULL || out_buf == NULL || out_len == NULL ||
max_len < 32U) {
return SYN_INVALID_PARAM;
}
int written = snprintf(out_buf, max_len, "[4,\"%s\",\"%s\",\"%s\",{}]", msg_id, error_code,
error_description ? error_description : "");
if (written < 0 || (size_t)written >= max_len)
return SYN_ERROR;
*out_len = (size_t)written;
return SYN_OK;
}