Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API V2 - I2C Review #693

Open
wants to merge 313 commits into
base: main
Choose a base branch
from
Open

API V2 - I2C Review #693

wants to merge 313 commits into from

Conversation

brentru
Copy link
Member

@brentru brentru commented Feb 13, 2025

Resolves #640

Pull Request Scope

  • Added new classes for interfacing with I2C drivers within src/i2c and refactored all existing drivers to fit the new modified driver base class (drvBase) within src/i2c/drivers
    • Major Changes:
      • This is a breaking change and complete rewrite of WipperSnapper's I2C component
      • I2C components now use the latest version of the i2c.proto message API
      • I2C drivers may optionally be created on an alternative I2C port
      • I2C drivers may optionally be created on an I2C Multiplexer
      • I2C drivers now share one-period-per-device, rather than multiple-periods-per-device
      • I2C components may be (optionally) initialized from a config.json file on an SD card
      • I2C components may now (optionally) log data to an SD card
      • I2C components log data into a unified SensorEvent message, from sensor.proto. This message can then be - published to MQTT, logged to sd, and/or published to the serial
  • Added handler for parsing I2C messages within a config.json file into a protobuf message within class ws_sd

This pull request only covers "offline mode" functionality, interfaces for publishing back to the MQTT broker have not yet been fully implemented.

Code Review Scope

This pull request adds the following new files within src/i2c/ and replaces all existing files:

I2C Model - Provides interfaces for interacting with messages within the i2c.proto API

  • model.cpp
  • model.h

I2C Hardware - Manages low-level i2c bus functionality

  • hardware.cpp
  • hardware.h

I2C Controller - Manages high-level functionality for handling I2C messages

  • controller.cpp
  • controller.h

There are also refactored drivers the within src/i2c/drivers/ folder. I do not expect all of the drivers within this folder to be reviewed as a subset was tested for functionality. However, drvBase.h should be reviewed.

drvBmp280.h
drvAdt7410.h
drvAhtx0.h
drvBase.h
drvBh1750.h
drvBme280.h
drvBme680.h
drvBmp3xx.h
drvDps310.h
drvDs2484.h
drvEns160.h
drvHts221.h
drvHtu21d.h
drvIna219.h
drvLc709203f.h
drvLps3xhw.h
drvLps22hb.h
drvLps25hb.h
drvLtr329_Ltr303.h
drvLtr390.h
drvMax1704x.h
drvMcp3421.h
drvMCP9808.h
drvMpl115a2.h
drvMprls.h
drvMs8607.h
drvNau7802.h
drvPct2075.h
drvPm25.h
drvScd4x.h
drvScd30.h
drvSen5x.h
drvSgp40.h
drvSht3x.h
drvSht4x.h
drvShtc3.h
drvSi7021.h
drvStemmaSoil.h
drvTmp117.h
drvTsl2591.h
drvVeml7700.h
drvVl53l0x.h
drvVl53l1x.h
drvVl53l4cd.h
drvVl53l4cx.h
drvVl6180x.h
drvVncl4020.h
drvVncl4040.h

Modifies functions within the ws_sdcard class:

  • parseConfigFile()

Adds new functions within the ws_sdcard class:

  • ParseI2cDeviceAddReplace()
  • LogI2cDeviceEvent()

(Optional) Hardware Testing

If you'd like to test this on hardware, I suggest building the firmware using PlatformIO. I have tested on: QT Py ESP32-S2 + RTC + SD BFF, Raspberry Pi Pico 2

Notes:

  • The config.json should reside on the WIPPER drive, not the MicroSD card.
  • Ensure that the RTC has a battery and the the MicroSD card is inserted prior to running WipperSnapper
  • For each of the configuration files, please note that you will need to modify rtc and sd_cs_pin variables to match the name of the RTC you are using and the pin the MicroSD Card breakout is utilizing
        "rtc": "DS3231",
        "sd_cs_pin": 17,

Example config.json - SHTC3 on default I2C Port
shtc3.json

Example config.json - MCP9808 connected to PCA9546 MUX on an alternate I2C Port
muxpca-mcp.json

Example config.json - BME6xx
muxpca-mcp.json

Example config.json - AHT20
aht20.json

Example config.json - 2x Sensors on one bus, BME280 and TSL2591
config-bme280-tsl.json

_msg_i2c_device_event
.i2c_device_events[_msg_i2c_device_event.i2c_device_events_count]
.type = sensor_type;
float value = GetValueFromSensorsEvent(sensor_type, &event);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure all adafruit_sensor value types will always be representable by a float, made me wonder what does a boolean become in float_value representation? Is it just nearly 1 or 0?
I don't thing we have a binary input yet, like a button over i2c, but it's on the roadmap (polling i2c in each loop, as though something was an analog pin over i2c for an ADC, or a button / keypress / rotary encoder change from seesaw device etc)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good point. The float is derived from the struct sensors_event_t in Adafruit_Sensor. However, I do not see any issue adding a bool type to that union (i.e: bool_value) as the union does not care about the type, only that one is selected, and we are "flagging" that it is a boolean by using a sensor type enum.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is something we should consider or implement right now?

void I2cHardware::InitBus(bool is_default, const char *sda, const char *scl) {
uint8_t pin_sda, pin_scl;
if (!is_default && (sda == nullptr || scl == nullptr)) {
_bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_ERROR_WIRING;
Copy link
Contributor

@tyeth tyeth Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be the wrong message, if nullptr then we haven't set the alt bus sda/scl pins, i.e. corrupt protobuf msg from offline config?

Copy link
Member Author

@brentru brentru Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what is better here.

What I2cBusStatus should we set instead? We could also add to I2cBusStatus:
https://github.com/adafruit/Wippersnapper_Protobuf/blob/api-v2/proto/wippersnapper/i2c.proto#L10

#if defined(PIN_I2C_POWER)
// turn on the I2C power by setting pin to opposite of 'rest state'
pinMode(PIN_I2C_POWER, INPUT);
delay(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work reliably, should we increase the time to stablise to 5ms, and could that help for the Feather S2 BME280 situation (I took a peek at support and twiddling gpio7/i2cPower as a component didn't help)? There is also a PIN_I2C_POWER_INVERTED defined as pin7 for the revTft S2 feather (probably same situation) which appears unused in code (v1)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work reliably, should we increase the time to stablise to 5ms,
The code here follows the variant.cpp for each board (https://github.com/espressif/arduino-esp32/blob/master/variants/adafruit_feather_esp32s2/variant.cpp#L37). I don't want to modify this to be out-of-sync with this file.

There is also a PIN_I2C_POWER_INVERTED defined as pin7 for the revTft S2 feather (probably same situation) which appears unused in code (v1)

I am confused. For the RevTFT S2, the variant only defines a TFT_I2C_POWER pin
https://github.com/espressif/arduino-esp32/blob/master/variants/adafruit_feather_esp32s2_reversetft/variant.cpp#L36:37

And we handle it here in the code:

_bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_ERROR_HANG;
return;
}
_bus->setClock(50000);
Copy link
Contributor

@tyeth tyeth Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is from V1, I wonder if we need this optionally per sensor or something, but I was sure there were a couple with very strict requirements (couple needed 100k in theory and maybe another one had issues except around 20k). Ignore me for now, but if it rings a bell or comes up again then shout.
In my head we were running at 100kHz for everything, but I must have been thinking of circuitpython or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The specification was to run at 500kHz for everything on the bus. API v1 did contain a message that sets the I2C bus' frequency (https://github.com/adafruit/Wippersnapper_Protobuf/blob/master/proto/wippersnapper/i2c/v1/i2c.proto#L45) but since we are always sending 500kHz in that message, it got dropped in API v2.

@brentru
Copy link
Member Author

brentru commented Feb 21, 2025

@tyeth Noting that we discussed the following over Slack Huddle:
a) A potentially unused constructor in drvBase (the one w/o mux ch. defined)
b) drvBase inherited classes potentially should not have anything in their constructors

@brentru
Copy link
Member Author

brentru commented Feb 24, 2025

The two issues mentioned above have been resolved.

Copy link

github-actions bot commented Feb 24, 2025

Test Results

6 tests   6 ✅  1m 5s ⏱️
1 suites  0 💤
1 files    0 ❌

Results for commit 5ac35f3.

♻️ This comment has been updated with latest results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor I2C Component for API v2 Update MQTT topics for api-v2
2 participants