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,38 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
add_subdirectory(tida_010939_comms)
target_include_directories(${MODULE_NAME}
PRIVATE
"common"
"tida_010939_comms"
"tida_010939_comms/nanopb"
"tida_010939_comms/protobuf"
)
target_link_libraries(${MODULE_NAME}
PRIVATE
Pal::Sigslot
tida_010939_comms
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"board_support/evse_board_supportImpl.cpp"
"rcd/ac_rcdImpl.cpp"
"connector_lock/connector_lockImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# install(FILES firmware.bin DESTINATION ${CMAKE_INSTALL_DATADIR}/everest/modules/TIDA010939/firmware)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,95 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "TIDA010939.hpp"
#include <fmt/core.h>
#include <utils/date.hpp>
namespace module {
void TIDA010939::init() {
// initialize serial driver
if (!serial.openDevice(config.serial_port.c_str(), config.baud_rate)) {
EVLOG_error << "Could not open serial port " << config.serial_port << " with baud rate " << config.baud_rate;
return;
}
invoke_init(*p_board_support);
invoke_init(*p_connector_lock);
invoke_init(*p_rcd);
}
void TIDA010939::ready() {
serial.run();
if (!serial.reset(config.reset_gpio_chip, config.reset_gpio)) {
EVLOG_error << "TIDA010939 reset not successful.";
}
serial.signalSpuriousReset.connect([this]() { EVLOG_error << "TIDA010939 uC spurious reset!"; });
serial.signalConnectionTimeout.connect([this]() { EVLOG_error << "TIDA010939 UART timeout!"; });
invoke_ready(*p_board_support);
invoke_ready(*p_connector_lock);
invoke_ready(*p_rcd);
serial.signalErrorFlags.connect([this](ErrorFlags e) { error_handling(e); });
if (not serial.is_open()) {
auto err = p_board_support->error_factory->create_error("evse_board_support/CommunicationFault", "",
"Could not open serial port.");
p_board_support->raise_error(err);
}
}
void TIDA010939::clear_errors_on_unplug() {
if (error_MREC2GroundFailure) {
p_board_support->clear_error("evse_board_support/MREC2GroundFailure");
}
error_MREC2GroundFailure = false;
if (error_MREC1ConnectorLockFailure) {
p_connector_lock->clear_error("connector_lock/MREC1ConnectorLockFailure");
}
error_MREC1ConnectorLockFailure = false;
}
void TIDA010939::error_handling(ErrorFlags e) {
if (e.diode_fault and not last_error_flags.diode_fault) {
Everest::error::Error error_object = p_board_support->error_factory->create_error(
"evse_board_support/DiodeFault", "", "Diode Fault", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.diode_fault and last_error_flags.diode_fault) {
p_board_support->clear_error("evse_board_support/DiodeFault");
}
if (e.ventilation_not_available and not last_error_flags.ventilation_not_available) {
Everest::error::Error error_object =
p_board_support->error_factory->create_error("evse_board_support/VentilationNotAvailable", "",
"State D is not supported", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.ventilation_not_available and last_error_flags.ventilation_not_available) {
p_board_support->clear_error("evse_board_support/VentilationNotAvailable");
}
if (e.connector_lock_failed and not last_error_flags.connector_lock_failed) {
Everest::error::Error error_object = p_connector_lock->error_factory->create_error(
"connector_lock/MREC1ConnectorLockFailure", "", "Lock motor failure", Everest::error::Severity::High);
error_MREC1ConnectorLockFailure = true;
p_connector_lock->raise_error(error_object);
}
if (e.cp_signal_fault and not last_error_flags.cp_signal_fault) {
Everest::error::Error error_object = p_board_support->error_factory->create_error(
"evse_board_support/MREC14PilotFault", "", "CP error", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.cp_signal_fault and last_error_flags.cp_signal_fault) {
p_board_support->clear_error("evse_board_support/MREC14PilotFault");
}
last_error_flags = e;
}
} // namespace module

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef TIDA010939_HPP
#define TIDA010939_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/ac_rcd/Implementation.hpp>
#include <generated/interfaces/connector_lock/Implementation.hpp>
#include <generated/interfaces/evse_board_support/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
#include "tida_010939_comms/evSerial.h"
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string serial_port;
int baud_rate;
std::string reset_gpio_chip;
int reset_gpio;
int max_current_A_import;
int min_current_A_import;
int min_phase_count_import;
int max_phase_count_import;
int min_current_A_export;
int max_current_A_export;
int min_phase_count_export;
int max_phase_count_export;
bool has_socket;
};
class TIDA010939 : public Everest::ModuleBase {
public:
TIDA010939() = delete;
TIDA010939(const ModuleInfo& info, std::unique_ptr<evse_board_supportImplBase> p_board_support,
std::unique_ptr<ac_rcdImplBase> p_rcd, std::unique_ptr<connector_lockImplBase> p_connector_lock,
Conf& config) :
ModuleBase(info),
p_board_support(std::move(p_board_support)),
p_rcd(std::move(p_rcd)),
p_connector_lock(std::move(p_connector_lock)),
config(config){};
const std::unique_ptr<evse_board_supportImplBase> p_board_support;
const std::unique_ptr<ac_rcdImplBase> p_rcd;
const std::unique_ptr<connector_lockImplBase> p_connector_lock;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
void publish_external_telemetry_livedata(const std::string& topic, const Everest::TelemetryMap& data);
evSerial serial;
void clear_errors_on_unplug();
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
void error_handling(ErrorFlags e);
ErrorFlags last_error_flags;
std::atomic_bool error_MREC2GroundFailure{false};
std::atomic_bool error_MREC1ConnectorLockFailure{false};
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
Everest::json keep_alive_lo_to_json(const KeepAliveLo& k);
std::string error_type_to_string(ErrorFlags s);
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // TIDA010939_HPP

View File

@@ -0,0 +1,187 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "evse_board_supportImpl.hpp"
namespace module {
namespace board_support {
static types::board_support_common::BspEvent cast_event_type(CpState cp_state) {
types::board_support_common::BspEvent event;
switch (cp_state) {
case CpState_STATE_A:
event.event = types::board_support_common::Event::A;
break;
case CpState_STATE_B:
event.event = types::board_support_common::Event::B;
break;
case CpState_STATE_C:
event.event = types::board_support_common::Event::C;
break;
case CpState_STATE_D:
event.event = types::board_support_common::Event::D;
break;
case CpState_STATE_E:
event.event = types::board_support_common::Event::E;
break;
case CpState_STATE_F:
event.event = types::board_support_common::Event::F;
break;
}
return event;
}
static types::board_support_common::BspEvent cast_event_type(bool relais_state) {
types::board_support_common::BspEvent event;
if (relais_state) {
event.event = types::board_support_common::Event::PowerOn;
} else {
event.event = types::board_support_common::Event::PowerOff;
}
return event;
}
static types::board_support_common::ProximityPilot cast_pp_type(PpState pp_state) {
types::board_support_common::ProximityPilot pp;
switch (pp_state) {
case PpState_STATE_13A:
pp.ampacity = types::board_support_common::Ampacity::A_13;
break;
case PpState_STATE_20A:
pp.ampacity = types::board_support_common::Ampacity::A_20;
break;
case PpState_STATE_32A:
pp.ampacity = types::board_support_common::Ampacity::A_32;
break;
case PpState_STATE_70A:
pp.ampacity = types::board_support_common::Ampacity::A_63_3ph_70_1ph;
break;
case PpState_STATE_FAULT:
pp.ampacity = types::board_support_common::Ampacity::None;
break;
case PpState_STATE_NC:
pp.ampacity = types::board_support_common::Ampacity::None;
break;
}
return pp;
}
void evse_board_supportImpl::init() {
{
std::lock_guard<std::mutex> lock(capsMutex);
caps.min_current_A_import = mod->config.min_current_A_import;
caps.max_current_A_import = mod->config.max_current_A_import;
caps.min_phase_count_import = mod->config.min_phase_count_import;
caps.max_phase_count_import = mod->config.max_phase_count_import;
caps.supports_changing_phases_during_charging = false;
caps.supports_cp_state_E = false;
caps.min_current_A_export = mod->config.min_current_A_export;
caps.max_current_A_export = mod->config.max_current_A_export;
caps.min_phase_count_export = mod->config.min_phase_count_export;
caps.max_phase_count_export = mod->config.max_phase_count_export;
if (mod->config.has_socket) {
caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Socket;
} else {
caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable;
}
}
mod->serial.signalCPState.connect([this](CpState cp_state) {
if (cp_state not_eq last_cp_state) {
auto event_cp_state = cast_event_type(cp_state);
EVLOG_info << "CP state changed: "
<< types::board_support_common::event_to_string(cast_event_type(last_cp_state).event) << " -> "
<< types::board_support_common::event_to_string(event_cp_state.event);
if (enabled) {
publish_event(event_cp_state);
}
if (cp_state == CpState_STATE_A) {
mod->clear_errors_on_unplug();
}
last_cp_state = cp_state;
}
});
mod->serial.signalRelaisState.connect([this](bool relais_state) {
if (last_relais_state not_eq relais_state) {
publish_event(cast_event_type(relais_state));
last_relais_state = relais_state;
}
});
mod->serial.signalPPState.connect([this](PpState pp_state) {
last_pp = cast_pp_type(pp_state);
publish_ac_pp_ampacity(last_pp);
});
mod->serial.signalKeepAliveLo.connect([this](KeepAliveLo l) {
if (not caps_received) {
EVLOG_info << "TIDA-010939 Controller Configuration:";
EVLOG_info << " Hardware revision: " << l.hw_revision;
EVLOG_info << " Firmware version: " << l.sw_version_string;
EVLOG_info << " Current Limit: " << l.hwcap_max_current;
}
caps_received = true;
});
}
void evse_board_supportImpl::ready() {
wait_for_caps();
{
// Publish caps once in the beginning
std::lock_guard<std::mutex> lock(capsMutex);
publish_capabilities(caps);
}
}
void evse_board_supportImpl::wait_for_caps() {
// Wait for caps to be received at least once
int i;
for (i = 0; i < 50; i++) {
if (caps_received)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (i == 50) {
EVLOG_error << "Did not receive hardware capabilities from TIDA-010939 hardware, using defaults.";
}
}
void evse_board_supportImpl::handle_pwm_on(double& value) {
mod->serial.setPWM(value * 100);
}
void evse_board_supportImpl::handle_cp_state_X1() {
mod->serial.setPWM(10001);
}
void evse_board_supportImpl::handle_cp_state_F() {
mod->serial.setPWM(0);
}
void evse_board_supportImpl::handle_cp_state_E() {
EVLOG_warning << "Command cp_state_E is not supported. Ignoring command.";
}
void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) {
mod->serial.allowPowerOn(value.allow_power_on);
}
void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) {
// your code for cmd ac_set_overcurrent_limit_A goes here
}
void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) {
}
void evse_board_supportImpl::handle_enable(bool& value) {
enabled = true;
// Publish CP state once on enable
publish_event(cast_event_type(last_cp_state));
}
} // namespace board_support
} // namespace module

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
#define BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/evse_board_support/Implementation.hpp>
#include "../TIDA010939.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace board_support {
struct Conf {};
class evse_board_supportImpl : public evse_board_supportImplBase {
public:
evse_board_supportImpl() = delete;
evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<TIDA010939>& mod, Conf& config) :
evse_board_supportImplBase(ev, "board_support"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_enable(bool& value) override;
virtual void handle_pwm_on(double& value) override;
virtual void handle_cp_state_X1() override;
virtual void handle_cp_state_F() override;
virtual void handle_cp_state_E() override;
virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override;
virtual void handle_ac_switch_three_phases_while_charging(bool& value) override;
virtual void handle_ac_set_overcurrent_limit_A(double& value) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<TIDA010939>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
types::evse_board_support::HardwareCapabilities caps;
std::atomic_bool caps_received{false};
std::mutex capsMutex;
CpState last_cp_state{CpState::CpState_STATE_F};
bool last_relais_state{false};
types::board_support_common::ProximityPilot last_pp{types::board_support_common::Ampacity::None};
void wait_for_caps();
std::atomic_bool enabled{false};
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace board_support
} // namespace module
#endif // BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "connector_lockImpl.hpp"
namespace module {
namespace connector_lock {
void connector_lockImpl::init() {
mod->serial.signalLockState.connect([this](bool l) { lock_state = l; });
}
void connector_lockImpl::ready() {
}
void connector_lockImpl::handle_lock() {
mod->serial.lock();
}
void connector_lockImpl::handle_unlock() {
mod->serial.forceUnlock();
}
} // namespace connector_lock
} // namespace module

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP
#define CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/connector_lock/Implementation.hpp>
#include "../TIDA010939.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace connector_lock {
struct Conf {};
class connector_lockImpl : public connector_lockImplBase {
public:
connector_lockImpl() = delete;
connector_lockImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<TIDA010939>& mod, Conf& config) :
connector_lockImplBase(ev, "connector_lock"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_lock() override;
virtual void handle_unlock() override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<TIDA010939>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
std::atomic_bool lock_state{false};
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace connector_lock
} // namespace module
#endif // CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP

View File

@@ -0,0 +1,66 @@
.. _everest_modules_handwritten_TIDA010939:
.. **********
.. TIDA010939
.. **********
The module ``TIDA010939`` is a board support driver for Texas Instruments
TIDA-010939 reference design. It is based on the Yeti driver with similar structure
and functionality.
Communication between the TIDA010939 microcontroller and this driver module
============================================================================
The hardware connection between TIDA010939 and the host system (the board running EVerest and
this module) is 3.3V TTL UART plus 2 GPIOs (one to reset the microcontroller
from Linux and one to boot into the bootloader).
The default configuration is 115200 bps 8N1.
Protocol
========
EVerest can send commands to TIDA010939 and TIDA010939 publishes data and events back
to EVerest. The packets are defined with protobuf to serialize the C structs
into a binary representation that is transferred over the serial wire in a
stream:
https://developers.google.com/protocol-buffers
To be able to split the stream back into packets all data is encoded using COBS
before it is transmitted on the UART:
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
COBS
----
COBS is implemented in ``tida_010939_comms/evSerial.cpp``. Whenever a new packet
was extracted from the stream ``handlePacket()`` is called to decode protobuf
and generate the corresponding signals.
Other parts of the module subscribe to these signals to handle the incoming
packets.
For TX ``linkWrite`` encodes the packet with COBS and outputs it to the UART.
Protobuf
--------
The actual packet definitions are located under ``tida_010939_comms/protobuf``.
``tida_010939.proto`` contains all messages that can be sent from EVerest to TIDA010939 and
all messages that TIDA010939 sends to EVerest.
Refer to these files for an up to date definition as they may change
frequently.
To generate the C code nanopb is used:
``nanopb_generator -I . -D . *.proto``
The output should also be manually copied to TIDA010939 Firmware to ensure the same
definition is used on both sides when making changes.
References
============
`Official website https://www.ti.com/tool/TIDA-010939 <https://www.ti.com/tool/TIDA-010939>`_

View File

@@ -0,0 +1,89 @@
description: Driver module for the Texas Instruments TIDA-010939 reference design
config:
serial_port:
description: Serial port the TIDA010939 hardware is connected to
type: string
default: /dev/ttyUSB0
baud_rate:
description: Serial baud rate to use when communicating with TIDA010939 hardware
type: integer
minimum: 9600
maximum: 230400
default: 115200
reset_gpio_chip:
description: >-
Reset GPIO chip to use to HW reset TIDA010939. If set to empty string, it is disabled.
type: string
default: 'gpiochip0'
reset_gpio:
description: GPIO line to use to reset TIDA010939
type: integer
default: 27
max_current_A_import:
description: Maximum import current in amps
type: integer
minimum: 0
default: 16
min_current_A_import:
description: Minimum import current in amps
type: integer
minimum: 0
default: 6
min_phase_count_import:
description: Minimum phase count for import
type: integer
minimum: 1
maximum: 3
default: 3
max_phase_count_import:
description: Maximum phase count for import
type: integer
minimum: 1
maximum: 3
default: 3
min_current_A_export:
description: Minimum export current in amps
type: integer
minimum: 0
maximum: 63
default: 0
max_current_A_export:
description: Maximum export current in amps
type: integer
minimum: 0
maximum: 63
default: 0
min_phase_count_export:
description: Minimum phase count for export
type: integer
minimum: 1
maximum: 3
default: 3
max_phase_count_export:
description: Maximum phase count for export
type: integer
minimum: 1
maximum: 3
default: 3
has_socket:
description: Set to true if it has a socket, false if it has a permanently attached cable
type: boolean
default: false
provides:
board_support:
interface: evse_board_support
description: provides the board support Interface to low level control control pilot, relais, motor lock
rcd:
interface: ac_rcd
description: RCD interface of the onboard RCD
connector_lock:
interface: connector_lock
description: Interface for the motor lock
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen
- Kai-Uwe Hermann
- Thilo Molitor
- Anton Wöllert
- Fabian Gajek

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ac_rcdImpl.hpp"
namespace module {
namespace rcd {
void ac_rcdImpl::init() {
mod->serial.signalErrorFlags.connect([this](ErrorFlags error_flags) {
if (error_flags.rcd_triggered and not last_error_flags.rcd_triggered) {
Everest::error::Error error_object =
this->error_factory->create_error("ac_rcd/DC", "", "RDC-MD triggered", Everest::error::Severity::High);
this->raise_error(error_object);
} else if (not error_flags.rcd_triggered and last_error_flags.rcd_triggered) {
this->clear_error("ac_rcd/DC");
}
if (error_flags.rcd_selftest_failed and not last_error_flags.rcd_selftest_failed) {
Everest::error::Error error_object = this->error_factory->create_error(
"ac_rcd/Selftest", "", "RCD self-test failed", Everest::error::Severity::High);
this->raise_error(error_object);
} else if (not error_flags.rcd_selftest_failed and last_error_flags.rcd_selftest_failed) {
this->clear_error("ac_rcd/Selftest");
}
last_error_flags = error_flags;
});
}
void ac_rcdImpl::ready() {
}
void ac_rcdImpl::handle_self_test() {
mod->serial.set_rcd_test(true);
}
bool ac_rcdImpl::handle_reset() {
mod->serial.reset_rcd(true);
return true;
}
} // namespace rcd
} // namespace module

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef RCD_AC_RCD_IMPL_HPP
#define RCD_AC_RCD_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ac_rcd/Implementation.hpp>
#include "../TIDA010939.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace rcd {
struct Conf {};
class ac_rcdImpl : public ac_rcdImplBase {
public:
ac_rcdImpl() = delete;
ac_rcdImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<TIDA010939>& mod, Conf& config) :
ac_rcdImplBase(ev, "rcd"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_self_test() override;
virtual bool handle_reset() override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<TIDA010939>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
ErrorFlags last_error_flags;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace rcd
} // namespace module
#endif // RCD_AC_RCD_IMPL_HPP

View File

@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.10)
# set the project name
project(tida_010939_comms VERSION 0.1)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# add the executable
add_library(tida_010939_comms STATIC)
ev_register_library_target(tida_010939_comms)
target_sources(tida_010939_comms
PRIVATE
evSerial.cpp
protobuf/tida010939.pb.c
)
target_include_directories(tida_010939_comms
PUBLIC
"${PROJECT_BINARY_DIR}"
protobuf
)
target_link_libraries(tida_010939_comms
PUBLIC
date::date-tz
everest::nanopb
PRIVATE
Pal::Sigslot
everest::framework
everest::gpio
)

View File

@@ -0,0 +1,437 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "evSerial.h"
#include <cerrno>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <fcntl.h>
#include <unistd.h>
#include <date/date.h>
#include <date/tz.h>
#include <everest/3rd_party/nanopb/pb_decode.h>
#include <everest/3rd_party/nanopb/pb_encode.h>
#include <everest/gpio/gpio.hpp>
#include "tida010939.pb.h"
evSerial::evSerial() {
fd = 0;
baud = 0;
reset_done_flag = false;
forced_reset = false;
cobsDecodeReset();
}
evSerial::~evSerial() {
if (fd)
close(fd);
}
bool evSerial::openDevice(const char* device, int _baud) {
fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Serial: error %d opening %s: %s\n", errno, device, strerror(errno));
return false;
} // else printf ("Serial: opened %s as %i\n", device, fd);
cobsDecodeReset();
switch (_baud) {
case 9600:
baud = B9600;
break;
case 19200:
baud = B19200;
break;
case 38400:
baud = B38400;
break;
case 57600:
baud = B57600;
break;
case 115200:
baud = B115200;
break;
case 230400:
baud = B230400;
break;
default:
baud = 0;
return false;
}
return setSerialAttributes();
}
bool evSerial::setSerialAttributes() {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
printf("Serial: error %d from tcgetattr\n", errno);
return false;
}
cfsetospeed(&tty, baud);
cfsetispeed(&tty, baud);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read blocks
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Serial: error %d from tcsetattr\n", errno);
return false;
}
// printf ("Success setting tcsetattr\n");
return true;
}
void evSerial::cobsDecodeReset() {
code = 0xff;
block = 0;
decode = msg;
}
uint32_t evSerial::crc32(uint8_t* buf, int len) {
int i, j;
uint32_t b, crc, msk;
i = 0;
crc = 0xFFFFFFFF;
while (i < len) {
b = buf[i];
crc = crc ^ b;
for (j = 7; j >= 0; j--) {
msk = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & msk);
}
i = i + 1;
}
// printf("%X",crc);
return crc;
}
void evSerial::handlePacket(uint8_t* buf, int len) {
// printf ("packet received len %u\n", len);
// Check CRC32 (last 4 bytes)
// uint32_t crc = calculateCrc(rx_packet_buf, rx_packet_len);
if (crc32(buf, len)) {
printf("CRC mismatch\n");
// hexdump the data
for (int i = 0; i < len; i++) {
printf("%02X ", buf[i]);
}
printf("\n");
return;
}
len -= 4;
McuToEverest msg_in;
pb_istream_t istream = pb_istream_from_buffer(buf, len);
if (pb_decode(&istream, McuToEverest_fields, &msg_in)) {
// printf("Decoded message with type %d\n", msg_in.which_payload);
switch (msg_in.which_payload) {
case McuToEverest_telemetry_tag:
// printf("Received telemetry\n");
// printf("cp_voltage_hi: %f\n", msg_in.payload.telemetry.cp_voltage_hi);
// printf("cp_voltage_lo: %f\n", msg_in.payload.telemetry.cp_voltage_lo);
// printf("temp0: %f\n", msg_in.payload.telemetry.temp0);
// printf("temp1: %f\n", msg_in.payload.telemetry.temp1);
break;
case McuToEverest_keep_alive_tag:
// printf("Received keep_alive_lo\n");
signalKeepAliveLo(msg_in.payload.keep_alive);
// detect connection timeout if keep_alive packets stop coming...
last_keep_alive_lo_timestamp = date::utc_clock::now();
break;
case McuToEverest_cp_state_tag:
signalCPState(msg_in.payload.cp_state);
break;
case McuToEverest_pp_state_tag:
signalPPState(msg_in.payload.pp_state);
break;
case McuToEverest_relais_state_tag:
signalRelaisState(msg_in.payload.relais_state);
break;
case McuToEverest_lock_state_tag:
signalLockState(msg_in.payload.lock_state);
break;
case McuToEverest_error_flags_tag:
signalErrorFlags(msg_in.payload.error_flags);
break;
case McuToEverest_reset_tag:
// printf("Received reset_done\n");
reset_done_flag = true;
if (!forced_reset)
signalSpuriousReset();
break;
}
} else {
printf("Error decoding message: %s\n", PB_GET_ERROR(&istream));
}
}
void evSerial::cobsDecode(uint8_t* buf, int len) {
for (int i = 0; i < len; i++)
cobsDecodeByte(buf[i]);
}
void evSerial::cobsDecodeByte(uint8_t byte) {
// check max length
if ((decode - msg == 2048 - 1) && byte != 0x00) {
printf("cobsDecode: Buffer overflow\n");
cobsDecodeReset();
}
if (block) {
// we're currently decoding and should not get a 0
if (byte == 0x00) {
// probably found some garbage -> reset
printf("cobsDecode: Garbage detected\n");
cobsDecodeReset();
return;
}
*decode++ = byte;
} else {
if (code != 0xff) {
*decode++ = 0;
}
block = code = byte;
if (code == 0x00) {
// we're finished, reset everything and commit
if (decode == msg) {
// we received nothing, just a 0x00
printf("cobsDecode: Received nothing\n");
} else {
// set back decode with one, as it gets post-incremented
handlePacket(msg, decode - 1 - msg);
}
cobsDecodeReset();
return; // need to return here, because of block--
}
}
block--;
}
void evSerial::run() {
readThreadHandle = std::thread(&evSerial::readThread, this);
timeoutDetectionThreadHandle = std::thread(&evSerial::timeoutDetectionThread, this);
}
void evSerial::timeoutDetectionThread() {
while (true) {
sleep(1);
if (timeoutDetectionThreadHandle.shouldExit())
break;
if (serial_timed_out())
signalConnectionTimeout();
}
}
void evSerial::readThread() {
uint8_t buf[2048];
int n;
cobsDecodeReset();
while (true) {
if (readThreadHandle.shouldExit())
break;
if (fd > 0) {
n = read(fd, buf, sizeof buf);
cobsDecode(buf, n);
}
}
}
bool evSerial::linkWrite(EverestToMcu* m) {
if (fd <= 0) {
return false;
}
uint8_t tx_packet_buf[1024];
uint8_t encode_buf[1500];
pb_ostream_t ostream = pb_ostream_from_buffer(tx_packet_buf, sizeof(tx_packet_buf) - 4);
bool status = pb_encode(&ostream, EverestToMcu_fields, m);
if (!status) {
// couldn't encode
return false;
}
size_t tx_payload_len = ostream.bytes_written;
// add crc32 (CRC-32/JAMCRC)
uint32_t crc = crc32(tx_packet_buf, tx_payload_len);
for (int byte_pos = 0; byte_pos < 4; ++byte_pos) {
tx_packet_buf[tx_payload_len] = (uint8_t)crc & 0xFF;
crc = crc >> 8;
tx_payload_len++;
}
size_t tx_encode_len = cobsEncode(tx_packet_buf, tx_payload_len, encode_buf);
// std::cout << "Write "<<tx_encode_len<<" bytes to serial port." << std::endl;
write(fd, encode_buf, tx_encode_len);
return true;
}
size_t evSerial::cobsEncode(const void* data, size_t length, uint8_t* buffer) {
uint8_t* encode = buffer; // Encoded byte pointer
uint8_t* codep = encode++; // Output code pointer
uint8_t code = 1; // Code value
for (const uint8_t* byte = (const uint8_t*)data; length--; ++byte) {
if (*byte) // Byte not zero, write it
*encode++ = *byte, ++code;
if (!*byte || code == 0xff) // Input is zero or block completed, restart
{
*codep = code, code = 1, codep = encode;
if (!*byte || length)
++encode;
}
}
*codep = code; // Write final code value
// add final 0
*encode++ = 0x00;
return encode - buffer;
}
bool evSerial::serial_timed_out() {
auto now = date::utc_clock::now();
auto timeSinceLastKeepAlive =
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_keep_alive_lo_timestamp).count();
if (timeSinceLastKeepAlive >= 5000)
return true;
return false;
}
void evSerial::setPWM(uint32_t dc) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_pwm_duty_cycle_tag;
msg_out.payload.pwm_duty_cycle = dc;
linkWrite(&msg_out);
}
void evSerial::allowPowerOn(bool p) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_allow_power_on_tag;
msg_out.payload.allow_power_on = p;
linkWrite(&msg_out);
}
void evSerial::lock() {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_connector_lock_tag;
msg_out.payload.connector_lock = true;
linkWrite(&msg_out);
}
void evSerial::forceUnlock() {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_connector_lock_tag;
msg_out.payload.connector_lock = false;
linkWrite(&msg_out);
}
void evSerial::set_rcd_test(bool test) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_rcd_cmd_tag;
msg_out.payload.rcd_cmd.test = test;
msg_out.payload.rcd_cmd.reset = false; // reset is false for test command
linkWrite(&msg_out);
}
void evSerial::reset_rcd(bool reset) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_rcd_cmd_tag;
msg_out.payload.rcd_cmd.test = false; // test is false for reset command
msg_out.payload.rcd_cmd.reset = reset;
linkWrite(&msg_out);
}
bool evSerial::reset(const std::string& reset_chip, const int reset_line) {
reset_done_flag = false;
forced_reset = true;
if (not reset_chip.empty()) {
// Try to hardware reset TIDA-010939 controller to be in a known state
printf("Resetting TIDA-010939 controller via GPIO %s:%d\n", reset_chip.c_str(), reset_line);
Everest::Gpio reset_gpio;
reset_gpio.open(reset_chip, reset_line);
reset_gpio.set_output(true);
reset_gpio.set(false);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
reset_gpio.set(true);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
reset_gpio.set(false);
} else {
// Try to soft reset TIDA-010939 controller to be in a known state
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_reset_tag;
linkWrite(&msg_out);
}
bool success = false;
// Wait for reset done message from uC
for (int i = 0; i < 20; i++) {
if (reset_done_flag) {
success = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// Reset flag to detect run time spurious resets of uC from now on
reset_done_flag = false;
forced_reset = false;
// send some dummy packets to resync COBS etc.
keepAlive();
keepAlive();
keepAlive();
return success;
}
void evSerial::keepAlive() {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_keep_alive_tag;
msg_out.payload.keep_alive.time_stamp = 0;
msg_out.payload.keep_alive.hw_type = 0;
msg_out.payload.keep_alive.hw_revision = 0;
strcpy(msg_out.payload.keep_alive.sw_version_string, "n/a");
linkWrite(&msg_out);
}

View File

@@ -0,0 +1,80 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef TIDA_010939_SERIAL
#define TIDA_010939_SERIAL
#include "tida010939.pb.h"
#include <date/date.h>
#include <date/tz.h>
#include <sigslot/signal.hpp>
#include <stdint.h>
#include <termios.h>
#include <utils/thread.hpp>
class evSerial {
public:
evSerial();
~evSerial();
bool openDevice(const char* device, int baud);
bool is_open() {
return fd > 0;
};
void readThread();
void run();
bool reset(const std::string& reset_chip, const int reset_line);
// void firmwareUpdate(bool rom); // Firmware update functionality removed for TIDA010939
void keepAlive();
void setPWM(uint32_t dc);
void allowPowerOn(bool p);
void lock();
void forceUnlock();
void set_rcd_test(bool test);
void reset_rcd(bool reset);
sigslot::signal<KeepAliveLo> signalKeepAliveLo;
sigslot::signal<CpState> signalCPState;
sigslot::signal<PpState> signalPPState;
sigslot::signal<ErrorFlags> signalErrorFlags;
sigslot::signal<bool> signalRelaisState;
sigslot::signal<bool> signalLockState;
sigslot::signal<> signalSpuriousReset;
sigslot::signal<> signalConnectionTimeout;
private:
// Serial interface
bool setSerialAttributes();
int fd;
int baud;
// COBS de-/encoder
void cobsDecodeReset();
void handlePacket(uint8_t* buf, int len);
void cobsDecode(uint8_t* buf, int len);
void cobsDecodeByte(uint8_t byte);
size_t cobsEncode(const void* data, size_t length, uint8_t* buffer);
uint8_t msg[2048];
uint8_t code;
uint8_t block;
uint8_t* decode;
uint32_t crc32(uint8_t* buf, int len);
// Read thread for serial port
Everest::Thread readThreadHandle;
Everest::Thread timeoutDetectionThreadHandle;
bool linkWrite(EverestToMcu* m);
volatile bool reset_done_flag;
volatile bool forced_reset;
bool serial_timed_out();
void timeoutDetectionThread();
std::chrono::time_point<date::utc_clock> last_keep_alive_lo_timestamp;
};
#endif

View File

@@ -0,0 +1,126 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef TIDA_010939_FIRMWARE_VERSION
#define TIDA_010939_FIRMWARE_VERSION
#include <regex>
#include <string>
/*
Helper class to handle TIDA-010939 Firmware Versions of the following style:
Reported by MCU: 2.0-1-g2d51638
Included in file name: TIDA-010939_2.0-1_firmware.bin
*/
static std::vector<std::string> split_by_delimeters(const std::string& s, const std::string& delimeters) {
std::regex re("[" + delimeters + "]");
std::sregex_token_iterator first{s.begin(), s.end(), re, -1}, last;
return {first, last};
}
// parse version string from filename
static std::string from_filename(const std::string& v) {
auto tokens = split_by_delimeters(v, "_");
if (tokens.size() >= 2) {
return tokens[1];
}
return "0.0-0";
}
class TIDA010939FirmwareVersion {
public:
TIDA010939FirmwareVersion() {
}
TIDA010939FirmwareVersion(const std::string& _version_string) {
from_string(_version_string);
}
std::string to_string() {
return std::to_string(major) + "." + std::to_string(minor) + "-" + std::to_string(revision);
}
void from_string(const std::string& _version_string) {
std::string version_string;
// Is it a full filename or just a version string from the MCU?
if (_version_string.rfind("yetiR", 0) == 0) {
version_string = from_filename(_version_string);
} else {
version_string = _version_string;
}
auto tokens = split_by_delimeters(version_string, ".-");
// parse into major, minor and revision
if (tokens.size() >= 1) {
try {
major = std::stoi(tokens[0]);
} catch (...) {
// Set to 0 if we cannot parse it
major = 0;
}
}
if (tokens.size() >= 2) {
try {
minor = std::stoi(tokens[1]);
} catch (...) {
// Set to 0 if we cannot parse it
minor = 0;
}
}
if (tokens.size() >= 3) {
try {
revision = std::stoi(tokens[2]);
} catch (...) {
// Set to 0 if we cannot parse it
revision = 0;
}
}
}
TIDA010939FirmwareVersion& operator=(const std::string& s) {
from_string(s);
return *this;
}
friend bool operator<(const TIDA010939FirmwareVersion& l, const TIDA010939FirmwareVersion& r) {
if (l.major < r.major) {
return true;
} else if (l.major > r.major) {
return false;
} else if (l.minor < r.minor) {
return true;
} else if (l.minor > r.minor) {
return false;
} else if (l.revision < r.revision) {
return true;
} else if (l.revision > r.revision) {
return false;
}
// they are identical
return false;
}
friend bool operator>(const TIDA010939FirmwareVersion& lhs, const TIDA010939FirmwareVersion& rhs) {
return rhs < lhs;
}
friend bool operator<=(const TIDA010939FirmwareVersion& lhs, const TIDA010939FirmwareVersion& rhs) {
return not(lhs > rhs);
}
friend bool operator>=(const TIDA010939FirmwareVersion& lhs, const TIDA010939FirmwareVersion& rhs) {
return not(lhs < rhs);
}
private:
int major{0};
int minor{0};
int revision{0};
};
#endif

View File

@@ -0,0 +1,2 @@
#!/bin/sh
nanopb_generator.py -L "#include <everest/3rd_party/nanopb/%s>" -I . -D . tida010939.proto

View File

@@ -0,0 +1,2 @@
KeepAlive.sw_version_string max_length:50
KeepAliveLo.sw_version_string max_length:50

View File

@@ -0,0 +1,41 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.9.1 */
#include "tida010939.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(EverestToMcu, EverestToMcu, AUTO)
PB_BIND(McuToEverest, McuToEverest, AUTO)
PB_BIND(ErrorFlags, ErrorFlags, AUTO)
PB_BIND(KeepAliveLo, KeepAliveLo, AUTO)
PB_BIND(KeepAlive, KeepAlive, AUTO)
PB_BIND(Telemetry, Telemetry, AUTO)
PB_BIND(FirmwareUpdate, FirmwareUpdate, AUTO)
PB_BIND(RcdCommand, RcdCommand, AUTO)

View File

@@ -0,0 +1,340 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.9.1 */
#ifndef PB_TIDA010939_PB_H_INCLUDED
#define PB_TIDA010939_PB_H_INCLUDED
#include <everest/3rd_party/nanopb/pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
typedef enum _CpState {
CpState_STATE_A = 0,
CpState_STATE_B = 1,
CpState_STATE_C = 2,
CpState_STATE_D = 3,
CpState_STATE_E = 4,
CpState_STATE_F = 5
} CpState;
typedef enum _ResetReason {
ResetReason_USER = 0,
ResetReason_WATCHDOG = 1
} ResetReason;
typedef enum _PpState {
PpState_STATE_NC = 0,
PpState_STATE_13A = 1,
PpState_STATE_20A = 2,
PpState_STATE_32A = 3,
PpState_STATE_70A = 4,
PpState_STATE_FAULT = 5
} PpState;
typedef enum _LockState {
LockState_UNDEFINED = 0,
LockState_UNLOCKED = 1,
LockState_LOCKED = 2
} LockState;
/* Struct definitions */
typedef struct _ErrorFlags {
bool diode_fault;
bool rcd_selftest_failed;
bool rcd_triggered;
bool ventilation_not_available;
bool connector_lock_failed;
bool cp_signal_fault;
bool over_current;
} ErrorFlags;
typedef struct _KeepAliveLo {
uint32_t time_stamp;
uint32_t hw_type;
uint32_t hw_revision;
uint32_t protocol_version_major;
uint32_t protocol_version_minor;
char sw_version_string[51];
float hwcap_max_current;
float hwcap_min_current;
uint32_t hwcap_max_phase_count;
uint32_t hwcap_min_phase_count;
bool supports_changing_phases_during_charging;
} KeepAliveLo;
typedef struct _KeepAlive {
uint32_t time_stamp;
uint32_t hw_type;
uint32_t hw_revision;
char sw_version_string[51];
} KeepAlive;
typedef struct _Telemetry {
float cp_voltage_hi;
float cp_voltage_lo;
float temp0;
float temp1;
} Telemetry;
/* *
This container message is send from MCU to EVerest and may contain any allowed message in that direction. */
typedef struct _McuToEverest {
pb_size_t which_payload;
union {
KeepAliveLo keep_alive;
ResetReason reset;
CpState cp_state;
/* false: relais are off, true: relais are on */
bool relais_state;
ErrorFlags error_flags;
Telemetry telemetry;
PpState pp_state;
LockState lock_state;
} payload;
} McuToEverest;
typedef struct _FirmwareUpdate {
bool invoke_rom_bootloader;
} FirmwareUpdate;
typedef struct _RcdCommand {
bool test; /* true -> set TEST pin high, false -> set TEST pin low */
bool reset; /* reset RCD/emergency off if set to true */
} RcdCommand;
/* *
This container message is send from EVerest to MCU and may contain any allowed message in that direction. */
typedef struct _EverestToMcu {
pb_size_t which_payload;
union {
FirmwareUpdate firmware_update;
KeepAlive keep_alive;
/* false: unlock, true: lock */
bool connector_lock;
/* in 0.01 %, 0 = State F, 10000 = X1 */
uint32_t pwm_duty_cycle;
bool allow_power_on;
bool reset;
RcdCommand rcd_cmd;
} payload;
} EverestToMcu;
#ifdef __cplusplus
extern "C" {
#endif
/* Helper constants for enums */
#define _CpState_MIN CpState_STATE_A
#define _CpState_MAX CpState_STATE_F
#define _CpState_ARRAYSIZE ((CpState)(CpState_STATE_F+1))
#define _ResetReason_MIN ResetReason_USER
#define _ResetReason_MAX ResetReason_WATCHDOG
#define _ResetReason_ARRAYSIZE ((ResetReason)(ResetReason_WATCHDOG+1))
#define _PpState_MIN PpState_STATE_NC
#define _PpState_MAX PpState_STATE_FAULT
#define _PpState_ARRAYSIZE ((PpState)(PpState_STATE_FAULT+1))
#define _LockState_MIN LockState_UNDEFINED
#define _LockState_MAX LockState_LOCKED
#define _LockState_ARRAYSIZE ((LockState)(LockState_LOCKED+1))
#define McuToEverest_payload_reset_ENUMTYPE ResetReason
#define McuToEverest_payload_cp_state_ENUMTYPE CpState
#define McuToEverest_payload_pp_state_ENUMTYPE PpState
#define McuToEverest_payload_lock_state_ENUMTYPE LockState
/* Initializer values for message structs */
#define EverestToMcu_init_default {0, {FirmwareUpdate_init_default}}
#define McuToEverest_init_default {0, {KeepAliveLo_init_default}}
#define ErrorFlags_init_default {0, 0, 0, 0, 0, 0, 0}
#define KeepAliveLo_init_default {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
#define KeepAlive_init_default {0, 0, 0, ""}
#define Telemetry_init_default {0, 0, 0, 0}
#define FirmwareUpdate_init_default {0}
#define RcdCommand_init_default {0, 0}
#define EverestToMcu_init_zero {0, {FirmwareUpdate_init_zero}}
#define McuToEverest_init_zero {0, {KeepAliveLo_init_zero}}
#define ErrorFlags_init_zero {0, 0, 0, 0, 0, 0, 0}
#define KeepAliveLo_init_zero {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
#define KeepAlive_init_zero {0, 0, 0, ""}
#define Telemetry_init_zero {0, 0, 0, 0}
#define FirmwareUpdate_init_zero {0}
#define RcdCommand_init_zero {0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define ErrorFlags_diode_fault_tag 1
#define ErrorFlags_rcd_selftest_failed_tag 2
#define ErrorFlags_rcd_triggered_tag 3
#define ErrorFlags_ventilation_not_available_tag 4
#define ErrorFlags_connector_lock_failed_tag 5
#define ErrorFlags_cp_signal_fault_tag 6
#define ErrorFlags_over_current_tag 7
#define KeepAliveLo_time_stamp_tag 1
#define KeepAliveLo_hw_type_tag 2
#define KeepAliveLo_hw_revision_tag 3
#define KeepAliveLo_protocol_version_major_tag 4
#define KeepAliveLo_protocol_version_minor_tag 5
#define KeepAliveLo_sw_version_string_tag 6
#define KeepAliveLo_hwcap_max_current_tag 7
#define KeepAliveLo_hwcap_min_current_tag 8
#define KeepAliveLo_hwcap_max_phase_count_tag 9
#define KeepAliveLo_hwcap_min_phase_count_tag 10
#define KeepAliveLo_supports_changing_phases_during_charging_tag 11
#define KeepAlive_time_stamp_tag 1
#define KeepAlive_hw_type_tag 2
#define KeepAlive_hw_revision_tag 3
#define KeepAlive_sw_version_string_tag 6
#define Telemetry_cp_voltage_hi_tag 1
#define Telemetry_cp_voltage_lo_tag 2
#define Telemetry_temp0_tag 3
#define Telemetry_temp1_tag 4
#define McuToEverest_keep_alive_tag 1
#define McuToEverest_reset_tag 2
#define McuToEverest_cp_state_tag 3
#define McuToEverest_relais_state_tag 4
#define McuToEverest_error_flags_tag 5
#define McuToEverest_telemetry_tag 6
#define McuToEverest_pp_state_tag 7
#define McuToEverest_lock_state_tag 8
#define FirmwareUpdate_invoke_rom_bootloader_tag 1
#define RcdCommand_test_tag 1
#define RcdCommand_reset_tag 2
#define EverestToMcu_firmware_update_tag 1
#define EverestToMcu_keep_alive_tag 2
#define EverestToMcu_connector_lock_tag 3
#define EverestToMcu_pwm_duty_cycle_tag 4
#define EverestToMcu_allow_power_on_tag 5
#define EverestToMcu_reset_tag 6
#define EverestToMcu_rcd_cmd_tag 8
/* Struct field encoding specification for nanopb */
#define EverestToMcu_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,firmware_update,payload.firmware_update), 1) \
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 2) \
X(a, STATIC, ONEOF, BOOL, (payload,connector_lock,payload.connector_lock), 3) \
X(a, STATIC, ONEOF, UINT32, (payload,pwm_duty_cycle,payload.pwm_duty_cycle), 4) \
X(a, STATIC, ONEOF, BOOL, (payload,allow_power_on,payload.allow_power_on), 5) \
X(a, STATIC, ONEOF, BOOL, (payload,reset,payload.reset), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload,rcd_cmd,payload.rcd_cmd), 8)
#define EverestToMcu_CALLBACK NULL
#define EverestToMcu_DEFAULT NULL
#define EverestToMcu_payload_firmware_update_MSGTYPE FirmwareUpdate
#define EverestToMcu_payload_keep_alive_MSGTYPE KeepAlive
#define EverestToMcu_payload_rcd_cmd_MSGTYPE RcdCommand
#define McuToEverest_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 1) \
X(a, STATIC, ONEOF, UENUM, (payload,reset,payload.reset), 2) \
X(a, STATIC, ONEOF, UENUM, (payload,cp_state,payload.cp_state), 3) \
X(a, STATIC, ONEOF, BOOL, (payload,relais_state,payload.relais_state), 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload,error_flags,payload.error_flags), 5) \
X(a, STATIC, ONEOF, MESSAGE, (payload,telemetry,payload.telemetry), 6) \
X(a, STATIC, ONEOF, UENUM, (payload,pp_state,payload.pp_state), 7) \
X(a, STATIC, ONEOF, UENUM, (payload,lock_state,payload.lock_state), 8)
#define McuToEverest_CALLBACK NULL
#define McuToEverest_DEFAULT NULL
#define McuToEverest_payload_keep_alive_MSGTYPE KeepAliveLo
#define McuToEverest_payload_error_flags_MSGTYPE ErrorFlags
#define McuToEverest_payload_telemetry_MSGTYPE Telemetry
#define ErrorFlags_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, diode_fault, 1) \
X(a, STATIC, SINGULAR, BOOL, rcd_selftest_failed, 2) \
X(a, STATIC, SINGULAR, BOOL, rcd_triggered, 3) \
X(a, STATIC, SINGULAR, BOOL, ventilation_not_available, 4) \
X(a, STATIC, SINGULAR, BOOL, connector_lock_failed, 5) \
X(a, STATIC, SINGULAR, BOOL, cp_signal_fault, 6) \
X(a, STATIC, SINGULAR, BOOL, over_current, 7)
#define ErrorFlags_CALLBACK NULL
#define ErrorFlags_DEFAULT NULL
#define KeepAliveLo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
X(a, STATIC, SINGULAR, UINT32, hw_type, 2) \
X(a, STATIC, SINGULAR, UINT32, hw_revision, 3) \
X(a, STATIC, SINGULAR, UINT32, protocol_version_major, 4) \
X(a, STATIC, SINGULAR, UINT32, protocol_version_minor, 5) \
X(a, STATIC, SINGULAR, STRING, sw_version_string, 6) \
X(a, STATIC, SINGULAR, FLOAT, hwcap_max_current, 7) \
X(a, STATIC, SINGULAR, FLOAT, hwcap_min_current, 8) \
X(a, STATIC, SINGULAR, UINT32, hwcap_max_phase_count, 9) \
X(a, STATIC, SINGULAR, UINT32, hwcap_min_phase_count, 10) \
X(a, STATIC, SINGULAR, BOOL, supports_changing_phases_during_charging, 11)
#define KeepAliveLo_CALLBACK NULL
#define KeepAliveLo_DEFAULT NULL
#define KeepAlive_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
X(a, STATIC, SINGULAR, UINT32, hw_type, 2) \
X(a, STATIC, SINGULAR, UINT32, hw_revision, 3) \
X(a, STATIC, SINGULAR, STRING, sw_version_string, 6)
#define KeepAlive_CALLBACK NULL
#define KeepAlive_DEFAULT NULL
#define Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, cp_voltage_hi, 1) \
X(a, STATIC, SINGULAR, FLOAT, cp_voltage_lo, 2) \
X(a, STATIC, SINGULAR, FLOAT, temp0, 3) \
X(a, STATIC, SINGULAR, FLOAT, temp1, 4)
#define Telemetry_CALLBACK NULL
#define Telemetry_DEFAULT NULL
#define FirmwareUpdate_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, invoke_rom_bootloader, 1)
#define FirmwareUpdate_CALLBACK NULL
#define FirmwareUpdate_DEFAULT NULL
#define RcdCommand_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, test, 1) \
X(a, STATIC, SINGULAR, BOOL, reset, 2)
#define RcdCommand_CALLBACK NULL
#define RcdCommand_DEFAULT NULL
extern const pb_msgdesc_t EverestToMcu_msg;
extern const pb_msgdesc_t McuToEverest_msg;
extern const pb_msgdesc_t ErrorFlags_msg;
extern const pb_msgdesc_t KeepAliveLo_msg;
extern const pb_msgdesc_t KeepAlive_msg;
extern const pb_msgdesc_t Telemetry_msg;
extern const pb_msgdesc_t FirmwareUpdate_msg;
extern const pb_msgdesc_t RcdCommand_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define EverestToMcu_fields &EverestToMcu_msg
#define McuToEverest_fields &McuToEverest_msg
#define ErrorFlags_fields &ErrorFlags_msg
#define KeepAliveLo_fields &KeepAliveLo_msg
#define KeepAlive_fields &KeepAlive_msg
#define Telemetry_fields &Telemetry_msg
#define FirmwareUpdate_fields &FirmwareUpdate_msg
#define RcdCommand_fields &RcdCommand_msg
/* Maximum encoded size of messages (where known) */
#define ErrorFlags_size 14
#define EverestToMcu_size 72
#define FirmwareUpdate_size 2
#define KeepAliveLo_size 106
#define KeepAlive_size 70
#define McuToEverest_size 108
#define RcdCommand_size 4
#define TIDA010939_PB_H_MAX_SIZE McuToEverest_size
#define Telemetry_size 20
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -0,0 +1,118 @@
syntax = "proto3";
/**
* This container message is send from EVerest to MCU and may contain any allowed message in that direction.
*/
message EverestToMcu {
oneof payload {
FirmwareUpdate firmware_update = 1;
KeepAlive keep_alive = 2;
// false: unlock, true: lock
bool connector_lock = 3;
// in 0.01 %, 0 = State F, 10000 = X1
uint32 pwm_duty_cycle = 4;
bool allow_power_on = 5;
bool reset = 6;
RcdCommand rcd_cmd = 8;
}
}
/**
* This container message is send from MCU to EVerest and may contain any allowed message in that direction.
*/
message McuToEverest {
oneof payload {
KeepAliveLo keep_alive = 1;
ResetReason reset = 2;
CpState cp_state = 3;
// false: relais are off, true: relais are on
bool relais_state = 4;
ErrorFlags error_flags = 5;
Telemetry telemetry = 6;
PpState pp_state = 7;
LockState lock_state = 8;
}
}
enum CpState {
STATE_A = 0;
STATE_B = 1;
STATE_C = 2;
STATE_D = 3;
STATE_E = 4;
STATE_F = 5;
}
message ErrorFlags {
bool diode_fault = 1;
bool rcd_selftest_failed = 2;
bool rcd_triggered = 3;
bool ventilation_not_available = 4;
bool connector_lock_failed = 5;
bool cp_signal_fault = 6;
bool over_current = 7;
}
enum ResetReason {
USER = 0;
WATCHDOG = 1;
}
message KeepAliveLo {
uint32 time_stamp = 1;
uint32 hw_type = 2;
uint32 hw_revision = 3;
uint32 protocol_version_major = 4;
uint32 protocol_version_minor = 5;
string sw_version_string = 6;
float hwcap_max_current = 7;
float hwcap_min_current = 8;
uint32 hwcap_max_phase_count = 9;
uint32 hwcap_min_phase_count = 10;
}
message KeepAlive {
uint32 time_stamp = 1;
uint32 hw_type = 2;
uint32 hw_revision = 3;
string sw_version_string = 6;
}
message Telemetry {
float cp_voltage_hi = 1;
float cp_voltage_lo = 2;
float temp0 = 3;
float temp1 = 4;
}
enum PpState {
STATE_NC = 0;
STATE_13A = 1;
STATE_20A = 2;
STATE_32A = 3;
STATE_70A = 4;
STATE_FAULT = 5;
}
enum LockState {
UNDEFINED = 0;
UNLOCKED = 1;
LOCKED = 2;
}
message FirmwareUpdate {
bool invoke_rom_bootloader = 1;
}
message RcdCommand {
bool test = 1; // true -> set TEST pin high, false -> set TEST pin low
bool reset = 2; // reset RCD/emergency off if set to true
}