11#include < std_msgs/msg/bool.h>
2- #include < std_msgs/msg/float32.h>
32
43#include < Arduino.h>
54#include < EMA.h>
65#include < micro_ros_platformio.h>
76#include < omros2_firmware_msgs/msg/emergency_status.h>
7+ #include < omros2_firmware_msgs/msg/power_status.h>
88#include < rcl/rcl.h>
99#include < rclc/executor.h>
1010#include < rclc/rclc.h>
1111#include < sensor_msgs/msg/battery_state.h>
1212#include < sensor_msgs/msg/imu.h>
1313
14+ #include < algorithm>
15+
1416#include " emergency.h"
1517#include " hardware.h"
1618#include " imu.h"
@@ -39,8 +41,57 @@ uint8_t powerSupplyStatus = 0;
3941uint8_t powerSupplyHealth = 0 ;
4042bool batteryPresent = false ;
4143bool chargeEnabled = false ;
44+ bool chargingAllowed = false ;
45+ bool batteryFullLatched = false ;
46+ uint8_t chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_DISCHARGING;
47+ uint8_t chargeInhibitReason = omros2_firmware_msgs__msg__PowerStatus__INHIBIT_NONE;
48+ uint32_t chargeRetryRemainingMs = 0 ;
49+ ulong chargingDisabledTime = 0 ;
4250ulong lastChargingLoop = 0 ;
4351
52+ bool shouldStartRetryDelay (uint8_t reason)
53+ {
54+ return reason == omros2_firmware_msgs__msg__PowerStatus__INHIBIT_BATTERY_OVERVOLTAGE
55+ || reason == omros2_firmware_msgs__msg__PowerStatus__INHIBIT_CHARGE_OVERVOLTAGE
56+ || reason == omros2_firmware_msgs__msg__PowerStatus__INHIBIT_OVERCURRENT;
57+ }
58+
59+ uint8_t getChargingInhibitReason ()
60+ {
61+ if (!batteryPresent)
62+ {
63+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_NO_BATTERY;
64+ }
65+
66+ if (batteryVoltage > BATT_ABSOLUTE_MAX )
67+ {
68+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_BATTERY_OVERVOLTAGE;
69+ }
70+
71+ if (chargeVoltage > CHARGE_VOLTAGE_CUTOFF )
72+ {
73+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_CHARGE_OVERVOLTAGE;
74+ }
75+
76+ if (chargeCurrent >= CHARGE_CURRENT_CUTOFF )
77+ {
78+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_OVERCURRENT;
79+ }
80+
81+ if (batteryFullLatched)
82+ {
83+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_BATTERY_FULL;
84+ }
85+
86+ return omros2_firmware_msgs__msg__PowerStatus__INHIBIT_NONE;
87+ }
88+
89+ void setChargeEnabled (bool enabled)
90+ {
91+ chargeEnabled = enabled;
92+ digitalWrite (PIN_ENABLE_CHARGE , chargeEnabled ? HIGH : LOW );
93+ }
94+
4495void manageBatteryCharging ()
4596{
4697 uint16_t readFiltered;
@@ -58,23 +109,79 @@ void manageBatteryCharging()
58109
59110 isChargerPresent = chargeVoltage >= CHARGER_PRESENT_VOLTAGE ;
60111
61- batteryPercentage =
62- std::min ((batteryVoltage - BATT_EMPTY ) / (BATT_FULL - BATT_EMPTY ) * 100 , 100 .0f );
112+ batteryPercentage = std::min (
113+ std::max ((batteryVoltage - BATT_EMPTY ) / (BATT_FULL - BATT_EMPTY ) * 100 , 0 . 0f ) , 100 .0f );
63114
64- bool shouldCharge = chargeVoltage <= CHARGE_VOLTAGE_CUTOFF
65- && chargeCurrent < CHARGE_CURRENT_CUTOFF
66- && batteryVoltage <= BATT_ABSOLUTE_MAX && batteryVoltage;
67- chargeEnabled = shouldCharge && batteryVoltage < BATT_FULL_HYSTERESIS ;
115+ if (batteryVoltage >= BATT_FULL )
116+ {
117+ batteryFullLatched = true ;
118+ }
119+ else if (batteryVoltage <= BATT_FULL_HYSTERESIS )
120+ {
121+ batteryFullLatched = false ;
122+ }
68123
69- digitalWrite (PIN_ENABLE_CHARGE , chargeEnabled ? HIGH : LOW );
124+ const auto now = millis ();
125+ const uint8_t previousInhibitReason = chargeInhibitReason;
70126
71- bool isCharging = chargeVoltage > batteryVoltage;
127+ chargeRetryRemainingMs = 0 ;
128+ chargeInhibitReason = omros2_firmware_msgs__msg__PowerStatus__INHIBIT_NONE;
72129
73- if (shouldCharge )
130+ if (chargeVoltage < CHARGE_REGEN_VOLTAGE )
74131 {
75- powerSupplyStatus = isCharging
76- ? sensor_msgs__msg__BatteryState__POWER_SUPPLY_STATUS_CHARGING
77- : sensor_msgs__msg__BatteryState__POWER_SUPPLY_STATUS_NOT_CHARGING;
132+ setChargeEnabled (true );
133+ chargingAllowed = false ;
134+ chargingDisabledTime = 0 ;
135+ chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_REGEN_PATH;
136+ }
137+ else
138+ {
139+ const uint8_t limitReason = getChargingInhibitReason ();
140+ if (limitReason != omros2_firmware_msgs__msg__PowerStatus__INHIBIT_NONE)
141+ {
142+ if (shouldStartRetryDelay (limitReason)
143+ && (chargingAllowed || chargeEnabled || previousInhibitReason != limitReason))
144+ {
145+ chargingDisabledTime = now;
146+ }
147+ else if (!shouldStartRetryDelay (limitReason))
148+ {
149+ chargingDisabledTime = 0 ;
150+ }
151+
152+ setChargeEnabled (false );
153+ chargingAllowed = false ;
154+ chargeInhibitReason = limitReason;
155+ chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_NOT_CHARGING;
156+ }
157+ else if (chargingDisabledTime != 0 && now - chargingDisabledTime < CHARGING_RETRY_MILLIS )
158+ {
159+ setChargeEnabled (false );
160+ chargingAllowed = false ;
161+ chargeInhibitReason = omros2_firmware_msgs__msg__PowerStatus__INHIBIT_RETRY_DELAY;
162+ chargeRetryRemainingMs = CHARGING_RETRY_MILLIS - (now - chargingDisabledTime);
163+ chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_RETRY_WAIT;
164+ }
165+ else
166+ {
167+ setChargeEnabled (true );
168+ chargingAllowed = true ;
169+ chargingDisabledTime = 0 ;
170+ chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_NOT_CHARGING;
171+ if (chargeVoltage > batteryVoltage)
172+ {
173+ chargingState = omros2_firmware_msgs__msg__PowerStatus__CHARGING_STATE_CHARGING;
174+ }
175+ }
176+ }
177+
178+ if (chargingAllowed)
179+ {
180+ powerSupplyStatus = sensor_msgs__msg__BatteryState__POWER_SUPPLY_STATUS_NOT_CHARGING;
181+ if (chargeVoltage > batteryVoltage)
182+ {
183+ powerSupplyStatus = sensor_msgs__msg__BatteryState__POWER_SUPPLY_STATUS_CHARGING;
184+ }
78185 }
79186 else
80187 {
@@ -89,7 +196,7 @@ void manageBatteryCharging()
89196 }
90197 else if (batteryVoltage > BATT_ABSOLUTE_MAX )
91198 {
92- powerSupplyHealth = sensor_msgs__msg__BatteryState__POWER_SUPPLY_HEALTH_OVERHEAT ;
199+ powerSupplyHealth = sensor_msgs__msg__BatteryState__POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
93200 }
94201 else
95202 {
@@ -168,35 +275,43 @@ void setup1()
168275
169276 auto batteryStatePublisher = new uros::Publisher<sensor_msgs__msg__BatteryState>(
170277 *node, " power" , UROS_GET_MSG_TYPE_SUPPORT (sensor_msgs, msg, BatteryState));
171- auto chargeVoltagePublisher = new uros::Publisher<std_msgs__msg__Float32>(
172- *node, " power/charge_voltage" , UROS_GET_MSG_TYPE_SUPPORT (std_msgs, msg, Float32));
173- auto chargerPresentPublisher = new uros::Publisher<std_msgs__msg__Bool>(
174- *node, " power/charger_present" , UROS_GET_MSG_TYPE_SUPPORT (std_msgs, msg, Bool));
278+ auto powerStatusPublisher = new uros::Publisher<omros2_firmware_msgs__msg__PowerStatus>(
279+ *node, " power/status" , UROS_GET_MSG_TYPE_SUPPORT (omros2_firmware_msgs, msg, PowerStatus));
175280 auto powerTimer = new uros::Timer (
176281 *node,
177282 1000 ,
178- [batteryStatePublisher, chargeVoltagePublisher, chargerPresentPublisher ]()
283+ [batteryStatePublisher, powerStatusPublisher ]()
179284 {
180285 auto & batteryMsg = batteryStatePublisher->get_message ();
181286
182287 auto ns = rmw_uros_epoch_nanos ();
183288 batteryMsg.header .stamp .sec = ns / 1000000000 ;
184289 batteryMsg.header .stamp .nanosec = ns % 1000000000 ;
185290 batteryMsg.voltage = batteryVoltage;
186- batteryMsg.charge = chargeCurrent;
187- batteryMsg.percentage = batteryPercentage;
291+ batteryMsg.current = chargeCurrent;
292+ batteryMsg.capacity = BATT_DESIGNED_CAPACITY ;
293+ batteryMsg.design_capacity = BATT_DESIGNED_CAPACITY ;
294+ batteryMsg.percentage = batteryPercentage / 100 .0f ;
188295 batteryMsg.present = batteryPresent;
189296 batteryMsg.power_supply_status = powerSupplyStatus;
190297 batteryMsg.power_supply_health = powerSupplyHealth;
191298 batteryStatePublisher->publish ();
192299
193- auto & chargeVoltageMsg = chargeVoltagePublisher->get_message ();
194- chargeVoltageMsg.data = chargeVoltage;
195- chargeVoltagePublisher->publish ();
196-
197- auto & chargerPresentMsg = chargerPresentPublisher->get_message ();
198- chargerPresentMsg.data = isChargerPresent;
199- chargerPresentPublisher->publish ();
300+ auto & powerStatusMsg = powerStatusPublisher->get_message ();
301+ powerStatusMsg.stamp .sec = ns / 1000000000 ;
302+ powerStatusMsg.stamp .nanosec = ns % 1000000000 ;
303+ powerStatusMsg.battery_voltage = batteryVoltage;
304+ powerStatusMsg.battery_percentage = batteryPercentage;
305+ powerStatusMsg.battery_present = batteryPresent;
306+ powerStatusMsg.charge_voltage = chargeVoltage;
307+ powerStatusMsg.charge_current = chargeCurrent;
308+ powerStatusMsg.charger_present = isChargerPresent;
309+ powerStatusMsg.charge_path_enabled = chargeEnabled;
310+ powerStatusMsg.charging_allowed = chargingAllowed;
311+ powerStatusMsg.charging_state = chargingState;
312+ powerStatusMsg.inhibit_reason = chargeInhibitReason;
313+ powerStatusMsg.retry_remaining_ms = chargeRetryRemainingMs;
314+ powerStatusPublisher->publish ();
200315 });
201316
202317 auto emergencyCommandSubscription = new uros::Subscriber<std_msgs__msg__Bool>(
0 commit comments