Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/everest_api/api_connector.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
namespace charge_bridge {
struct bsp_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
evse_bsp::everest_api_config api;
};
class bsp_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
bsp_bridge(bsp_bridge_config const& config);
~bsp_bridge() = default;
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_timer_event();
evse_bsp::api_connector m_api;
everest::lib::io::udp::udp_client m_udp;
everest::lib::io::event::timer_fd m_timer;
bool m_udp_on_error{false};
};
} // namespace charge_bridge

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <everest/io/can/can_payload.hpp>
#include <everest/io/can/socket_can.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <memory>
extern "C" struct cb_can_message;
namespace charge_bridge {
struct can_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::string can_device;
};
class can_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
can_bridge(can_bridge_config const& config);
~can_bridge();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_error_timer();
void handle_heartbeat_timer();
void send_can_to_udp(cb_can_message const& pl);
std::unique_ptr<everest::lib::io::can::socket_can> m_can;
everest::lib::io::udp::udp_client m_udp;
std::string m_can_device;
std::string m_identifier;
everest::lib::io::event::timer_fd m_heartbeat_timer;
std::chrono::steady_clock::time_point m_last_msg_to_cb;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <atomic>
#include <charge_bridge/bsp_bridge.hpp>
#include <charge_bridge/can_bridge.hpp>
#include <charge_bridge/discovery.hpp>
#include <charge_bridge/firmware_update/sync_fw_updater.hpp>
#include <charge_bridge/gpio_bridge.hpp>
#include <charge_bridge/heartbeat_service.hpp>
#include <charge_bridge/plc_bridge.hpp>
#include <charge_bridge/serial_bridge.hpp>
#include <charge_bridge/utilities/symlink.hpp>
#include <everest/io/event/fd_event_handler.hpp>
#include <everest/io/serial/event_pty.hpp>
#include <everest/io/tun_tap/tap_client.hpp>
#include <everest/util/async/monitor.hpp>
#include <memory>
#include <optional>
namespace charge_bridge {
struct charge_bridge_status {
bool is_connected{false};
bool discovery_pending{false};
};
struct charge_bridge_config {
std::string cb_name;
std::uint16_t cb_port;
std::string cb_remote;
std::optional<can_bridge_config> can0;
std::optional<serial_bridge_config> serial1;
std::optional<serial_bridge_config> serial2;
std::optional<serial_bridge_config> serial3;
std::optional<plc_bridge_config> plc;
std::optional<bsp_bridge_config> bsp;
std::optional<heartbeat_config> heartbeat;
std::optional<gpio_config> gpio;
firmware_update::fw_update_config firmware;
};
void print_charge_bridge_config(charge_bridge_config const& config);
class charge_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
charge_bridge(charge_bridge_config const& config);
~charge_bridge();
bool update_firmware(bool force);
std::string get_pty_1_slave_path();
std::string get_pty_2_slave_path();
std::string get_pty_3_slave_path();
void print_config();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void manage(everest::lib::io::event::fd_event_handler& handler, std::atomic_bool const& exit, bool force_update);
private:
void init();
void init_discovery(discovery_device_type type, std::set<std::string> const& interfaces, bool excluding);
void handle_discovery(std::string const& ip);
private:
std::unique_ptr<can_bridge> m_can_0_client;
std::unique_ptr<serial_bridge> m_pty_1;
std::unique_ptr<serial_bridge> m_pty_2;
std::unique_ptr<serial_bridge> m_pty_3;
std::unique_ptr<bsp_bridge> m_bsp;
std::unique_ptr<plc_bridge> m_plc;
std::unique_ptr<heartbeat_service> m_heartbeat;
std::unique_ptr<gpio_bridge> m_gpio;
std::unique_ptr<discovery> m_discovery;
everest::lib::io::event::fd_event_handler* m_event_handler{nullptr};
bool m_force_firmware_update{false};
everest::lib::util::monitor<charge_bridge_status> m_cb_status;
bool m_was_connected{false};
bool m_discovery_active{false};
charge_bridge_config m_config;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mdns/mdns.hpp>
#include <everest/io/mdns/mdns_client.hpp>
#include <functional>
#include <memory>
#include <set>
namespace charge_bridge {
enum class discovery_device_type {
CB_EVSE,
CB_EV
};
class discovery : public everest::lib::io::event::fd_event_register_interface {
public:
using discovery_cb = std::function<void(std::string const&)>;
discovery(discovery_device_type type);
discovery(discovery_device_type type, std::set<std::string> const& interfaces, bool excluding);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void set_discovery_callback(discovery_cb const& cb);
private:
void add_client(std::string const& interface);
void query_registry();
std::vector<std::unique_ptr<everest::lib::io::mdns::mdns_client>> m_mdns;
everest::lib::io::event::timer_fd m_timer;
discovery_cb m_on_discover;
everest::lib::io::mdns::mDNS_registry m_registry;
discovery_device_type m_type;
static const std::string discovery_id;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/everest_api/ev_bsp_api.hpp>
#include <charge_bridge/everest_api/evse_bsp_api.hpp>
#include <charge_bridge/everest_api/ovm_api.hpp>
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mqtt/mqtt_client.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_BSP = everest::lib::API::V1_0::types::evse_board_support;
struct everest_api_config {
std::string mqtt_remote;
std::string mqtt_bind;
uint16_t mqtt_port;
uint32_t mqtt_ping_interval_ms;
evse_bsp_config evse;
evse_ovm_config ovm;
evse_ev_bsp_config ev;
};
class api_connector : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
public:
api_connector(everest_api_config const& config, std::string const& cb_identifier);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_mqtt_connect();
void handle_cb_connection_state();
bool check_cb_heartbeat();
std::string m_cb_identifier;
everest::lib::io::mqtt::mqtt_client m_mqtt;
tx_ftor m_tx;
std::chrono::steady_clock::time_point m_last_cb_heartbeat;
everest::lib::io::event::timer_fd m_sync_timer;
std::string m_evse_bsp_receive_topic;
std::string m_evse_bsp_send_topic;
std::string m_ovm_receive_topic;
std::string m_ovm_send_topic;
std::string m_ev_bsp_receive_topic;
std::string m_ev_bsp_send_topic;
bool m_evse_bsp_enabled{false};
bool m_ovm_enabled{false};
bool m_ev_bsp_enabled{false};
bool m_cb_initial_comm_check{true};
bool m_cb_connected{false};
evse_bsp_host_to_cb m_host_status;
evse_bsp_api m_evse_bsp;
ovm_api m_ovm;
ev_bsp_api m_ev_bsp;
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,101 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/ev_board_support/API.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/generic/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_EVSE_BSP = everest::lib::API::V1_0::types::evse_board_support;
namespace API_EV_BSP = everest::lib::API::V1_0::types::ev_board_support;
namespace API_EVM = everest::lib::API::V1_0::types::evse_manager;
namespace API_GENERIC = everest::lib::API::V1_0::types::generic;
// namespace API_OVM = everest::lib::API::V1_0::types::over_voltage_monitor;
struct evse_ev_bsp_config {
bool enabled{false};
std::string module_id;
};
class ev_bsp_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
ev_bsp_api(evse_ev_bsp_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void send_bsp_event(API_EVSE_BSP::Event data);
void send_bsp_measurement(API_EV_BSP::BspMeasurement data);
void send_ev_info(API_EVM::EVInfo data);
void send_raise_error(API_GENERIC::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_clear_error(API_GENERIC::ErrorEnum error, std::string const& subtype);
void send_communication_check();
void send_mqtt(std::string const& topic, std::string const& message);
void send_event(API_EVSE_BSP::Event data);
void receive_enable(std::string const& payload);
void receive_set_cp_state(std::string const& payload);
void receive_allow_power_on(std::string const& payload);
void receive_diode_fail(std::string const& payload);
void receive_set_ac_max_current(std::string const& payload);
void receive_set_three_phases(std::string const& payload);
void receive_set_rcd_error(std::string const& payload);
void receive_heartbeat(std::string const& pl);
void handle_error(const SafetyErrorFlags& data);
void handle_event_cp(std::uint8_t cp);
void handle_event_relay(std::uint8_t relay);
void handle_bsp_measurement(uint16_t cp, uint8_t pp_1, uint8_t pp2);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host m_cb_status;
tx_ftor m_tx;
bool m_everest_connected{false};
bool m_cb_connected{false};
bool m_cb_initial_comm_check{true};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
everest::lib::API::V1_0::types::evse_board_support::Event last_cp_event{
everest::lib::API::V1_0::types::evse_board_support::Event::Disconnected};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_BSP = everest::lib::API::V1_0::types::evse_board_support;
namespace API_EVM = everest::lib::API::V1_0::types::evse_manager;
struct evse_bsp_config {
std::string module_id;
bool enabled{false};
API_BSP::HardwareCapabilities capabilities;
};
class evse_bsp_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
evse_bsp_api(evse_bsp_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void handle_event_cp(std::uint8_t cp);
void handle_event_relay(std::uint8_t relay);
void handle_error(const SafetyErrorFlags& data);
void handle_pp_type1(std::uint8_t data);
void handle_pp_type2(std::uint8_t data);
void handle_stop_button(std::uint8_t data);
void send_event(API_BSP::Event data);
void send_ac_nr_of_phases(std::uint8_t data);
void send_capabilities();
void send_ac_pp_amapcity(API_BSP::Ampacity data);
void send_request_stop_transaction(API_EVM::StopTransactionReason data);
void send_rcd_current(std::uint8_t data);
void send_raise_error(API_BSP::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_clear_error(API_BSP::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_communication_check();
void send_reply_reset(std::string const& replyTo);
void send_mqtt(std::string const& topic, std::string const& message);
void receive_enable(std::string const& payload);
void receive_pwm_on(std::string const& payload);
void receive_cp_state_X1(std::string const& payload);
void receive_cp_state_F(std::string const& payload);
void receive_allow_power_on(std::string const& payload);
void receive_ac_switch_three_phases_while_charging(std::string const& payload);
void receive_ac_overcurrent_limit(std::string const& payload);
void receive_lock();
void receive_unlock();
void receive_self_test(std::string const& payload);
void receive_request_reset(std::string const& payload);
void receive_heartbeat(std::string const& pl);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host cb_status;
tx_ftor m_tx;
everest::lib::io::event::timer_fd m_capabilities_timer;
API_BSP::HardwareCapabilities m_capabilities;
bool m_enabled{false};
bool everest_connected{false};
bool m_cb_connected{false};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/over_voltage_monitor/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_OVM = everest::lib::API::V1_0::types::over_voltage_monitor;
struct evse_ovm_config {
bool enabled{false};
std::string module_id;
};
class ovm_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
ovm_api(evse_ovm_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void send_voltage_measurement_V(double data);
void send_raise_error(API_OVM::ErrorEnum error, std::string const& subtype, std::string const& msg,
API_OVM::ErrorSeverityEnum severity);
void send_clear_error(API_OVM::ErrorEnum error, std::string const& subtype);
void send_communication_check();
void send_mqtt(std::string const& topic, std::string const& message);
void handle_dc_hv_ov_emergency(bool high);
void handle_dc_hv_ov_error(bool high);
void handle_cp_state(CpState state);
void receive_set_limits(std::string const& payload);
void receive_start();
void receive_stop();
void receive_reset_over_voltage_error();
void receive_heartbeat(std::string const& pl);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host m_cb_status;
tx_ftor m_tx;
bool m_everest_connected{false};
bool m_cb_connected{false};
bool m_cb_initial_comm_check{true};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
API_OVM::OverVoltageLimits m_limits{0, 0};
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/utilities/filesystem.hpp>
#include <charge_bridge/utilities/sync_udp_client.hpp>
#include <fstream>
namespace charge_bridge::firmware_update {
struct fw_update_config {
std::string cb;
std::uint16_t cb_port;
std::string cb_remote;
std::string fw_path;
bool fw_update_on_start;
};
class sync_fw_updater {
public:
sync_fw_updater(fw_update_config const& config);
~sync_fw_updater() = default;
std::optional<std::string> get_fw_version();
bool switch_bank();
bool ping();
bool upload_fw();
void print_fw_version();
bool print_switch_bank();
bool quick_check_connection();
bool check_connection();
bool check_if_correct_fw_installed();
private:
bool check_reply(utilities::sync_udp_client::reply const& val);
bool upload_firmware();
bool upload_init(const fs::path& file_path, std::uint32_t& offset,
charge_bridge::filesystem_utils::CryptSignedHeader& hdr);
bool upload_transfer(const fs::path& file_path, std::uint16_t& sector, std::uint32_t offset,
std::uint32_t& total_bytes);
bool upload_finish(const fs::path& file_path, std::uint32_t total_bytes,
const charge_bridge::filesystem_utils::CryptSignedHeader& hdr);
everest::lib::io::udp::udp_payload make_fw_chunk(std::uint16_t sector, std::uint8_t last_chunk,
std::vector<std::uint8_t> const& data);
utilities::sync_udp_client m_udp;
fw_update_config m_config;
static const std::uint32_t app_udp_sector_size;
static const std::uint16_t sub_chunk_size;
};
} // namespace charge_bridge::firmware_update

View File

@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "everest/io/mqtt/mosquitto_cpp.hpp"
#include <array>
#include <everest/io/can/can_payload.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mqtt/mqtt_client.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <protocol/cb_management.h>
namespace charge_bridge {
struct gpio_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::uint16_t interval_s;
std::string mqtt_remote;
std::string mqtt_bind;
std::uint16_t mqtt_port;
std::uint32_t mqtt_ping_interval_ms;
};
class gpio_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
gpio_bridge(gpio_config const& config);
~gpio_bridge();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_error_timer();
void handle_heartbeat_timer();
void handle_udp_rx(everest::lib::io::udp::udp_payload const& payload);
void dispatch(everest::lib::io::mqtt::mqtt_client::message const& data);
void send_mqtt(std::string const& topic, std::string const& message);
void send_udp();
everest::lib::io::udp::udp_client m_udp;
bool m_udp_on_error{false};
everest::lib::io::event::timer_fd m_heartbeat_timer;
std::chrono::steady_clock::time_point last_heartbeat;
CbManagementPacket<CbGpioPacket> m_message;
std::string m_identifier;
bool m_mqtt_on_error{false};
everest::lib::io::mqtt::mqtt_client m_mqtt;
std::string m_receive_topic;
std::string m_send_topic;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,54 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "protocol/cb_management.h"
#include <chrono>
#include <everest/io/can/can_payload.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <memory>
#include <protocol/cb_config.h>
namespace charge_bridge {
struct heartbeat_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::uint16_t interval_s;
std::uint16_t connection_to_s;
CbConfig cb_config;
};
class heartbeat_service : public everest::lib::io::event::fd_event_register_interface {
public:
heartbeat_service(heartbeat_config const& config, std::function<void(bool)> const& publish_connection_status);
~heartbeat_service();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_error_timer();
void handle_heartbeat_timer();
void handle_udp_rx(everest::lib::io::udp::udp_payload const& payload);
everest::lib::io::udp::udp_client m_udp;
bool m_udp_on_error{false};
everest::lib::io::event::timer_fd m_heartbeat_timer;
std::string m_identifier;
CbManagementPacket<CbConfig> m_config_message;
std::chrono::steady_clock::time_point m_last_heartbeat_reply;
bool m_cb_connected{false};
bool m_inital_cb_commcheck{true};
std::chrono::milliseconds m_heartbeat_interval;
std::chrono::milliseconds m_connection_to;
std::function<void(bool)> m_publish_connection_status;
std::uint32_t m_mcu_timestamp{0};
int m_mcu_reset_count{0};
};
} // namespace charge_bridge

View File

@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/tun_tap/tap_client.hpp>
#include <everest/io/udp/udp_client.hpp>
namespace charge_bridge {
struct plc_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::string plc_tap;
std::string plc_ip;
std::string plc_netmaks;
int plc_mtu;
};
class plc_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
plc_bridge(plc_bridge_config const& config);
~plc_bridge() = default;
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_timer_event();
everest::lib::io::tun_tap::tap_client m_tap;
everest::lib::io::udp::udp_client m_udp;
everest::lib::io::event::timer_fd m_timer;
bool m_udp_on_error{false};
bool m_tap_on_error{false};
};
} // namespace charge_bridge

View File

@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/utilities/symlink.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/serial/event_pty.hpp>
#include <everest/io/tcp/tcp_client.hpp>
namespace charge_bridge {
struct serial_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::string serial_device;
};
class serial_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
serial_bridge(serial_bridge_config const& config);
~serial_bridge() = default;
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
std::string get_slave_path();
private:
void reset_tcp();
everest::lib::io::serial::event_pty m_pty;
everest::lib::io::tcp::tcp_client m_tcp;
utilities::symlink m_symlink;
int m_tcp_last_error_id = -1;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <functional>
#include <vector>
namespace fs = std::filesystem;
namespace charge_bridge::filesystem_utils {
bool read_from_file_partial(const fs::path& file_path, const std::size_t byte_count, std::string& out_data);
bool read_from_file(const fs::path& file_path, std::string& out_data);
/// @brief Process the file in chunks with the provided function. If the process function
/// returns false, this function will also immediately return
/// @return True if the file was properly opened false otherwise
bool process_file(const fs::path& file_path, std::size_t buffer_size,
std::function<bool(const std::vector<std::uint8_t>&, bool last_chunk)>&& func);
bool process_file(std::ifstream& file, std::size_t buffer_size,
std::function<bool(const std::vector<std::uint8_t>&, bool last_chunk)>&& func);
struct CryptSignedHeader {
std::string firmware_version; // max 32 bytes long string describing the fw version
std::uint8_t sig_len = 0;
std::vector<std::uint8_t> signature; // length = sig_len
std::uint8_t num_sectors = 0; // global one-byte value
std::array<std::uint8_t, 16> iv{}; // 16-byte IV from file #2
};
bool read_crypt_signed_header(const fs::path& path, CryptSignedHeader& hdr, std::uint32_t& image_offset);
} // namespace charge_bridge::filesystem_utils

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iostream>
namespace charge_bridge::utilities {
std::ostream& print_error(std::string const& device, std::string const& unit, int status);
}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/charge_bridge.hpp>
namespace charge_bridge::utilities {
std::vector<charge_bridge_config> parse_config_multi(std::string const& config_file);
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <cstring>
#include <vector>
namespace charge_bridge::utilities {
// Converts a struct to raw bytes
template <typename T> static inline void struct_to_vector(const T& data_struct, std::vector<std::uint8_t>& buffer) {
static constexpr auto struct_size = sizeof(T);
buffer.resize(struct_size);
std::memcpy(buffer.data(), &data_struct, struct_size);
}
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <protocol/cb_config.h>
#include <string>
namespace charge_bridge::utilities {
std::string to_string(CbCanBaudrate value);
std::string to_string(CbUartBaudrate value);
std::string to_string(CbUartParity value);
std::string to_string(CbUartStopbits value);
std::string to_string(CbUartConfig const& value);
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <set>
#include <string>
namespace charge_bridge::utilities {
bool string_starts_with(std::string_view const& str, std::string_view const& pattern);
bool string_ends_with(std::string const& str, std::string const& pattern);
std::string string_after_pattern(std::string_view const& str, std::string_view const& pattern);
std::string& replace_all_in_place(std::string& source, std::string const& placeholder, std::string const& substitute);
std::string replace_all(std::string const& source, std::string const& placeholder, std::string const& substitute);
std::set<std::string> csv_to_set(std::string const& str);
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
namespace charge_bridge::utilities {
class symlink {
public:
symlink(std::string const& src, std::string const& tar);
symlink();
bool set_link(std::string const& src, std::string const& tar);
bool del_link();
~symlink();
private:
std::string m_tar;
};
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "everest/io/udp/udp_payload.hpp"
#include <everest/io/event/fd_event_handler.hpp>
#include <everest/io/udp/udp_socket.hpp>
#include <optional>
namespace charge_bridge::utilities {
class sync_udp_client {
public:
using udp_payload = everest::lib::io::udp::udp_payload;
using reply = std::optional<udp_payload>;
sync_udp_client(std::string const& remote, std::uint16_t port);
sync_udp_client(std::string const& remote, std::uint16_t port, std::uint16_t retries, std::uint16_t timeout_ms);
reply request_reply(udp_payload const& payload);
reply request_reply(udp_payload const& payload, std::uint16_t timeout_ms, std::uint16_t retries);
bool tx(udp_payload const& payload);
reply rx();
reply rx(std::uint16_t timeout_ms);
bool is_open();
private:
void init(std::string const& remote, std::uint16_t port);
void clear_socket();
std::uint16_t m_retries;
std::uint16_t m_timeout_ms;
everest::lib::io::udp::udp_client_socket m_udp;
everest::lib::io::event::fd_event_handler m_handler;
};
} // namespace charge_bridge::utilities

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <everest_api_types/evse_board_support/API.hpp>
#include <protocol/cb_config.h>
#include <protocol/cb_management.h>
#include <ryml.hpp>
#include <string>
namespace charge_bridge::utilities {
class yml_node_error {
public:
yml_node_error(c4::yml::ConstNodeRef node);
yml_node_error(c4::yml::ConstNodeRef node, std::string const& msg);
c4::yml::ConstNodeRef m_node;
std::string m_msg;
};
namespace EXT_API = everest::lib::API;
namespace EXT_API_BSP = EXT_API::V1_0::types::evse_board_support;
bool decode_CbGpioMode(c4::yml::ConstNodeRef const& node, CbGpioMode& rhs);
bool decode_CbGpioPulls(c4::yml::ConstNodeRef const& node, CbGpioPulls& rhs);
bool decode_CbUartBaudrate(c4::yml::ConstNodeRef const& node, CbUartBaudrate& rhs);
bool decode_CbUartStopbits(c4::yml::ConstNodeRef const& node, CbUartStopbits& rhs);
bool decode_CbUartParity(c4::yml::ConstNodeRef const& node, CbUartParity& rhs);
bool decode_CbCanBaudrate(c4::yml::ConstNodeRef const& node, CbCanBaudrate& rhs);
bool decode_CbRelayMode(c4::yml::ConstNodeRef const& node, CbRelayMode& rhs);
bool decode_CbSafetyMode(c4::yml::ConstNodeRef const& node, CbSafetyMode& rhs);
bool decode_RelayConfig(c4::yml::ConstNodeRef const& node, RelayConfig& rhs);
bool decode_SafetyConfig(c4::yml::ConstNodeRef const& node, SafetyConfig& rhs);
bool decode_CbGpioConfig(c4::yml::ConstNodeRef const& node, CbGpioConfig& rhs);
bool decode_CbUartConfig(c4::yml::ConstNodeRef const& node, CbUartConfig& rhs);
bool decode_CbCanConfig(c4::yml::ConstNodeRef const& node, CbCanConfig& rhs);
bool decode_CbNetworkConfig(c4::yml::ConstNodeRef const& node, CbNetworkConfig& rhs);
bool decode_Connector_type(c4::yml::ConstNodeRef const& node, EXT_API_BSP::Connector_type& rhs);
bool decode_HardwareCapabilities(c4::yml::ConstNodeRef const& node, EXT_API_BSP::HardwareCapabilities& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbGpioMode& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbGpioPulls& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbUartBaudrate& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbUartStopbits& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbUartParity& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbCanBaudrate& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbRelayMode& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbSafetyMode& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, RelayConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, SafetyConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbGpioConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbUartConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbCanConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, CbNetworkConfig& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, EXT_API_BSP::Connector_type& rhs);
c4::yml::ConstNodeRef const& operator>>(c4::yml::ConstNodeRef const& node, EXT_API_BSP::HardwareCapabilities& rhs);
} // namespace charge_bridge::utilities