Realization
This chapter will show how the realization has been done. It will use diagrams to show how some of the parts were designed and it will describe each part in more detail.
Last updated
This chapter will show how the realization has been done. It will use diagrams to show how some of the parts were designed and it will describe each part in more detail.
Last updated
This is about the following code repository: https://github.com/NXPHoverGames/RDDRONE-BMS772
In the main source file, the BMS main can be found, this is the BMS application. This function will initialize each part and start the main loop task. This task will implement the battery main state machine as seen in Figure 2 (https://app.gitbook.com/o/-L9GLsni4p7csCR7QCJ8/s/4sYITYOw8lqjlbpxfe9d/~/changes/1/software-guide-nuttx/bms-application-state-machine). In the main source code, the state is changed. In this source file there is a function to handle a changed parameter as well. This function will call other functions from the needed parts to do something with the parameter that is changed. If for example a configuration changed, such that a configuration of the BCC needs to change, it will call the right function from the Bat management part to change the configuration of the BCC as well. At the end of the main loop, the watchdog in the SBC is kicked. If this is not done in time, it will reset the MCU.
There is a lot of data that is needed or set by different tasks. Because it is not wise to move this big chunk of data through all the tasks there needs to be some sort of shared memory. Because NuttX is POSIX compliance there are shared memory functions that could be used. But for these shared memory functions a memory management unit (MMU) is needed and this microcontroller does not have an MMU. That is why the whole data management will be made in a data source file. This makes sure the data is only made once and is not global. With functions the data can be read or written, and these functions ensures protection against multiple threads accessing the data at the same time. These functions can be seen in Figure 5.
To protect the data from multiple threads trying to access it at the same time, a mutex is used. A mutex is an object that can be locked and unlocked in an atomic operation. Meaning that if both threads want to lock the mutex, the threads cannot lock the same mutex at the same time. A mutex is needed to prevent data race. The other thread needs to wait until the mutex is available.
The big data chuck is in a struct, together with a parameter info array. This array supports a fast access of the data type, the minimum, the maximum and the address of the data. This ensures it is faster to get and set data than with a large switch.
If a variable is set with the set parameter function. It will check if the variable is changed. If the variable is changed, it will call a handle parameter change function. This function will check which parameter has changed and will set other parameters if needed. At the end there is a callback to the main which will make sure the correct functions / reactions are being called. There are parameters that have an effect on the battery management and thus a function that handles changes in the battery management part is called as well to react on the parameter that changed. Next to handling the change of a parameter, this function will check if one of the savable parameters has been changed. If this is the case, it will remember this to save the parameters to flash when the DEEP SLEEP state is executed.
In NuttX there is a nuttshell, this is the UART communication with the MCU. In this nuttshell, applications can be called with or without arguments. There arguments will be given to the function it calls, in this case the BMS main. This means that a CLI can be created with calling the application with some arguments.
This CLI that is made, can be used by calling the BMS application in the nuttshell with a command and optionally up to 2 arguments for that command. When this happens the BMS main is called. Meaning that this main needs to be resistant against multiple calls, this should not restart the BMS application because than the battery power will be cut.
If there are commands given when calling the BMS application, the CLI process commands function will be called to handle it. It will parse the command and optionally the arguments and check if the inputs are valid. If it is valid, it will act based on which command has been given. The flowchart can be seen in Figure 6.
In order to set a color to the RGB LED, the set led color function should be used. The flowchart of this function can be found in Figure 7. Because this function can be used from different tasks, a mutex will be locked before it checks if the color and if blink is already set. This function will set the semaphore to start or stop the blink sequence. It will skip the semaphore timed wait function to ensure the blink sequence restarts if needed. It will begin with the new color. This function will use the NuttX userled functions.
In order to use a GPIO in NuttX, these GPIO’s need to be defined in the board file and the board specific GPIO file. This will create devices for each GPIO pin. To use the GPIO in the application an IOCTL call needs to be used. IOCTL means input-output control and it is a device specific system call.
The IOCTL is used to give commands to a driver to control a device, in this case the GPIO pins. But for an IOCTL to work an open file descriptor needs to be given. This is obtained by giving the path to the device as a string. This is too much work to do in the application for setting or reading a GPIO, that is why a GPIO BMS application driver is made. This will make sure that a GPIO can be read or written with simple write/read pin functions and a define to indicate the pin. An input pin can be an interrupt pin On an interrupt it will generate a signal that will queue the action for the handler. Keep in mind that these signals can be very intrusive.
The Bat management part can be used to monitor the battery and control the power switch. Because the Bat management part is quite large, there are other source files made to help with functions it needs to do and with utilizing the BCC. Like monitoring, to take care of measurements. Configuration, to take care of the whole configuration for the BCC. Balancing, to add easy to implement balancing function. For the main to implement the state machine, functions are made to let the main implement the functionality. Some functions are made to enable the measurements, to check for faults, to self-discharge etc.
The batManag task is created to handle the battery management. It will start the manual measurements with the BCC. Mostly it will calculate and check the current or it will measure and calculate every variable at t-meas interval. It will then perform a measurement, calculate the variables, save the values, it will check for software faults, it will calculate the new transition variables, handle the cell balancing if needed and it will trigger the callback that new measurements have been done. When saving the data, it will save the new measured and calculated variables in one go using the structs. Because the BCC will not check for an overcurrent, the current needs to be read and calculated every time to be compared with the current threshold. For short circuit protection there is a hardware circuit. If it measures a fault, it will trigger the main to act on it.
The sequence of the batManag task can be seen in Figure 8. The sem_wait and sem_post functions are called consecutively in the endless loop, this is used to start and stop the task with a function from the main. The meas task will check if the next measurement needs to be the measure everything or just the current and calculate the wait time to make sure the t-meas interval is met or with the remaining wait time. The task will wait for the next time or when the measurement is enabled, it will measure right away. To make sure the cyclic measurements does not drift, the time before the loop starts and the period t-meas are gained. The target time is calculated by adding the period with the previous target time. If t-meas should change, this is updated in the sequence using a global variable. This is left out of the sequence diagram because it is too detailed.
To calculate the state of charge (s-charge), the coulomb counter is used. The coulomb counter register holds the sum of the measured currents (until read). There is another register that holds the number of samples in the coulomb counter register. The average current is calculated by dividing the sum of the currents by the number of samples. When the time is known for which the average current is calculated, the difference in charge can be calculated with the following formula: ∆Q=I_avg*∆t. The new remaining charge is calculated by adding the difference in charge with the old remaining charge. The state of charge can then be calculated by dividing the full charge capacity by the remaining charge.
In order to provide the average power consumption over a time period of ten seconds, a constant moving average is taken. This moving average is constructed by removing the oldest measurement and adding the new measurement, which is than divided by the amount of measurements. This way the average will only be of the last ten seconds. In order to be memory efficient, the measurements used in the moving average will be sub-sampled if the measurement period is configured as less than one second. This way maximum ten old measurements need to be known. Measurements are not lost when sub-sampling, because the BCC chip will remember an average of it.
Since the user can change configurations in run time, sometimes a configuration needs to be changed in the BCC as well. When there is a change in the configuration, this is set with the setParameter function and a task in the main source file will handle the change. This function will call a function to handle the change in the bat management part. In this part it will call the right function from the configuration source file to change the configuration of the BCC.
The batManag task will check for software measured faults. The BCC chip will take care of hardware fault monitoring for the overvoltage, undervoltage, over temperature and under temperature. It will set the fault pin high when there is an error. If this happens it will trigger an interrupt in the main and it will check what fault happened. The main can than act on the fault. This ensures that the main is in control of what happens.
Since the user can change configurations in run time, sometimes a configuration needs to be changed in the BCC as well. When there is a change in the configuration, this is set with the setParameter function and a task in the main source file will handle the change. This function will call a function to handle the change in the bat management part. In this part it will call the right function from the configuration source file to change the configuration of the BCC.
The SBC part is used to control the power of voltage regulators V1 (The most used 3.3V (VCC_3V3_SBC)) and V2 (CAN PHY) (VCC_5V_SBC). With the setSbcMode() function the mode of the SBC can be set. In the normal mode both V1 and V2 are active, in the standby mode V2 is off, turning off the CAN transceiver and in the sleep mode both V1 and V2 are off, turning off almost the whole BMS board. In Figure 10 the simplified flowchart of this function can be seen. Besides power regulators, the SBC has a watchdog, which is used to reset the MCU if it doesn’t "kick" (reset) the watchdog within the set time. The reset of the MCU this is done via the NRST pin.
This part works with a DroneCAN (or Cyphal) task that waits (it sleeps until a CAN transceiver signal comes in) for an incoming CAN transmission. It will also wake up if the main signals the task that new data needs to be send. When new data needs to be sent, the task will put the data that needs to be sent in the transmit buffer. It will check if the transmit buffer if it is filled and transmit the data if it is. Then it will wait for an incoming CAN transmission or signal again. To see the flowchart see Figure 10: DroneCAN flowchart below.
There are DroneCAN and Cyphal messages implemented. Keep in mind that you can only select one at the time. The DroneCAN messages can be seen below in "DroneCAN messages implemented".
The CyphalCAN messages are a snapshot of the CyphalCAN V1 with WIP DS-015. This consists of 3 messages, the energy source, the battery status and the battery parameters. These messages can be seen in "CyphalCAN messages implemented"
For information on the DSDL message definition used for DroneCAN, please see the .uavcan files:
ardupilot_equipment_power_BatteryContinuous
uavcan_equipment_power_BatteryInfo
ardupilot_equipment_power_BatteryPeriodic
ardupilot_equipment_power_BatteryCells
ardupilot_equipment_power_BatteryInfoAux
Table 1. DroneCAN battery continuous message
Table 2. DroneCAN battery info message
Typical publishing rate should be around 0.2~1 Hz.
Table 3. DroneCAN battery periodic message
Battery data to be sent statically upon request or periodically at a low rate
Recommend that this message is sent at a maximum of 1Hz and nominally 0.2 Hz (IE: once every 5 seconds.).
Table 4. DroneCAN battery cells message
Rate: set by parameter on smart battery (default off).
Table 5. DroneCAN battery info auxiliary message
Table 6. Energy source 0.1 CyphalCAN udral
Table 7. Battery status 0.2 CyphalCAN udral
* not implemented in the BMS example code
Table 8. Battery parameter 0.3 CyphalCAN udral
Type
Name
Unit
Description
Float16
Temperature_cells
C
Pack mounted thermistor (preferably installed between cells), NAN: field not provided
Float16
Temperature_pcb
C
Battery PCB temperature (likely output FET(s) or current sense resistor), NAN: field not provided.
Float16
Temperature_other
C
Application dependent, NAN: field not provided
Float32
Current
A
Positive: defined as a discharge current. Negative: defined as a charging current, NAN: field not provided
Float32
Voltage
V
Battery voltage
Float16
State_of_charge
%
The estimated state of charge, in percent remaining (0 - 100).
Uint8
Slot_id
-
The physical location of the battery on the aircraft. 0: field not provided
Float32
Capacity_consumed
Ah
This is either the consumption since power-on or since the battery was full, depending on the value of STATUS_FLAG_CAPACITY_RELATIVE_TO_FULL, NAN: field not provided
Uint32
Status_flags
-
Fault, health, readiness, and other status indications.
READY_TO_USE = 1
CHARGING = 2
CELL_BALANCING = 4
FAULT_CELL_IMBALANCE = 8
AUTO_DISCHARGING = 16
REQUIRES_SERVICE = 32
BAD_BATTERY = 64
PROTECTIONS_ENABLED = 128
FAULT_PROTECTION_SYSTEM = 256
FAULT_OVER_VOLT = 512
FAULT_UNDER_VOLT = 1024
FAULT_OVER_TEMP = 2048
FAULT_UNDER_TEMP = 4096
FAULT_OVER_CURRENT = 8192
FAULT_SHORT_CIRCUIT = 16384
FAULT_INCOMPATIBLE_VOLTAGE = 32768
FAULT_INCOMPATIBLE_FIRMWARE = 65536
FAULT_INCOMPATIBLE_CELLS_CONFIGURATION = 131072
CAPACITY_RELATIVE_TO_FULL = 262144
Type
Name
Unit
Description
Float16
Temperature
K
Float16
Voltage
V
Float16
Current
A
Float16
Average_power_10sec
W
Average power consumption over the last 10 seconds.
Float16
remaining_capacity_wh
Wh
Will be increasing during charging
Float16
full_charge_capacity_wh
Wh
Predicted battery capacity when it is fully charged. Falls with aging
Float16
hours_to_full_charge
h
Charging is expected to complete in this time; zero if not charging
Uint11
status_flags
-
- CHARGING must be always set as long as the battery is connected to a charger, even if the charging is complete.
- CHARGED must be cleared immediately when the charger is disconnected. STATUS_FLAG_IN_USE = 1 # The battery is currently used as a power supply
STATUS_FLAG_CHARGING = 2 # Charger is active
STATUS_FLAG_CHARGED = 4 # Charging complete, but the charger is still active
STATUS_FLAG_TEMP_HOT = 8 # Battery temperature is above normal
STATUS_FLAG_TEMP_COLD = 16 # Battery temperature is below normal
STATUS_FLAG_OVERLOAD = 32 # Safe operating area violation
STATUS_FLAG_BAD_BATTERY = 64 # This battery should not be used anymore (e.g. low SOH)
STATUS_FLAG_NEED_SERVICE = 128 # This battery requires maintenance (e.g. balancing, full recharge)
STATUS_FLAG_BMS_ERROR = 256 # Battery management system/controller error, smart battery interface error
STATUS_FLAG_RESERVED_A = 512 # Keep zero
STATUS_FLAG_RESERVED_B = 1024 # Keep zero
Uint7
state_of_health_pct
%
Health of the battery, in percent, optional. STATE_OF_HEALTH_UNKNOWN = 127 # Use this constant if SOH cannot be estimated.
Uint7
state_of_charge_pct
%
Relative State of Charge (SOC) estimate, in percent. Percent of the full charge [0, 100]. This field is required.
Uint7
state_of_charge_pct_stdev
%
SOC error standard deviation; use best guess if unknown.
Uint8
battery_id
-
Identifies the battery within this vehicle, e.g. 0 - primary battery.
Uint32
model_instance_id
-
Set to zero if not applicable. Model instance ID must be unique within the same battery model name.
Uint8[<32]
model_name
-
Battery model name. Model name is a human-readable string that normally should include the vendor name, model name, and chemistry
type of this battery. This field should be assumed case-insensitive. Example: "Zubax Smart Battery v1.1 LiPo".
Type
Name
Unit
Description
Uint8[<=50]
Name
-
Formatted as manufacturer_product, 0 terminated
Uint8[<=32]
Serial_number
-
Serial number in ASCII characters, 0 terminated
Uint8[<=9]
Manufacturer_date
-
Manufacture date (DDMMYYYY) in ASCII characters, 0 terminated
Float32
Design_capacity
Fully charged design capacity. 0: field not provided.
Uint8
Cells_in_series
-
Number of battery cells in series. 0: field not provided.
Float16
Nominal_voltage
V
Battery nominal voltage. Used for conversion between Wh and Ah. 0: field not provided.
Float16
Discharge_minimum_voltage
V
Minimum per-cell voltage when discharging. 0: field not provided.
Float16
charging_minimum_voltage
V
Minimum per-cell voltage when charging. 0: field not provided.
Float16
charging_maximum_voltage
V
Maximum per-cell voltage when charged. 0: field not provided.
Float32
charging_maximum_current
A
Maximum pack continuous charge current. 0: field not provided.
Float32
discharge_maximum_current
A
Maximum pack continuous discharge current. 0: field not provided.
Float32
discharge_maximum_burst_current
A
Maximum pack discharge burst current for 30 seconds. 0: field not provided
Float32
full_charge_capacity
Ah
Predicted battery capacity when fully charged (accounting for battery degradation), NAN: field not provided
Uint16
cycle_count
-
Lifetime count of the number of charge/discharge cycles (https://en.wikipedia.org/wiki/Charge_cycle). UINT16_MAX: field not provided.
Uint8
state_of_health
%
State of Health (SOH) estimate, in percent (0 - 100). UINT8_MAX: field not provided.
Type
Name
Unit
Description
Float16 [<=24]
Voltages
V
Cell voltage array.
Uint16
index
-
Index of the first cell in the array, index 0 is cells at array indices 0 - 23, index 24 is cells at array indices 24 - 47, etc.
Type
Name
Unit
Description
uavcan.Timestamp
timestamp
-
timestamp
Float16 [<=255]
voltage_cell
V
Battery individual cell voltages, length of following field also used as cell count.
Uint16
cycle_count
-
Uint16
over_discharge_count
-
Number of times the battery was discharged over the rated capacity.
Float16
max_current
A
Max instantaneous current draw since last message.
Float16
nominal_voltage
V
Nominal voltage of the battery pack.
Bool
is_powering_off
-
Power off event imminent indication, false if unknown
Uint8
battery_id
-
Identifies the battery within this vehicle, e.g. 0 - primary battery
Subject
Type
Typ. Rate [Hz]
energy_source
1…100
status
~1
parameters
~0.2
Type
Name
Unit
Description
Uint56
timestamp.microsecond
us
The number of microseconds that have passed since some arbitrary moment in the past. UNKNOWN = 0.
Float32
source.power.current
A
Battery current.
Float32
source.power.voltage
V
Battery voltage.
Float32
source.energy
J
A pessimistic estimate of the amount of energy that can be reclaimed from the source in its current state.
Float32
source.full_energy
J
A pessimistic estimate of the amount of energy that can be reclaimed from a fresh source (a fully charged battery) under the current conditions.
Type
Name
Unit
Description
Uint2
heartbeat.readiness*
-
SLEEP = 0, STANDBY = 2, ENGAGED = 3.
Uint2
heartbeat.health*
-
NOMINAL = 0, ADVISORY = 1, CAUTION = 2, WARNING = 3.
Float32[2]
temperature_min_max
K
The minimum and maximum readings of the pack temperature sensors.
Float32
available_charge
C
The estimated electric charge currently stored in the battery.
Uint8
error*
-
Error status. NONE = 0, BAD_BATTERY = 10, NEEDS_SERVICE = 11, BMS_ERROR = 20, CONFIGURATION = 30, OVERDISCHARGE = 50, OVERLOAD = 51, CELL_OVERVOLTAGE = 60, CELL_UNDERVOLTAGE = 61, CELL_COUNT = 62, TEMPERATURE_HOT = 100, TEMPERATURE_COLD = 101.
Float16[<=255]
cell_voltages
V
The voltages of individual cells in the battery pack.
Type
Name
Unit
Description
Uint64
unique_id
-
Unique number.
Float32
mass
Kg
The total mass of the battery.
Float32
design_capacity
C
Design capacity.
Float32[2]
design_cell_voltage_min_max
V
Factory cell voltages.
Float32
discharge_current
A
The discharge current.
Float32
discharge_current_burst
A
The burst discharge current at least for 5 seconds.
Float32
charge_current
A
The charge current.
Float32
charge_current_fast
A
The fast charge current.
Float32
charge_termination_treshold
A
End-of-charging current.
Float32
charge_voltage
V
Total charging voltage.
Uint16
cycle_count
-
The number of cycles.
Uint16
Void16
-
Was cell count.
Uint7
state_of_health_pct
%
The state of health.
Uint8
technology.value
-
Battery chemistry 100 = LiPo, 101 = LiFePO4, 0 = unknown.
Float32
nominal_voltage
V
The nominal battery voltage.