Files
helios-evcs/hardware/firmware/src/main.cpp

300 lines
6.7 KiB
C++

#include <Arduino.h>
#include <WiFiManager.h>
#include <string.h>
#include <MicroOcpp.h>
#include <MicroOcppMongooseClient.h>
#include <MicroOcpp/Core/Context.h>
#include <SmartLeds.h>
#include <MFRC522.h>
#include "esp_system.h"
#include "config.h"
/* LED State Enum */
enum LEDState
{
LED_INITIALIZING, // Blue blinking - Initialization and WiFi connecting
LED_WIFI_CONNECTED, // Blue solid - WiFi connected, connecting to OCPP server
LED_OCPP_CONNECTED, // Green solid - Successfully connected to OCPP server
LED_ERROR // Red - Error state
};
static int s_retry_num = 0;
static volatile bool s_ocpp_connected = false;
static volatile LEDState s_led_state = LED_INITIALIZING;
static volatile unsigned long s_blink_last_time = 0;
static volatile bool s_blink_on = false;
static const unsigned long BLINK_INTERVAL = 200; // 200ms blink interval
uint8_t mac[6];
char cpSerial[13];
struct mg_mgr mgr;
/**
* WS2812B LED Pin
* - GPIO 17 - RYMCU ESP32-DevKitC
* - GPIO 16 - YD-ESP32-A
*/
#define LED_PIN 17
#define LED_COUNT 1
SmartLed leds(LED_WS2812B, LED_COUNT, LED_PIN, 0, DoubleBuffer);
/* LED Control Functions */
void updateLED()
{
unsigned long current_time = millis();
switch (s_led_state)
{
case LED_INITIALIZING:
// Blue blinking during initialization
if (current_time - s_blink_last_time >= BLINK_INTERVAL)
{
s_blink_last_time = current_time;
s_blink_on = !s_blink_on;
if (s_blink_on)
{
leds[0] = Rgb{0, 0, 255}; // Blue on
}
else
{
leds[0] = Rgb{0, 0, 0}; // Off
}
leds.show();
}
break;
case LED_WIFI_CONNECTED:
// Blue solid - WiFi connected, OCPP connecting
leds[0] = Rgb{0, 0, 255}; // Blue solid
leds.show();
break;
case LED_OCPP_CONNECTED:
// Green solid - OCPP connected
leds[0] = Rgb{0, 255, 0}; // Green solid
leds.show();
break;
case LED_ERROR:
// Red solid - Error state
leds[0] = Rgb{255, 0, 0}; // Red solid
leds.show();
break;
}
}
void setup()
{
// Get MAC address and set as Charge Point Serial Number
esp_efuse_mac_get_default(mac);
snprintf(cpSerial, sizeof(cpSerial),
"%02X%02X%02X%02X%02X%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// reset LED
leds[0] = Rgb{0, 0, 0};
leds.show();
// initialize Serial
Serial.begin(115200);
delay(1000);
Serial.printf("\n\n%s(%s) made by %s\n", CFG_CP_MODAL, cpSerial, CFG_CP_VENDOR);
Serial.println("Initializing firmware...\n");
// Initialize LED
s_led_state = LED_INITIALIZING;
s_blink_last_time = 0;
s_blink_on = false;
leds[0] = Rgb{255, 255, 0};
leds.show();
WiFiManager wm;
const char *customHeadElement = R"rawliteral(
<style>
:root {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #1f2a37;
--primarycolor: #2563eb;
}
body,
body.invert {
margin: 0;
background: #f4f6fb;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 16px;
color: #1f2a37;
}
.wrap {
width: min(360px, 100%);
background: #fff;
border-radius: 16px;
padding: 24px;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
text-align: left;
}
h1,
h2,
h3,
h4 {
color: #111827;
margin-top: 0;
text-align: center;
}
label {
display: block;
margin-bottom: 6px;
font-weight: 600;
}
input,
select,
button {
border-radius: 10px;
width: 100%;
font-size: 1rem;
box-sizing: border-box;
}
input[type="text"],
input[type="password"],
input[type="number"],
input[type="email"],
select {
padding: 12px;
margin: 0 0 16px 0;
border: 1px solid #d0d7e2;
background: #f9fafc;
transition: border 0.2s, box-shadow 0.2s, background 0.2s;
}
input:focus,
select:focus {
outline: none;
border-color: #2563eb;
background: #fff;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
button,
input[type="submit"],
button.D {
padding: 12px;
border: none;
background: #2563eb;
color: #fff;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
button.D {
background: #dc2626;
}
button:hover,
input[type="submit"]:hover {
background: #1d4ed8;
}
button:active,
input[type="submit"]:active {
transform: scale(0.99);
}
.msg {
border-radius: 12px;
border: 1px solid #e5e7eb;
background: #f9fafb;
padding: 16px;
color: #374151;
}
.msg.P {
border-color: #2563eb;
background: rgba(37, 99, 235, 0.08);
color: #1f2a37;
}
.msg.S {
border-color: #16a34a;
background: rgba(22, 163, 74, 0.08);
}
.msg.D {
border-color: #dc2626;
background: rgba(220, 38, 38, 0.08);
}
.q {
display: none;
}
a {
color: #2563eb;
font-weight: 600;
}
hr {
border: none;
height: 1px;
background: #e5e7eb;
margin: 24px 0;
}
</style>
)rawliteral";
wm.setCustomHeadElement(customHeadElement);
bool autoConnectRet = wm.autoConnect((String("HLCP_") + String(cpSerial).substring(String(cpSerial).length() - 6)).c_str(), cpSerial);
if (!autoConnectRet)
{
Serial.println("Failed to connect and hit timeout");
s_led_state = LED_ERROR;
}
else
{
Serial.println("WiFi connected successfully");
s_led_state = LED_WIFI_CONNECTED;
mg_mgr_init(&mgr);
MicroOcpp::MOcppMongooseClient *client = new MicroOcpp::MOcppMongooseClient(&mgr, CFG_OCPP_BACKEND, CFG_CP_IDENTIFIER, CFG_AUTHORIZATIONKEY, "", MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail), MicroOcpp::ProtocolVersion(1, 6));
mocpp_initialize(*client, ChargerCredentials(CFG_CP_MODAL, CFG_CP_VENDOR, CFG_CP_FW_VERSION, cpSerial, nullptr, nullptr, CFG_CB_SERIAL, nullptr, nullptr), MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail));
}
}
void loop()
{
mg_mgr_poll(&mgr, 10);
mocpp_loop();
auto ctx = getOcppContext();
if (ctx && ctx->getConnection().isConnected())
{
if (s_led_state != LED_OCPP_CONNECTED)
{
s_led_state = LED_OCPP_CONNECTED;
}
}
else
{
if (s_led_state != LED_WIFI_CONNECTED)
{
s_led_state = LED_WIFI_CONNECTED;
}
}
updateLED();
delay(10);
}