Solar basically does the following things:
Periodically collect data (voltage/current/temperature)
Log the data (to console or telemetry)
Check for faults
Respond to faults
To support this, here’s a breakdown of the project. Note that there are 2 solar boards, one of which has 5 MPPTs and one of which has 6, so firmware must support both.
Hardware schematic: https://github.com/uw-midsun/hardware/blob/mh_29_solar_sense_rev_1/MSXIV_SolarSense/MSXIV_SolarSense/Project Outputs for MSXIV_SolarSense/MSXIV_SolarSense_1.0.PDF
Modules
Generating data:
sense is a generic module that encapsulates the pattern of “periodically, read X and store the data, then tell the data store that we’re done.”
The
sense_register()
function takes a callback. All of the callbacks passed into this function will be periodically called (at a configurable interval), followed by a call todata_store_done()
.The callback passed to
sense_register()
should read voltage/current/temperature (and maybe PWM) data from hardware, then store it in the data store.
sense_current, sense_voltage, sense_temperature, and sense_mppt provide the callbacks passed to
sense_register()
. Each callssense_register()
in theirinit
function.sense_current reads the current output of the solar array through an MCP3427 ADC over I2C (via ACS722 current sense, though that shouldn’t be relevant for firmware).
sense_voltage reads the voltage from each of the MPPTs via 5-6 MCP3427s over I2C.
sense_temperature reads temperature data from GPIO pins PA0-4 (5 MPPTs) or PA0-5 (6 MPPTs) from thermistors.
sense_mppt communicates with the (presumably) SPV1020 MPPTs over SPI and reads their current and input voltage (and possibly PWM). It also reads the status register and raises fault events if an MPPT has an overcurrent/overvoltage/overtemperature bit set.
Storing data:
data_store provides an abstraction layer between reading the data and doing something with it. It stores all of the data collected by sense et al., then raises a “data ready” event. Consumers of the data (logger, fault_monitor, etc) can retrieve the data directly from data_store to avoid overloading the event queue. All data is stored as
uint16_t
.To store and retrieve data, we use a
DataPoint
enum with one entry for each data point we need (e.g.DATA_POINT_VOLTAGE_1
,DATA_POINT_MPPT_CURRENT_3
, etc.).The
data_store_enter()
function (not sold on the name) takes aDataPoint
parameter to identify which data point it is, as well as the data point as auint16_t
. It’ll overwrite that data point in the module’s storage.The
data_store_done()
function is called by sense when a batch of data is read. It raises a “data ready” event which is received by the consumers of the data.The
data_store_get()
function will take aDataPoint
parameter and auint16_t*
and will set the pointer to the value of the data point.
Consuming data:
logger is a very simple module: when it receives a data ready event, it logs the data from data_store to console in a human-readable way. (Don’t log the 6th MPPT’s data if there are only 5 MPPTs.)
data_tx, similar to logger, will take data from data_store on data ready events and TX it over CAN. Telemetry or other systems can then use the data.
fault_monitor encapsulates all the rules for what data levels make a fault. On data ready events, it takes data from data_store and raises a fault event if anything is at a fault level.
Dealing with faults:
solar_fsm keeps track of the state of the module. It transitions on fault events (and possibly commands from centre console, if those exist) and calls the DRV120 driver to open/close the relay and mppt to turn on/off the MPPTs. (This could be split off into multiple FSMs to handle specific fault sequences.)
Other:
mppt is a thin wrapper over the spv1020_mppt driver which handles dealing with the demux to support addressing multiple SPV1020s. It should provide the same functions as spv1020_mppt, but with an extra argument specifying which MPPT the command should go to; it’ll then set the demux to address that MPPT, then call the corresponding spv1020_mppt function. It should use the existing generic
mux
driver.This isn’t a driver because the fact that our SPV1020s are behind a demux is an implementation detail of the board which isn’t appropriate for a generic driver to deal with. This module encapsulates the interaction between the MPPTs and the demux.
Drivers
mcp3427_adc - driver for the MCP3427 analog-digital converter. Reportedly already exists in the MSXII codebase (project
solar_slave
) and can be reused.spv1020_mppt - driver for the SPV1020 maximum power point tracker and DC-DC converter. Communicates over SPI with the SPV1020s and issues commands (a list can be found in the SPV1020 datasheet). Should provide a separate function for calling each command.
drv120_relay - driver for the DRV120PWR relay driver.
Comments
We will have three types of events.
Action events will cause something to physically happen - like opening the relay or shutting down an MPPT. They have
EVENT_PRIORITY_HIGHEST
.Fault events are raised when a fault is detected. They have
EVENT_PRIORITY_HIGH
.Data ready events are raised by data_store when new data is available. They have
EVENT_PRIORITY_NORMAL
.
ICs we don’t need drivers for include:
ACS722 current sense: nothing configurable, hidden behind an MCP3427
MC74LVX138 decoder: we can use the existing generic
mux
driver.
From a separation-of-concerns standpoint it might make sense to move the MPPT fault detection logic out of sense_mppt to its own module since it isn’t strictly related to data collection, but that would be more complex and would require duplicating the soft timer logic (though that could be desired if we wanted to check faults more or less frequently). We could do it either way.