1277 lines
No EOL
46 KiB
C++
1277 lines
No EOL
46 KiB
C++
/*!
|
|
* @file src/components/i2c/controller.cpp
|
|
*
|
|
* Controller for the i2c.proto API
|
|
*
|
|
* Adafruit invests time and resources providing this open source code,
|
|
* please support Adafruit and open-source hardware by purchasing
|
|
* products from Adafruit!
|
|
*
|
|
* Copyright (c) Brent Rubell 2025 for Adafruit Industries.
|
|
*
|
|
* BSD license, all text here must be included in any redistribution.
|
|
*
|
|
*/
|
|
#include "controller.h"
|
|
#include "drivers/drvBase.h"
|
|
#include "drivers/drvOutputBase.h"
|
|
|
|
/*!
|
|
@brief Lambda function to create a drvBase driver instance
|
|
@param i2c
|
|
The desired I2C interface.
|
|
@param addr
|
|
The desired i2c device address.
|
|
@param mux_channel
|
|
The desired I2C multiplexer channel.
|
|
@param driver_name
|
|
The i2c driver's name.
|
|
*/
|
|
using FnCreateI2CSensorDriver =
|
|
std::function<drvBase *(TwoWire *, uint16_t, uint32_t, const char *)>;
|
|
|
|
// Factory for creating a new I2C SENSOR drivers
|
|
// NOTE: When you add a new SENSOR driver, make sure to add it to the factory!
|
|
static const std::map<std::string, FnCreateI2CSensorDriver> I2cFactorySensor = {
|
|
{"bme280",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBme280(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"adt7410",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvAdt7410(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"aht20",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvAhtx0(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"am2301b",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvAhtx0(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"am2315c",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvAhtx0(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"dht20",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvAhtx0(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bh1750",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBh1750(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bme680",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBme680(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bme688",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBme680(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"BMP280",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBmp3xx(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bmp388",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBmp3xx(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bmp390",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBmp3xx(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"bmp280",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvBmp280(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"dps310",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvDps310(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ds2484",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvDs2484(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ens160",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvEns160(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"hts221",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvHts221(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"htu21d",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvHtu21d(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ina219",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvIna219(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"lc709203f",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLc709203f(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"lps3xhw",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLps3xhw(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"lps22hb",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLps22hb(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"lps25hb",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLps25hb(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ltr329",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLtr329_Ltr303(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ltr303",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLtr329_Ltr303(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ltr390",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvLtr390(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"max17048",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMax1704x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"mcp3421",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMax1704x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"mcp9808",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMcp9808(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"mpl115a2",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMpl115a2(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"mprls",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMprls(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ms8607",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvMs8607(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"nau7802",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvNau7802(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"pct2075",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvPct2075(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"pmsa003i",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvPm25(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"scd40",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvScd4x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"scd41",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvScd4x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"scd30",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvScd30(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sgp40",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSgp40(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht3x",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht3x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht30_shell",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht3x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht30_mesh",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht3x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht40",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht4x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht41",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht4x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sht45",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSht4x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sen5x",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSen5x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sen55",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSen5x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sen54",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSen5x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"sen50",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSen5x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"shtc3",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvShtc3(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"si7021",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSi7021(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"stemma_soil",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvSen5x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"tmp117",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvTmp117(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"tsl2591",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvTsl2591(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vncl4020",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVncl4020(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"veml7700",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVeml7700(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vncl4040",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVncl4040(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vl53l0x",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVl53l0x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vl53l1x",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVl53l1x(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vl53l4cd",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVl53l4cd(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vl53l4cx",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVl53l4cx(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"vl6180x",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvBase * {
|
|
return new drvVl6180x(i2c, addr, mux_channel, driver_name);
|
|
}}}; ///< I2C driver factory
|
|
|
|
/*!
|
|
@brief Lambda function to create a drvOutputBase instance
|
|
@param i2c
|
|
The desired I2C interface.
|
|
@param addr
|
|
The desired i2c device address.
|
|
@param mux_channel
|
|
The desired I2C multiplexer channel.
|
|
@param driver_name
|
|
The i2c output driver's name.
|
|
*/
|
|
using FnCreateI2cOutputDrv =
|
|
std::function<drvOutputBase *(TwoWire *, uint16_t, uint32_t, const char *)>;
|
|
|
|
// Factory for creating a new i2c OUTPUT driver
|
|
// NOTE: When adding a new OUTPUT driver, make sure to add it to the map below!
|
|
static const std::map<std::string, FnCreateI2cOutputDrv> I2cFactoryOutput = {
|
|
{"quadalphanum",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvOutputBase * {
|
|
return new drvOutQuadAlphaNum(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"7seg",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvOutputBase * {
|
|
return new drvOut7Seg(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"charlcd",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvOutputBase * {
|
|
return new drvOutCharLcd(i2c, addr, mux_channel, driver_name);
|
|
}},
|
|
{"ssd1306",
|
|
[](TwoWire *i2c, uint16_t addr, uint32_t mux_channel,
|
|
const char *driver_name) -> drvOutputBase * {
|
|
return new drvOutSsd1306(i2c, addr, mux_channel, driver_name);
|
|
}}}; ///< I2C output driver factory
|
|
|
|
/*!
|
|
@brief Creates an I2C driver by name
|
|
@param driver_name
|
|
The name of the I2C driver.
|
|
@param i2c
|
|
The I2C bus.
|
|
@param addr
|
|
The I2C device address.
|
|
@param i2c_mux_channel
|
|
The I2C MUX channel.
|
|
@param status
|
|
The I2cDeviceStatus message.
|
|
@returns A pointer to the I2C driver.
|
|
*/
|
|
drvBase *CreateI2cSensorDrv(const char *driver_name, TwoWire *i2c,
|
|
uint16_t addr, uint32_t i2c_mux_channel,
|
|
wippersnapper_i2c_I2cDeviceStatus &status) {
|
|
auto it = I2cFactorySensor.find(driver_name);
|
|
if (it == I2cFactorySensor.end()) {
|
|
status =
|
|
wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_FAIL_UNSUPPORTED_SENSOR;
|
|
return nullptr;
|
|
}
|
|
|
|
status = wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_SUCCESS;
|
|
return it->second(i2c, addr, i2c_mux_channel, driver_name);
|
|
}
|
|
|
|
/*!
|
|
@brief Creates an I2C driver by name
|
|
@param driver_name
|
|
The name of the I2C driver.
|
|
@param i2c
|
|
The I2C bus.
|
|
@param addr
|
|
The I2C device address.
|
|
@param i2c_mux_channel
|
|
The I2C MUX channel.
|
|
@param status
|
|
The I2cDeviceStatus message.
|
|
@returns A pointer to the I2C driver.
|
|
*/
|
|
drvOutputBase *CreateI2cOutputDrv(const char *driver_name, TwoWire *i2c,
|
|
uint16_t addr, uint32_t i2c_mux_channel,
|
|
wippersnapper_i2c_I2cDeviceStatus &status) {
|
|
auto it = I2cFactoryOutput.find(driver_name);
|
|
if (it == I2cFactoryOutput.end()) {
|
|
status =
|
|
wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_FAIL_UNSUPPORTED_SENSOR;
|
|
return nullptr;
|
|
}
|
|
|
|
status = wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_SUCCESS;
|
|
return it->second(i2c, addr, i2c_mux_channel, driver_name);
|
|
}
|
|
|
|
/*!
|
|
@brief I2cController constructor
|
|
*/
|
|
I2cController::I2cController() {
|
|
_i2c_model = new I2cModel();
|
|
_i2c_output_model = new I2cOutputModel();
|
|
_i2c_bus_default = new I2cHardware();
|
|
}
|
|
|
|
/*!
|
|
@brief I2cController destructor
|
|
*/
|
|
I2cController::~I2cController() {
|
|
if (_i2c_model)
|
|
delete _i2c_model;
|
|
|
|
if (_i2c_output_model)
|
|
delete _i2c_output_model;
|
|
|
|
if (_i2c_bus_default)
|
|
delete _i2c_bus_default;
|
|
}
|
|
|
|
/*!
|
|
@brief Removes an I2C driver from the controller and frees memory
|
|
@param address
|
|
The desired I2C device's address.
|
|
@param is_output_device
|
|
True if the driver is an output device, False otherwise.
|
|
@returns True if the driver was removed, False otherwise.
|
|
*/
|
|
bool I2cController::RemoveDriver(uint32_t address, bool is_output_device) {
|
|
if (!is_output_device) {
|
|
// Safely remove the i2c sensor driver from the vector and free memory
|
|
for (drvBase *driver : _i2c_drivers) {
|
|
if (driver == nullptr)
|
|
continue;
|
|
|
|
if (driver->GetAddress() != address)
|
|
continue;
|
|
|
|
auto it = std::find(_i2c_drivers.begin(), _i2c_drivers.end(), driver);
|
|
if (it != _i2c_drivers.end()) {
|
|
_i2c_drivers.erase(it);
|
|
}
|
|
delete driver;
|
|
return true;
|
|
}
|
|
} else {
|
|
// This was an output driver type, safely remove the i2c output driver from
|
|
// the vector and free memory
|
|
for (drvOutputBase *driver : _i2c_drivers_output) {
|
|
if (driver == nullptr)
|
|
continue;
|
|
|
|
if (driver->GetAddress() != address)
|
|
continue;
|
|
|
|
auto it = std::find(_i2c_drivers_output.begin(),
|
|
_i2c_drivers_output.end(), driver);
|
|
if (it != _i2c_drivers_output.end()) {
|
|
_i2c_drivers_output.erase(it);
|
|
}
|
|
delete driver;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// We didn't find the driver to remove
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to find driver to remove!");
|
|
return false;
|
|
}
|
|
|
|
/*!
|
|
@brief Returns if the I2C bus has been created successfully.
|
|
@param is_alt_bus
|
|
True if the alt. I2C bus is being queried, False otherwise.
|
|
@returns True if the I2C bus has already been created, False otherwise.
|
|
*/
|
|
bool I2cController::IsBusStatusOK(bool is_alt_bus) {
|
|
bool is_ok = false;
|
|
if (is_alt_bus) {
|
|
is_ok = (_i2c_bus_alt->GetBusStatus() ==
|
|
wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS);
|
|
} else {
|
|
is_ok = (_i2c_bus_default->GetBusStatus() ==
|
|
wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS);
|
|
}
|
|
return is_ok;
|
|
}
|
|
|
|
/*!
|
|
@brief Publishes an I2cDeviceAddedorReplaced message to the broker
|
|
@param device_descriptor
|
|
The I2cDeviceDescriptor message.
|
|
@param device_status
|
|
The I2cDeviceStatus message.
|
|
@returns True if the I2cDeviceAddedorReplaced message was published
|
|
successfully, False otherwise.
|
|
*/
|
|
bool I2cController::PublishI2cDeviceAddedorReplaced(
|
|
const wippersnapper_i2c_I2cDeviceDescriptor &device_descriptor,
|
|
const wippersnapper_i2c_I2cDeviceStatus &device_status) {
|
|
// If we're in offline mode, don't publish out to IO
|
|
if (WsV2._sdCardV2->isModeOffline())
|
|
return true; // Back out if we're in offline mode
|
|
|
|
// Encode the I2cDeviceAddedorReplaced message and publish it to IO
|
|
if (!_i2c_model->encodeMsgI2cDeviceAddedorReplaced(
|
|
device_descriptor, _i2c_bus_default->GetBusStatus(), device_status)) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Unable to encode I2cDeviceAddedorReplaced message!");
|
|
return false;
|
|
}
|
|
if (!WsV2.PublishSignal(
|
|
wippersnapper_signal_DeviceToBroker_i2c_device_added_replaced_tag,
|
|
_i2c_model->GetMsgI2cDeviceAddedOrReplaced())) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to publish I2cDeviceAddedorReplaced "
|
|
"message to IO!");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
@brief Implements handling for a I2cDeviceRemove message
|
|
@param stream
|
|
A pointer to the pb_istream_t stream.
|
|
@returns True if the I2cDeviceRemove message was handled, False
|
|
otherwise.
|
|
*/
|
|
bool I2cController::Handle_I2cDeviceRemove(pb_istream_t *stream) {
|
|
// Attempt to decode an I2cDeviceRemove message
|
|
WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceRemove message...");
|
|
if (!_i2c_model->DecodeI2cDeviceRemove(stream)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to decode I2cDeviceRemove message!");
|
|
return false;
|
|
}
|
|
|
|
// TODO [Online]: Implement the rest of this function
|
|
// TODO: Remember - can be on either bus! (default or alt)
|
|
// TODO: Remember to handle removal of a mux device or a device on a mux
|
|
// strlen(descriptor.i2c_bus_sda) == 0
|
|
|
|
wippersnapper_i2c_I2cDeviceRemove *msgRemove =
|
|
_i2c_model->GetI2cDeviceRemoveMsg();
|
|
if (!msgRemove->has_i2c_device_description) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: I2cDeviceRemove message missing required "
|
|
"device description!");
|
|
return false;
|
|
}
|
|
|
|
bool did_remove = true;
|
|
|
|
// Check for default bus
|
|
if (strlen(msgRemove->i2c_device_description.i2c_bus_scl) == 0 &&
|
|
strlen(msgRemove->i2c_device_description.i2c_bus_sda) == 0) {
|
|
WS_DEBUG_PRINTLN("[i2c] Removing device from default bus...");
|
|
if (!_i2c_bus_default->HasMux()) {
|
|
if (!RemoveDriver(msgRemove->i2c_device_description.i2c_device_address,
|
|
msgRemove->is_output_device)) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Failed to remove i2c device from default bus!");
|
|
did_remove = false;
|
|
}
|
|
} else {
|
|
// Bus has a I2C MUX attached
|
|
// Case 1: Is the I2C device connected to a MUX?
|
|
if (msgRemove->i2c_device_description.i2c_mux_address != 0xFFFF &&
|
|
msgRemove->i2c_device_description.i2c_mux_channel >= 0) {
|
|
_i2c_bus_default->SelectMuxChannel(
|
|
msgRemove->i2c_device_description.i2c_mux_channel);
|
|
if (!RemoveDriver(msgRemove->i2c_device_description.i2c_device_address,
|
|
msgRemove->is_output_device)) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Failed to remove i2c device from default bus!");
|
|
did_remove = false;
|
|
}
|
|
}
|
|
// Case 2: Is the I2C device a MUX?
|
|
if (msgRemove->i2c_device_description.i2c_device_address ==
|
|
msgRemove->i2c_device_description.i2c_mux_address) {
|
|
wippersnapper_i2c_I2cBusScanned scan_results;
|
|
_i2c_bus_default->ScanMux(&scan_results);
|
|
for (int i = 0; i < scan_results.i2c_bus_found_devices_count; i++) {
|
|
// Select the channel and remove the device
|
|
_i2c_bus_default->SelectMuxChannel(
|
|
scan_results.i2c_bus_found_devices[i].i2c_mux_channel);
|
|
RemoveDriver(scan_results.i2c_bus_found_devices[i].i2c_device_address,
|
|
msgRemove->is_output_device);
|
|
}
|
|
_i2c_bus_default->RemoveMux();
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Check for Alt. I2C Bus
|
|
|
|
// Publush with did_remove to the response
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
@brief Attempts to initialize a MUX on the bus.
|
|
@param name
|
|
The name of the MUX to initialize, used to set number
|
|
of channels.
|
|
@param address
|
|
The MUX's I2C address.
|
|
@param is_alt_bus
|
|
True if the alternative I2C bus is being used, False
|
|
otherwise.
|
|
@returns True if the MUX was successfully initialized, False
|
|
otherwise.
|
|
*/
|
|
bool I2cController::InitMux(const char *name, uint32_t address,
|
|
bool is_alt_bus) {
|
|
if (is_alt_bus) {
|
|
if (!_i2c_bus_alt->HasMux()) {
|
|
if (!_i2c_bus_alt->AddMuxToBus(address, name)) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
if (!_i2c_bus_default->HasMux()) {
|
|
if (!_i2c_bus_default->AddMuxToBus(address, name)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// TODO [Online]: Publish back out to IO here!
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
@brief Configures the MUX channel on the bus.
|
|
@param stream
|
|
Pointer to the pb_istream
|
|
@returns True if the I2C bus was successfully scanned and the
|
|
I2cBusScan message was published to IO, False otherwise.
|
|
*/
|
|
bool I2cController::Handle_I2cBusScan(pb_istream_t *stream) {
|
|
WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceAddOrReplace message...");
|
|
// Attempt to decode I2cBusScan message
|
|
if (!_i2c_model->DecodeI2cBusScan(stream)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to decode I2cBusScan message!");
|
|
return false;
|
|
}
|
|
|
|
_i2c_model->ClearI2cBusScanned();
|
|
wippersnapper_i2c_I2cBusScanned *scan_results =
|
|
_i2c_model->GetI2cBusScannedMsg();
|
|
|
|
bool scan_success = true;
|
|
// Case 1: Scan the default I2C bus
|
|
if (_i2c_model->GetI2cBusScanMsg()->scan_default_bus) {
|
|
// Was the default bus initialized correctly and ready to scan?
|
|
WS_DEBUG_PRINT("Bus State: ");
|
|
WS_DEBUG_PRINTLN(_i2c_bus_default->GetBusStatus());
|
|
WS_DEBUG_PRINTLN(IsBusStatusOK());
|
|
if (IsBusStatusOK()) {
|
|
if (!_i2c_bus_default->ScanBus(scan_results)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan default I2C bus!");
|
|
scan_success = false;
|
|
}
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Default I2C bus state is stuck, please "
|
|
"reset the board!");
|
|
scan_success = false;
|
|
}
|
|
}
|
|
|
|
// Case 2: Optionally scan the alternative I2C bus
|
|
if (_i2c_model->GetI2cBusScanMsg()->scan_alt_bus) {
|
|
// Is the alt bus initialized?
|
|
if (_i2c_bus_alt == nullptr) {
|
|
_i2c_bus_alt = new I2cHardware(
|
|
_i2c_model->GetI2cBusScanMsg()->i2c_alt_bus_descriptor.i2c_bus_sda,
|
|
_i2c_model->GetI2cBusScanMsg()->i2c_alt_bus_descriptor.i2c_bus_sda);
|
|
// Was the default bus initialized correctly and ready to scan?
|
|
if (IsBusStatusOK(true)) {
|
|
if (!_i2c_bus_alt->ScanBus(scan_results)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan alt. I2C bus!");
|
|
scan_success = false;
|
|
}
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: alt. I2C bus state is stuck, please "
|
|
"reset the board!");
|
|
scan_success = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Case 3: Optionally scan MUX attached to the default bus
|
|
if (_i2c_model->GetI2cBusScanMsg()->scan_default_bus_mux) {
|
|
if (_i2c_bus_default->HasMux()) {
|
|
if (!_i2c_bus_default->ScanMux(scan_results)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan I2C MUX on default bus!");
|
|
scan_success = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Case 4: Optionally scan MUX attached to the alt. bus
|
|
if (_i2c_model->GetI2cBusScanMsg()->scan_alt_bus) {
|
|
if (_i2c_bus_alt->HasMux()) {
|
|
if (!_i2c_bus_alt->ScanMux(scan_results)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan I2C MUX on alt. bus!");
|
|
scan_success = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Printout content of scan_results
|
|
WS_DEBUG_PRINT("[i2c] Scan found ");
|
|
WS_DEBUG_PRINT(scan_results->i2c_bus_found_devices_count);
|
|
WS_DEBUG_PRINTLN(" devices.");
|
|
for (int i = 0; i < scan_results->i2c_bus_found_devices_count; i++) {
|
|
WS_DEBUG_PRINTLN(i);
|
|
WS_DEBUG_PRINT("Address: ");
|
|
WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_device_address,
|
|
HEX);
|
|
WS_DEBUG_PRINT("SCL: ");
|
|
WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_bus_scl);
|
|
WS_DEBUG_PRINT("SDA: ");
|
|
WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_bus_sda);
|
|
WS_DEBUG_PRINT("MUX Address: ");
|
|
WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_mux_address);
|
|
WS_DEBUG_PRINT("MUX Channel: ");
|
|
WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_mux_channel);
|
|
}
|
|
|
|
// TODO: Encode and publish out to IO!
|
|
// TODO: Take scan_success into account here
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
@brief Handler for an I2cDeviceOutputWrite message
|
|
@param stream
|
|
A pointer to the pb_istream_t stream.
|
|
@returns True if the callback was successfully executed by the driver,
|
|
False otherwise.
|
|
*/
|
|
bool I2cController::Handle_I2cDeviceOutputWrite(pb_istream_t *stream) {
|
|
WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceOutputWrite message...");
|
|
// Attempt to decode an I2cDeviceOutputWrite message
|
|
if (!_i2c_model->DecodeI2cDeviceOutputWrite(stream)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to decode I2cDeviceOutputWrite "
|
|
"message!");
|
|
return false;
|
|
}
|
|
wippersnapper_i2c_I2cDeviceDescriptor descriptor =
|
|
_i2c_model->GetI2cDeviceOutputWriteMsg()->i2c_device_description;
|
|
|
|
// Attempt to find the driver
|
|
drvOutputBase *driver = nullptr;
|
|
for (auto *drv : _i2c_drivers_output) {
|
|
if (drv == nullptr)
|
|
continue;
|
|
|
|
if (drv->GetAddress() != descriptor.i2c_device_address)
|
|
continue;
|
|
|
|
driver = drv;
|
|
break;
|
|
}
|
|
|
|
if (driver == nullptr) {
|
|
WS_DEBUG_PRINT("[i2c] ERROR: Unable to find driver for device at addr 0x");
|
|
WS_DEBUG_PRINTLN(descriptor.i2c_device_address, HEX);
|
|
return false;
|
|
}
|
|
|
|
// Optionally configure the I2C MUX
|
|
uint32_t mux_channel = driver->GetMuxChannel();
|
|
WS_DEBUG_PRINTLN(mux_channel);
|
|
if (driver->HasMux()) {
|
|
ConfigureMuxChannel(mux_channel, driver->HasAltI2CBus());
|
|
}
|
|
|
|
// Determine which driver cb function to use
|
|
if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg ==
|
|
wippersnapper_i2c_I2cDeviceOutputWrite_write_led_backpack_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Writing to LED backpack...");
|
|
driver->WriteMessage(_i2c_model->GetI2cDeviceOutputWriteMsg()
|
|
->output_msg.write_led_backpack.message);
|
|
} else if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg ==
|
|
wippersnapper_i2c_I2cDeviceOutputWrite_write_char_lcd_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Writing to char LCD...");
|
|
if (!driver->WriteMessageCharLCD(&_i2c_model->GetI2cDeviceOutputWriteMsg()
|
|
->output_msg.write_char_lcd)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to write to char LCD!");
|
|
return false;
|
|
}
|
|
} else if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg ==
|
|
wippersnapper_i2c_I2cDeviceOutputWrite_write_oled_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Writing to SSD1306 OLED...");
|
|
// Note: In the future, we can expand this to support other OLEDs by
|
|
// creating and checking a tag within the write oled msg (e.g. SSD1327,
|
|
// etc.)
|
|
driver->WriteMessageSSD1306(_i2c_model->GetI2cDeviceOutputWriteMsg()
|
|
->output_msg.write_oled.message);
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to determine I2C Output Write type!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
@brief Implements handling for a I2cDeviceAddOrReplace message
|
|
@param stream
|
|
A pointer to the pb_istream_t stream.
|
|
@returns True if the I2cDeviceAddOrReplace message was handled
|
|
(created or replaced), False otherwise.
|
|
*/
|
|
bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) {
|
|
bool use_alt_bus = false;
|
|
bool did_set_mux_ch = false;
|
|
// Attempt to decode an I2cDeviceAddOrReplace message
|
|
WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceAddOrReplace message...");
|
|
if (!_i2c_model->DecodeI2cDeviceAddReplace(stream)) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Unable to decode I2cDeviceAddOrReplace message!");
|
|
return false;
|
|
}
|
|
|
|
wippersnapper_i2c_I2cDeviceStatus device_status =
|
|
wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_UNSPECIFIED;
|
|
// Parse out device name and descriptor
|
|
char device_name[15];
|
|
strcpy(device_name,
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_name);
|
|
wippersnapper_i2c_I2cDeviceDescriptor device_descriptor =
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_description;
|
|
|
|
// Is this an i2c output device?
|
|
bool is_output = _i2c_model->GetI2cDeviceAddOrReplaceMsg()->is_output;
|
|
|
|
// Is this a i2c GPS?
|
|
bool is_gps = _i2c_model->GetI2cDeviceAddOrReplaceMsg()->is_gps;
|
|
|
|
// TODO [Online]: Handle Replace messages by implementing the Remove handler
|
|
// first...then proceed to adding a new device
|
|
|
|
// Does the device's descriptor specify a different i2c bus?
|
|
if (strcmp(device_descriptor.i2c_bus_scl, "default") != 0) {
|
|
WS_DEBUG_PRINTLN("[i2c] Non-default I2C bus specified!");
|
|
if (_i2c_bus_alt == nullptr) {
|
|
WS_DEBUG_PRINTLN("[i2c] Initializing alternative i2c bus...");
|
|
_i2c_bus_alt = new I2cHardware(device_descriptor.i2c_bus_sda,
|
|
device_descriptor.i2c_bus_scl);
|
|
}
|
|
use_alt_bus = true;
|
|
}
|
|
|
|
// Before we do anything on the bus - was the bus initialized correctly?
|
|
if (!IsBusStatusOK(use_alt_bus)) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] I2C bus is stuck or not operational, reset the board!");
|
|
if (WsV2._sdCardV2->isModeOffline()) {
|
|
WsV2.haltErrorV2(" ", WS_LED_STATUS_ERROR_RUNTIME,
|
|
false); // doesn't return, halts
|
|
}
|
|
if (!PublishI2cDeviceAddedorReplaced(device_descriptor, device_status)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to publish message to IO!");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// I2C MUX (Case #1) - We are creating an I2C mux via the
|
|
// I2cDeviceAddorReplace message
|
|
if ((strcmp(device_name, "pca9546") == 0) ||
|
|
(strcmp(device_name, "pca9548") == 0)) {
|
|
WS_DEBUG_PRINT("[i2c] Initializing MUX driver...");
|
|
if (!InitMux(device_name, device_descriptor.i2c_mux_address, use_alt_bus)) {
|
|
// TODO [Online]: Publish back out to IO here!
|
|
WsV2.haltErrorV2("[i2c] Failed to initialize MUX driver!",
|
|
WS_LED_STATUS_ERROR_RUNTIME, false);
|
|
}
|
|
WS_DEBUG_PRINTLN("OK!");
|
|
return true;
|
|
}
|
|
|
|
// Mux case #2 - We are creating a new driver that USES THE MUX via
|
|
// I2cDeviceAddorReplace message
|
|
if (device_descriptor.i2c_mux_address != 0x00) {
|
|
if (_i2c_bus_alt->HasMux() || _i2c_bus_default->HasMux()) {
|
|
WS_DEBUG_PRINT("[i2c] Configuring MUX channel: ");
|
|
WS_DEBUG_PRINTLN(device_descriptor.i2c_mux_channel);
|
|
ConfigureMuxChannel(device_descriptor.i2c_mux_channel, use_alt_bus);
|
|
did_set_mux_ch = true;
|
|
} else {
|
|
WsV2.haltErrorV2("[i2c] Device requires a MUX but MUX not present "
|
|
"within config.json!",
|
|
WS_LED_STATUS_ERROR_RUNTIME, false);
|
|
}
|
|
}
|
|
|
|
WS_DEBUG_PRINTLN("Creating a new I2C driver");
|
|
// Assign I2C bus
|
|
TwoWire *bus = nullptr;
|
|
if (use_alt_bus) {
|
|
bus = _i2c_bus_alt->GetBus();
|
|
} else {
|
|
bus = _i2c_bus_default->GetBus();
|
|
}
|
|
|
|
// Attempt to create the driver
|
|
bool did_init = false;
|
|
drvBase *drv = nullptr;
|
|
drvOutputBase *drv_out = nullptr;
|
|
GPSController *drv_uart_gps = nullptr;
|
|
|
|
if (is_output) {
|
|
WS_DEBUG_PRINT("[i2c] Creating an I2C output driver...");
|
|
drv_out = CreateI2cOutputDrv(
|
|
device_name, bus, device_descriptor.i2c_device_address,
|
|
device_descriptor.i2c_mux_channel, device_status);
|
|
if (drv_out != nullptr) {
|
|
did_init = true;
|
|
}
|
|
WS_DEBUG_PRINTLN("OK!");
|
|
} else if (is_gps) {
|
|
WS_DEBUG_PRINT("[i2c] Creating a GPS driver...");
|
|
if (!WsV2._gps_controller->AddGPS(
|
|
bus, device_descriptor.i2c_device_address,
|
|
&_i2c_model->GetI2cDeviceAddOrReplaceMsg()->gps_config)) {
|
|
did_init = false;
|
|
WS_DEBUG_PRINTLN("FAILURE!");
|
|
} else {
|
|
did_init = true;
|
|
WS_DEBUG_PRINTLN("OK!");
|
|
// TODO: We are doing an early-out here and should publish back to IO!
|
|
return true;
|
|
}
|
|
} else {
|
|
drv = CreateI2cSensorDrv(device_name, bus,
|
|
device_descriptor.i2c_device_address,
|
|
device_descriptor.i2c_mux_channel, device_status);
|
|
if (drv != nullptr) {
|
|
did_init = true;
|
|
}
|
|
}
|
|
|
|
if (!did_init) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: I2C driver failed to initialize!");
|
|
if (WsV2._sdCardV2->isModeOffline()) {
|
|
WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set "
|
|
"the correct value for i2cDeviceName?\n\tDid you set "
|
|
"the correct value for"
|
|
"i2cDeviceAddress?",
|
|
WS_LED_STATUS_ERROR_RUNTIME, false);
|
|
}
|
|
}
|
|
|
|
// Attempt to initialize the driver
|
|
if (did_set_mux_ch) {
|
|
if (!is_output) {
|
|
drv->SetMuxAddress(device_descriptor.i2c_mux_address);
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] Setting MUX address for output driver...");
|
|
drv_out->SetMuxAddress(device_descriptor.i2c_mux_address);
|
|
}
|
|
WS_DEBUG_PRINTLN("[i2c] Set driver to use MUX");
|
|
}
|
|
if (use_alt_bus) {
|
|
if (!is_output) {
|
|
drv->EnableAltI2CBus(_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_device_description.i2c_bus_scl,
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_device_description.i2c_bus_sda);
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] Setting alt. I2C bus for output driver...");
|
|
drv_out->EnableAltI2CBus(_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_device_description.i2c_bus_scl,
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_device_description.i2c_bus_sda);
|
|
}
|
|
WS_DEBUG_PRINTLN("[i2c] Set driver to use Alt I2C bus");
|
|
}
|
|
// Configure the driver
|
|
|
|
if (!is_output) {
|
|
// Configure Input-driver settings
|
|
drv->EnableSensorReads(
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_sensor_types,
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_device_sensor_types_count);
|
|
drv->SetSensorPeriod(
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_period);
|
|
} else {
|
|
WS_DEBUG_PRINTLN("[i2c] Configuring output driver...");
|
|
// Configure Output-driver settings
|
|
pb_size_t config =
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_output_add.which_config;
|
|
if (config ==
|
|
wippersnapper_i2c_output_I2cOutputAdd_led_backpack_config_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Configuring LED backpack...");
|
|
wippersnapper_i2c_output_LedBackpackConfig cfg =
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_output_add.config.led_backpack_config;
|
|
WS_DEBUG_PRINT("[i2c] Got cfg, calling ConfigureI2CBackpack...");
|
|
drv_out->ConfigureI2CBackpack(cfg.brightness, cfg.alignment);
|
|
WS_DEBUG_PRINTLN("OK!");
|
|
} else if (config ==
|
|
wippersnapper_i2c_output_I2cOutputAdd_char_lcd_config_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Configuring char LCD...");
|
|
} else if (config ==
|
|
wippersnapper_i2c_output_I2cOutputAdd_oled_config_tag) {
|
|
WS_DEBUG_PRINTLN("[i2c] Configuring OLED...");
|
|
wippersnapper_i2c_output_OledConfig cfg =
|
|
_i2c_model->GetI2cDeviceAddOrReplaceMsg()
|
|
->i2c_output_add.config.oled_config;
|
|
WS_DEBUG_PRINT("[i2c] Got cfg, calling ConfigureOLED...");
|
|
drv_out->ConfigureSSD1306(cfg.width, cfg.height, cfg.font_size);
|
|
WS_DEBUG_PRINTLN("OK!");
|
|
} else {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Unknown config specified for output driver!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!is_output) {
|
|
if (!drv->begin()) {
|
|
if (WsV2._sdCardV2->isModeOffline()) {
|
|
WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set "
|
|
"the correct value for i2cDeviceName?\n\tDid you set "
|
|
"the correct value for"
|
|
"i2cDeviceAddress?",
|
|
WS_LED_STATUS_ERROR_RUNTIME, false);
|
|
}
|
|
}
|
|
_i2c_drivers.push_back(drv);
|
|
} else {
|
|
if (!drv_out->begin()) {
|
|
if (WsV2._sdCardV2->isModeOffline()) {
|
|
WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set "
|
|
"the correct value for i2cDeviceName?\n\tDid you set "
|
|
"the correct value for"
|
|
"i2cDeviceAddress?",
|
|
WS_LED_STATUS_ERROR_RUNTIME, false);
|
|
}
|
|
}
|
|
_i2c_drivers_output.push_back(drv_out);
|
|
}
|
|
|
|
WS_DEBUG_PRINTLN("[i2c] Driver initialized and added to controller: ");
|
|
WS_DEBUG_PRINTLN(device_name);
|
|
|
|
// If we're using a MUX, clear the channel for any subsequent bus
|
|
// operations that may not involve the MUX
|
|
if (did_set_mux_ch) {
|
|
if (use_alt_bus) {
|
|
_i2c_bus_alt->ClearMuxChannel();
|
|
} else {
|
|
_i2c_bus_default->ClearMuxChannel();
|
|
}
|
|
}
|
|
|
|
if (WsV2._sdCardV2->isModeOffline() != true) {
|
|
// Create and publish the I2cDeviceAddedorReplaced message to the broker
|
|
WS_DEBUG_PRINTLN("[i2c] MQTT Publish I2cDeviceAddedorReplaced not yet "
|
|
"implemented!");
|
|
// TODO!
|
|
/* if (!PublishI2cDeviceAddedorReplaced(device_descriptor,
|
|
device_status)) return false; */
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/*!
|
|
@brief Returns the I2C bus object.
|
|
@param is_alt_bus
|
|
True if the alternative I2C bus is being used, False
|
|
otherwise.
|
|
@returns A pointer to the I2C bus object.
|
|
*/
|
|
/***********************************************************************/
|
|
TwoWire *I2cController::GetI2cBus(bool is_alt_bus) {
|
|
if (is_alt_bus) {
|
|
return _i2c_bus_alt->GetBus();
|
|
}
|
|
return _i2c_bus_default->GetBus();
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/*!
|
|
@brief Returns an i2c address of a device found on the bus.
|
|
@param index
|
|
The index of the scanned device within scan_results.
|
|
@returns The I2C device address of the scanned device.
|
|
*/
|
|
/***********************************************************************/
|
|
uint32_t I2cController::GetScanDeviceAddress(int index) {
|
|
if (index < 0 || index >= _scan_results.i2c_bus_found_devices_count)
|
|
return 0;
|
|
return _scan_results.i2c_bus_found_devices[index].i2c_device_address;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/*!
|
|
@brief Gets the number of devices found on the bus.
|
|
@returns The number of devices found on the bus.
|
|
*/
|
|
/***********************************************************************/
|
|
size_t I2cController::GetScanDeviceCount() {
|
|
return _scan_results.i2c_bus_found_devices_count;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/*!
|
|
@brief Scans the I2C bus for devices and stores the results.
|
|
@param default_bus
|
|
True to scan the default I2C bus, False to scan the
|
|
alternative I2C bus.
|
|
@returns True if the I2C bus was successfully scanned, False
|
|
if the scan failed with an error.
|
|
*/
|
|
/***********************************************************************/
|
|
bool I2cController::ScanI2cBus(bool default_bus = true) {
|
|
// zero-out the scan I2cBusScanned message before attempting a scan
|
|
_scan_results = wippersnapper_i2c_I2cBusScanned_init_zero;
|
|
|
|
// Scan the desired i2c bus
|
|
if (default_bus) {
|
|
if (_i2c_bus_default->GetBusStatus() !=
|
|
wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) {
|
|
_i2c_bus_default->InitBus(default_bus);
|
|
}
|
|
return _i2c_bus_default->ScanBus(&_scan_results);
|
|
} else {
|
|
if (_i2c_bus_alt->GetBusStatus() !=
|
|
wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) {
|
|
_i2c_bus_alt->InitBus(default_bus);
|
|
}
|
|
return _i2c_bus_alt->ScanBus(&_scan_results);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@brief Enables a MUX channel on the appropriate I2C bus.
|
|
@param mux_channel
|
|
Desired MUX channel to enable
|
|
@param is_alt_bus
|
|
True if an alternative I2C bus is being used, False otherwise.
|
|
*/
|
|
void I2cController::ConfigureMuxChannel(uint32_t mux_channel, bool is_alt_bus) {
|
|
if (is_alt_bus) {
|
|
_i2c_bus_alt->ClearMuxChannel(); // sanity-check
|
|
_i2c_bus_alt->SelectMuxChannel(mux_channel);
|
|
return;
|
|
}
|
|
_i2c_bus_default->ClearMuxChannel(); // sanity-check
|
|
_i2c_bus_default->SelectMuxChannel(mux_channel);
|
|
}
|
|
|
|
/*!
|
|
@brief Prints all drivers attached to the I2C controller.
|
|
*/
|
|
void I2cController::PrintAllDrivers() {
|
|
WS_DEBUG_PRINTLN("[i2c] Printing all drivers...");
|
|
for (drvBase *drv : _i2c_drivers) {
|
|
drv->printSensorInfo();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@brief Handles polling, reading, and logger for i2c devices
|
|
attached to the I2C controller.
|
|
*/
|
|
void I2cController::update() {
|
|
// WS_DEBUG_PRINTLN("[i2c] Updating I2C controller...");
|
|
if (_i2c_drivers.empty())
|
|
return; // bail out if no drivers exist
|
|
|
|
for (auto *drv : _i2c_drivers) {
|
|
// Does this driver have any enabled sensors?
|
|
size_t sensor_count = drv->GetEnabledSensorCnt();
|
|
if (sensor_count == 0)
|
|
continue; // bail out if driver has no sensors enabled
|
|
|
|
// Did driver's period elapse yet?
|
|
ulong cur_time = millis();
|
|
if (cur_time - drv->GetSensorPeriodPrv() < drv->GetSensorPeriod()) {
|
|
continue; // bail out if the period hasn't elapsed yet
|
|
}
|
|
|
|
// Optionally configure the I2C MUX
|
|
uint32_t mux_channel = drv->GetMuxChannel();
|
|
WS_DEBUG_PRINTLN(mux_channel);
|
|
if (drv->HasMux())
|
|
ConfigureMuxChannel(mux_channel, drv->HasAltI2CBus());
|
|
|
|
// Read the driver's sensors
|
|
_i2c_model->ClearI2cDeviceEvent();
|
|
for (size_t i = 0; i < sensor_count; i++) {
|
|
sensors_event_t event = {0};
|
|
// Attempt to call driver's read handler function
|
|
if (!drv->GetSensorEvent(drv->_sensors[i], &event)) {
|
|
WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to read sensor!");
|
|
continue;
|
|
}
|
|
// Fill the I2cDeviceEvent's sensor_event array submsg.
|
|
_i2c_model->AddI2cDeviceSensorEvent(event, drv->_sensors[i]);
|
|
}
|
|
|
|
// Configure the DeviceEvent's DeviceDescription sub-msg
|
|
_i2c_model->SetI2cDeviceEventDeviceDescripton(
|
|
drv->GetPinSCL(), drv->GetPinSDA(), (uint32_t)drv->GetAddress(),
|
|
drv->GetMuxAddress(), mux_channel);
|
|
_i2c_model->EncodeI2cDeviceEvent();
|
|
|
|
// Handle the DeviceEvent message
|
|
if (WsV2._sdCardV2->isModeOffline()) {
|
|
if (!WsV2._sdCardV2->LogI2cDeviceEvent(_i2c_model->GetI2cDeviceEvent())) {
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] ERROR: Unable to log the I2cDeviceEvent to SD!");
|
|
statusLEDSolid(WS_LED_STATUS_FS_WRITE);
|
|
}
|
|
} else {
|
|
// TODO: This needs to be implemented for online mode
|
|
WS_DEBUG_PRINTLN(
|
|
"[i2c] MQTT Publish I2cDeviceEvent not yet implemented!");
|
|
}
|
|
|
|
cur_time = millis();
|
|
drv->SetSensorPeriodPrv(cur_time);
|
|
}
|
|
} |