| name | add-sensor-component-v1 |
| description | Guides adding a new I2C sensor component to Adafruit IO WipperSnapper v1 firmware and the Wippersnapper_Components definition repo. Use this skill whenever the user wants to add a new sensor, create a WipperSnapper driver, register an I2C device, or contribute a component to WipperSnapper — even if they just mention a sensor name and "wippersnapper" in the same breath. Covers the full workflow: driver code, registration, component definition JSON, library deps, build verification, clang-format, and PR creation for both repos.
|
| postRun | Automatically use run_in_terminal to execute `clang-format -i` on the modified files, and run `doxygen` (e.g., `doxygen Doxyfile` or manually) to validate documentation blocks. Review the terminal output, identify any warnings or errors on the modified files, and apply fixes before continuing. You can pip install clang-format 20 like GH runners (venv).
|
Add I2C Sensor Component to WipperSnapper v1
Hard Gates — violations of these rules produce broken drivers
These rules exist because agents have previously skipped the learn guide, guessed GitHub URLs for
libraries, silently ignored 404s, and constructed library names from repo names instead of looking
them up. Each of these produced drivers that either called non-existent APIs or failed to compile
because the dependency couldn't be resolved.
-
Learn guide is a prerequisite, not a nice-to-have. You must find and read the Adafruit
learn guide for the product before writing any driver code. The research chain is:
web search for product page → extract learn guide URL from the product page → fetch the
.md?view=all version of the learn guide → extract the Arduino library name from the
## Arduino section. If you cannot find the learn guide after reasonable effort, STOP and
ask the user for the library name and API details. Do not guess library names from chip or
sensor part numbers — boards with a known chip onboard may have a dedicated wrapper library
with a completely different name and API.
-
Library names come from library.properties, not from the repo name. The GitHub repo name
and the Arduino/PlatformIO library name are often different (e.g. repo Adafruit_AS7331 →
library Adafruit AS7331 Library). An agent once guessed adafruit/Adafruit AS7331 for
platformio.ini and got UnknownPackageError: Could not find the package. The fix is simple:
in Step 0c, fetch the library's library.properties file and read its name= field. Use that
exact string everywhere — platformio.ini lib_deps and the firmware's library.properties
depends= line.
-
A 404 or failed fetch is a red flag, not an inconvenience to route around. If any WebFetch,
curl, or gh API call for a library header, example sketch, .cpp file, or learn guide returns
a 404, timeout, or error: immediately report the failure to the user with the URL that
failed and what you were trying to find. Do not silently fall through to the next step. Do not
write driver code based on partial or unverified information. A 404 on an expected GitHub URL
often means the library doesn't exist, has a different repo name, or the file structure differs
from what you assumed. Any of these will produce a driver that doesn't compile.
Changes span two repositories:
- Adafruit_Wippersnapper_Arduino — C++ firmware: new driver + registration
- Wippersnapper_Components — JSON definition + product image
The user supplies a sensor name (e.g. "TMP119"). Research the sensor, find the Adafruit Arduino
library, identify the closest existing driver, and walk through every file change.
Naming convention
- PascalCase for C++: class
WipperSnapper_I2C_Driver_TMP119, file
WipperSnapper_I2C_Driver_TMP119.h, pointer _tmp119
- lowercase for component folder and
strcmp string: tmp119
Decide the canonical name in Step 0 and use it everywhere.
Proto files are off-limits. Only Adafruit staff modify .proto files.
Reference
The official Adafruit guide for this process:
Fetch the .md?view=all single page version (not subpage) if you need more detail on any step — particularly for the products learn guide, along with the
Wippersnapper_Components repo setup, image requirements, and testing in Adafruit IO.
Arguments
This skill accepts a sensor name as its argument (e.g. /add-sensor-component-v1 TMP119).
Ideally the user will provide the datasheet, product purchase url, wiki / learn guide url, and arduino driver library name. If not, the skill will research these based on the sensor name, attempting to load the adafruit product page to then find the almost-always-present learn guide link which mentions the needed arduino library (GH repo link and example code sketch link in GH).
Environment Check (optional, do not block on this)
If Bash is available, quickly check connectivity to adapt your approach. If Bash is not
available or these commands fail, skip this section and proceed — use WebFetch/WebSearch for
research, or playwright if necessary, and do the code changes with the file tools you have.
The user can handle git/PRs. NOTE: Do not leave files in wippersnapper_components as untracked - instead if unable to fork / PR components repo then attach image to summary and component definition
curl -s -o /dev/null -w "%{http_code}" https://www.bbc.com
curl -s -o /dev/null -w "%{http_code}" https://api.github.com
git ls-remote https://github.com/adafruit/Wippersnapper_Components HEAD
gh auth status
| Result | Capability |
|---|
| All succeed | Full access — use gh for forking, PRs, API queries |
| BBC fails, GitHub API works | Restricted web but gh works — skip web fetches for product pages |
| BBC + API fail, git works | Git-only — use git clone/git push, create PRs manually |
| All fail / no Bash | Offline — write code with file tools, user handles git/PRs |
CI Checks
PRs to both repos run CI. Key checks to pass before submitting:
Adafruit_Wippersnapper_Arduino:
- clang-format — code formatting must match
.clang-format config. Run clang-format -i on all changed files.
- Doxygen — all public/protected methods need Doxygen-style
/*! @brief ... */ comment blocks. CI will fail if these are missing or malformed. Follow the existing driver style exactly.
- Build — firmware must compile for all target boards in
platformio.ini.
Wippersnapper_Components:
- JSON schema validation —
definition.json must conform to schema.json in the repo root.
- Image validation — dimensions, file size, and format are checked.
Step 0 — Research the Sensor MUST BE DONE BEFORE WRITING ANY CODE
Before writing any code, gather this information. Steps 0a–0c are sequential gates — each
must succeed before moving to the next. If any gate fails, stop and ask the user.
0a. Find the product page
Search the web for "adafruit " to find the product page. The product page
(https://www.adafruit.com/product/<PRODUCT_ID>) html data has links to the learn guide (do not
use the API or guess URLs). Use a tool like wget or curl:
curl -sL https://www.adafruit.com/product/5817 | grep "learn.adafruit.com"
(Replace 5817 with the actual product ID found via search.)
If this fails: try WebSearch for "adafruit site:adafruit.com". If you still can't
find a product page, ask the user for the product URL or learn guide link.
0b. Find and read the learn guide (GATE — cannot proceed without this)
Extract the learn guide URL from the product page. Then fetch the text view version:
https://learn.adafruit.com/<guide-slug>.md?view=all
If the .md?view=all URL 404s, try:
- The HTML version:
https://learn.adafruit.com/<guide-slug>?view=all
- WebSearch for
"learn.adafruit.com" "<SENSOR>" to find the correct slug
- Ask the user
From the learn guide, extract:
- The exact Arduino library name from the
## Arduino section
- The example code link (usually a GitHub URL in the
## Example Code section)
- The I2C address(es)
- What the sensor measures (map to subcomponent types)
If you cannot find or read the learn guide after trying all fallbacks above, STOP and ask the
user. Do not guess the library name from the sensor part number.
0c. Confirm the library exists and get its exact registered name
Verify the library by checking at least one of:
gh search repos "<Library Name>" --owner adafruit
- WebFetch the library's GitHub page or header file
- The library appears in the Arduino Library Manager
If the library cannot be found, it may not be released yet, or it may have a different name
than expected. Ask the user before proceeding.
Then, fetch the library's library.properties to get the exact registered name:
gh api repos/adafruit/<Repo>/contents/library.properties --jq '.content' | base64 -d
Extract the name= value — this is the canonical library name used by both the Arduino
Library Manager and PlatformIO registry. It often differs from the GitHub repo name (e.g. repo
Adafruit_AS7331 → library name Adafruit AS7331 Library; repo Adafruit_TMP117 → library
name Adafruit TMP117). Guessing the library name from the repo name will produce "package not
found" build failures. Record this name and use it verbatim in Step 4 for both platformio.ini
and the firmware's library.properties.
0d. Gather remaining details
| What | Where to look |
|---|
| Library API | Read the library header on GitHub — find begin() signature and sensor read methods (getEvent, readTempC, etc.) |
| I2C addresses | Sensor datasheet or Adafruit product page or learn guide or driver. Check https://learn.adafruit.com/i2c-addresses/the-list |
| What it measures | Datasheet — map each reading to a subcomponent type (see table below) |
| Closest existing driver | Browse src/components/i2c/drivers/ for a sensor in the same family or with identical reading types |
| Documentation URL | Prefer: Adafruit learn guide (from product page) > manufacturer datasheet. Non-Adafruit products are accepted — use the manufacturer's product/datasheet URL. Note: third-party domain URLs may initially fail CI URL validation until a maintainer adds the domain to the allowlist. |
Subcomponent type reference
Valid subcomponents values in definition.json, mapping 1:1 to base driver getEvent*() methods:
| Subcomponent | getEvent method | sensors_event_t field | SI unit |
|---|
ambient-temp | getEventAmbientTemp | .temperature | °C |
ambient-temp-fahrenheit | getEventAmbientTempF | .temperature | °F |
humidity | getEventRelativeHumidity | .relative_humidity | %RH |
pressure | getEventPressure | .pressure | hPa |
altitude | getEventAltitude | .altitude | m |
co2 | getEventCO2 | .CO2 | ppm |
eco2 | getEventECO2 | .eCO2 | ppm |
tvoc | getEventTVOC | .tvoc | ppb |
gas-resistance | getEventGasResistance | .gas_resistance | Ω |
light | getEventLight | .light | lux |
proximity | getEventProximity | .data[0] | unitless |
voltage | getEventVoltage | .voltage | V |
current | getEventCurrent | .current | A |
raw | getEventRaw | .data[0] | unitless |
pm10-std | getEventPM10_STD | .data[0] | µg/m³ |
pm25-std | getEventPM25_STD | .data[0] | µg/m³ |
pm100-std | getEventPM100_STD | .data[0] | µg/m³ |
unitless-percent | getEventUnitlessPercent | .data[0] | % |
object-temp | getEventObjectTemp | .temperature | °C |
object-temp-fahrenheit | getEventObjectTempF | .temperature | °F |
When using raw reads (not Unified Sensor getEvent()), assign to the correct field above, e.g.
tempEvent->temperature = _sensor->readTempC();
Temperature sensors almost always include both ambient-temp and ambient-temp-fahrenheit.
Fahrenheit: getEventAmbientTempF and getEventObjectTempF are in the base class — they
call the Celsius method and convert. Only implement the Celsius version. Never implement °F.
Read-and-cache requirement: The calling order of getEvent*() methods is not guaranteed (°F
may be called before °C). Every getEvent*() must go through a shared _readSensor() with a
millis-based time guard so only the first call per cycle does the I2C read; subsequent calls
return cached data. Cache millis() once at the top of the function (e.g.
unsigned long now = millis();) and use the cached value for both the time guard check and
setting _lastRead — never call millis() twice.
See the driver template in Step 1 and WipperSnapper_I2C_Driver_SCD30.h and SGP30
for the canonical patterns.
This is especially important for multi-reading sensors but also applies to temperature sensors
where both °C and °F subcomponents are enabled.
Step 1 — Create the Driver Header
File: src/components/i2c/drivers/WipperSnapper_I2C_Driver_<SENSOR>.h
First: Read the library's example sketch — MANDATORY before writing any code
Do NOT assume the library API based on other sensors. Every library is different. You must
read the actual example code to know the real API. Guessing from similar sensors (e.g. assuming
SCD30-style getEvent() for an STCC4) will produce a driver that does not compile.
Before writing any driver code, find and read the library's simple test or basic_usage or all examples not using interrupts (data ready flags are okay)
on GitHub. Check all matches for suitable usage suggestions. This is your source of truth for how the sensor is meant to be used:
gh api repos/adafruit/<Library_Repo>/contents/examples --jq '.[].name'
Fallback routes (try in order if gh/Bash is unavailable):
- WebFetch raw GitHub:
https://raw.githubusercontent.com/adafruit/<Library_Repo>/main/examples/<example>/<example>.ino (you may need to access the web version (not raw) to view default branch and example related folder structure).
- WebFetch learn guide:
https://learn.adafruit.com/<guide-slug>.md?view=all — the Arduino
section usually contains example code link showing the exact API (or embedded code if non-markdown version).
- Ask the user: If tools are restricted, ask them to paste the library header and example.
If any fetch returns a 404 or error: Do not silently move to the next fallback and then
forget that all routes failed. If every fallback also fails, you have no verified API to code
against. Report all failed URLs to the user and ask them to provide the example code or
confirm the library repo name and structure. Writing driver code without having successfully
read either the example sketch or the learn guide's embedded code is forbidden — you will
produce code that calls methods which may not exist.
From the example, extract the exact method signatures used:
begin() — what arguments, what return type
- How readings are triggered (
getEvent(), readMeasurement(), measureSingleShot(), etc.)
- What the return values look like (Unified Sensor
sensors_event_t? Raw floats? uint16_t pointers?)
- Any required setup calls (continuous mode, conditioning, etc.)
- Any delays or polling (
dataReady(), fixed delays between reads)
Then: Read the library header — MANDATORY
Read the .h file to see all public methods and their exact signatures. This is essential
because:
- The example may only show one usage pattern; the header shows everything available
- You need the exact types (float vs uint16_t, pointer args vs return values)
- You need to identify default configuration set in
begin()/_init()
gh api repos/adafruit/<Library_Repo>/contents/<Library_Name>.h --jq '.content' | base64 -d
Or via WebFetch: https://raw.githubusercontent.com/adafruit/<Library_Repo>/main/<Library_Name>.h
If this fetch 404s, the repo name or header filename may differ from what you assumed. Try:
- List the repo contents:
gh api repos/adafruit/<Library_Repo>/contents --jq '.[].name'
- WebFetch the repo's main page to find the actual header filename
- Fall back to the learn guide
.md?view=all which may contain enough API detail from code snippets
If none of these work, ask the user for the header content. Do not proceed to write the
driver without having verified the actual method signatures.
Explicitly set every configuration parameter that the library defaults in begin()/_init().
This pins behavior so library updates can't silently change WipperSnapper.
For example, if the library's _init() sets continuous mode with 8x averaging as defaults:
bool begin() {
_sensor = new Adafruit_Sensor();
if (!_sensor->begin((uint8_t)_sensorAddress, _i2c))
return false;
_sensor->setMeasurementMode(CONTINUOUS);
_sensor->setAveragedSampleCount(AVERAGE_8X);
return true;
}
Then: Write the driver
This is a header-only class. Use the closest existing driver as a template. Always apply the patterns from this skill!
Good References:
- Look at
WipperSnapper_I2C_Driver_SCD30.h for a clean example of the new style, including read caching and setting explicit defaults in begin().
- Check
WipperSnapper_I2C_Driver_SGP30.h for a complete example of fastTick() usage, which is needed when the datasheet explicitly requires a minimum polling cadence for correct operation.
Key decisions when writing the driver:
-
Handling dataReady() or Data Availability: Some sensors (like the SCD30) require you to check a flag before reading to avoid stale data or blocked I2C buses.
- If the library requires polling a
dataReady() method, implement a short, limited test in your read loop.
- Never use infinite
while (!sensor.dataReady()) delay(10); loops. Since WipperSnapper runs many components concurrently, infinite loops crash the whole system if a sensor hangs.
- Instead, use a brief check (optionally with a small finite
delay and subsequent retry, like if(!dataReady) { delay(100); if(!dataReady) return false; }). See WipperSnapper_I2C_Driver_SCD30.h for a safe implementation.
-
Library API style: Some Adafruit libraries use Unified Sensor (getEvent(sensors_event_t*))
which fills the event struct directly. Others expose raw read methods like readTempC(). If the
library uses raw reads, assign to the appropriate field (e.g. tempEvent->temperature = readTempC())
and return true, or return false if the read fails.
-
Multiple sensor types: If the sensor measures more than one thing (e.g. BME280 does temp +
humidity + pressure + altitude), implement a getEvent*() for each. Each usually needs its own
Adafruit_Sensor* pointer obtained from the library (e.g. _bme->getTemperatureSensor()).
-
begin() signature: Check the library header — some take (address, wire), others take
(wire) with address set separately. Match exactly.
-
Minimum polling interval (fastTick): Check the sensor datasheet for a recommended or
required minimum polling period. Some sensors (like gas sensors) need to be read at a fixed
cadence to keep their internal algorithms running correctly — even when WipperSnapper's publish
interval is much longer. If the datasheet specifies a required polling interval, you need the
fastTick() pattern:
- Override
fastTick() — this is called once per main loop iteration by the I2C component
manager, so it runs much more frequently than the publish interval.
- Use a
millis()-based guard to enforce the datasheet cadence (non-blocking, no delay()).
- Cache the latest reading in member variables.
- Have
getEvent*() return the cached values instead of doing a fresh I2C read.
See WipperSnapper_I2C_Driver_SGP30.h for a complete example — the SGP30 datasheet requires
~1 Hz polling to maintain its IAQ algorithm.
Most simple sensors (temperature, pressure, humidity) do NOT need this — they can be read
on-demand at whatever interval WipperSnapper requests. Only use fastTick() when the
datasheet explicitly requires a minimum polling cadence for correct operation.
Step 2 — Register in WipperSnapper_I2C.h
File: src/components/i2c/WipperSnapper_I2C.h
Two changes, both in alphabetical order among existing entries:
#include "drivers/WipperSnapper_I2C_Driver_<SENSOR>.h"
WipperSnapper_I2C_Driver_<SENSOR> *_<sensor> = nullptr;
Step 3 — Add Initialization in WipperSnapper_I2C.cpp
File: src/components/i2c/WipperSnapper_I2C.cpp
Find the initI2CDevice() method. Add a new else if block in alphabetical order by device
name string. The device name must exactly match the folder name you'll create in the
Wippersnapper_Components repo.
} else if (strcmp("<sensor_name>", msgDeviceInitReq->i2c_device_name) == 0) {
_<sensor> = new WipperSnapper_I2C_Driver_<SENSOR>(this->_i2c, i2cAddress);
if (!_<sensor>->begin()) {
WS_DEBUG_PRINTLN("ERROR: Failed to initialize <SENSOR>!");
_busStatusResponse =
wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL;
return false;
}
_<sensor>->configureDriver(msgDeviceInitReq);
drivers.push_back(_<sensor>);
WS_DEBUG_PRINTLN("<SENSOR> Initialized Successfully!");
}
configureDriver() is inherited — reads sensor periods from the init request. Never reimplement.
Step 4 — Add Library Dependencies in library.properties and platformio.ini
Skip if the sensor class lives in an already-listed library (e.g. TMP119 in Adafruit_TMP117).
Use the exact name= value from the library's own library.properties that you fetched in
Step 0c. Do not construct the name from the GitHub repo name — they frequently differ
(underscores vs spaces, trailing "Library" suffix, etc.) and a wrong name causes "package not
found" build failures.
4a. platformio.ini
Add to [env] lib_deps in alphabetical order ignoring any purpose specific sections of the lib_deps, using the
adafruit/<exact library.properties name> format:
adafruit/Adafruit <Exact Name From library.properties>
https://github.com/<owner>/<repo>.git
4b. library.properties
Add the exact same name= value to the comma-separated depends= line. Add it in a logical
position among the existing entries.
Example: if the library's library.properties contains name=Adafruit FooBar Library:
depends=..., Adafruit FooBar Library, ...
GitHub-only libraries can't go in library.properties — note this in the PR description (and advise to fork n release ardu lib).
Step 5 — Wippersnapper_Components Definition
This step uses a separate repository: https://github.com/adafruit/Wippersnapper_Components
5a. Fork and clone
Clone inside the firmware repo (it's in .gitignore):
gh auth switch --user <username>
gh repo fork adafruit/Wippersnapper_Components --clone=true --remote-name upstream -- Wippersnapper_Components
Wippersnapper_Components/components/i2c/<sensor_name>/
├── definition.json
└── image.jpg (or .png, .gif, .svg)
The `<sensor_name>` folder name is lowercase and must exactly match the string used in
`strcmp()` in Step 3.
### 5c. Write definition.json
```json
{
"displayName": "<Sensor Display Name>",
"vendor": "<Vendor Name>",
"productURL": "https://www.adafruit.com/product/<ID>",
"documentationURL": "https://learn.adafruit.com/<guide-slug>",
"published": false,
"i2cAddresses": ["0xNN"],
"subcomponents": ["ambient-temp", "ambient-temp-fahrenheit"]
}
Field notes:
published — always false for new contributions. Adafruit sets it to true after release.
productURL — Adafruit product page if available, place of sale, otherwise the manufacturer's product page.
documentationURL — Adafruit learn guide (preferred), or manufacturer docs page / wiki, or datasheet URL as
fallback. Third-party domain URLs may initially fail CI URL validation until a maintainer adds
the domain to the allowlist — note this in the PR if using a non-Adafruit/non-TI URL.
i2cAddresses — hex strings, all addresses the chip can use (check datasheet for ADDR pin
configurations).
subcomponents — mixed array of simple strings or objects. Use exact type strings from the Step 0 table.
Subcomponent formats
Simple format — when the sensor type name is self-explanatory:
"subcomponents": ["ambient-temp", "ambient-temp-fahrenheit", "pressure"]
Object format — when you need a custom display name for clarity:
"subcomponents": [
{ "displayName": "Ambient Light", "sensorType": "light" },
{ "displayName": "UV Count", "sensorType": "raw" }
]
Use objects when:
- Type name is ambiguous — "light" could mean visible, UV, or IR.
displayName clarifies in the UI.
- Two readings share the same physical type — v1 schema forbids duplicate
sensorType. Use
"raw" for the second with a descriptive displayName (e.g. LTR-329: "light" for ambient,
"raw" with displayName: "Infrared" for IR).
Examples:
- LTR-390 (UV + light):
[{"displayName": "Ambient Light", "sensorType": "light"}, {"displayName": "UV Count", "sensorType": "raw"}]
- LTR-329 (visible + IR):
[{"displayName": "Ambient Light", "sensorType": "light"}, {"displayName": "Infrared", "sensorType": "raw"}]
- INA219 (no ambiguity):
["voltage", "current"]
When using "raw" as a stand-in, the driver must implement getEventRaw() for that reading.
3. Non-standard units — if the sensor reports in a non-SI unit (or doesn't match Adafruit_Sensor type SI unit) then use the appropriate unitless type or raw with a descriptive displayName including units.
4. Clarity compared to the auto UI labels or between subcomponents — compare other components using the same types for reference.
5d. Add product image
Requirements:
- Dimensions: 400px × 300px (4:3 ratio)
- File size: 3 KB – 100 KB
- Formats: jpg, jpeg, gif, png, svg
- Filename:
image.<ext>
You can usually grab the product image url details from the Adafruit product API (http://www.adafruit.com/api/product/) and resize it [4:3] and compress it (using compressjpeg.com) to pass CI validation. Ideally pick the straight-on product shot with a plain background, not angled or lifestyle images. Include a small amount of dead space around the product so it doesn't get cropped in the UI (go with existing examples for reference).
Step 6 — Build and Verify
Check if windows powershell/cmd first by testing echo $env:USERNAME %WINDIR%
windows powershell users:
. "$env:userprofile/.platformio/penv/Scripts/activate.ps1"
pio run -e adafruit_feather_esp32s3
bash/other users:
. ~/.platformio/penv/bin/activate.sh
pio run -e adafruit_feather_esp32s3
Fix any compilation errors. Common issues:
- Wrong
begin() signature — check the library header
- Missing include — verify the
#include path is correct
- Library not found — verify
platformio.ini lib_deps entry
Step 7 — Format with clang-format, ensure passes doxygen and other CI checks
clang-format -i src/components/i2c/drivers/WipperSnapper_I2C_Driver_<SENSOR>.h
Run on all modified files. The repo's .clang-format should be applied.
Ensure all doxygen style is consistent with existing drivers.
Step 8 — Create Pull Requests
Two separate PRs are needed (mention the model used for the PRs in the description):
PR 1: Wippersnapper_Components
- Branch from main, add the component folder with
definition.json and image
- PR title:
Add <SENSOR> component definition
- Wait for CI checks to pass
PR 2: Adafruit_Wippersnapper_Arduino
Step 9 — Test (if hardware available)
- Upload firmware to a WipperSnapper-compatible board
- Go to Adafruit IO device page → New Component → check "Show Dev"
- The new component appears with "In Development" badge
- Configure I2C address and reading period
- Monitor serial output for initialization success
- Verify sensor readings appear on the device page
Worked (abridged) Example: TMP119 - Do not follow this example step-by-step, it's only an abbreviated demonstration of the above process
TMP119: TI high-accuracy temperature sensor, TMP117 variant (chip ID 0x2117 vs 0x0117).
Step 0 — Research
- Search Process: Web search "adafruit TMP119" → product page https://www.adafruit.com/product/6482
which links to the learn guide https://learn.adafruit.com/adafruit-tmp119-high-precision-temperature-sensor
which shows the Arduino library is
Adafruit_TMP117. Alternatively,
gh search repos "Adafruit TMP119" --owner adafruit only finds the PCB repo — no dedicated
library. Trying TMP11 finds arduino lib, or checking Adafruit_TMP117 repo contents reveals
Adafruit_TMP119.h/.cpp — TMP119 inherits from TMP117.
- Library:
Adafruit_TMP117 (contains Adafruit_TMP119 class)
- Product URL:
https://www.adafruit.com/product/6482
- Docs URL:
https://learn.adafruit.com/adafruit-tmp119-high-precision-temperature-sensor
- Markdown learn guide:
https://learn.adafruit.com/adafruit-tmp119-high-precision-temperature-sensor.md?view=all (mentions ## Arduino section with driver repo link https://github.com/adafruit/Adafruit_TMP117 and ## Example Code using https://github.com/adafruit/Adafruit_TMP117/blob/master/examples/TMP119_basic_test/TMP119_basic_test.ino.)
- I2C addresses: 0x48, 0x49, 0x4A, 0x4B (same as TMP117, datasheet Table 7-1)
- Measures: Temperature only → subcomponents:
ambient-temp, ambient-temp-fahrenheit
- Closest driver:
WipperSnapper_I2C_Driver_TMP117.h
Step 1 — Read the example, then the library source
Example (examples/TMP119_basic_test/TMP119_basic_test.ino):
Adafruit_TMP119 tmp11x;
tmp11x.begin();
while (!tmp11x.dataReady()) delay(10);
tmp11x.getEvent(&temp);
Library source (Adafruit_TMP117.h / Adafruit_TMP119.cpp):
_init() calls reset() which restores factory defaults:
- Continuous conversion mode (
TMP117_MODE_CONTINUOUS)
- 8x averaging (
TMP117_AVERAGE_8X)
- 1000ms conversion delay (
TMP117_DELAY_1000_MS)
getEvent() internally calls waitForData() which blocks until dataReady() is true
begin(addr, wire) — address first, wire second
Decisions:
- The library's
getEvent() handles data-ready blocking internally, so no fastTick().
- Explicitly set mode and averaging in
begin() to pin the defaults.
- Only implement
getEventAmbientTemp (Celsius) — the base class handles °F conversion.
- Since the calling order of °C and °F methods is not guaranteed, use a shared read-and-cache
pattern with a time guard so only the first call per cycle hits the I2C bus:
protected:
Adafruit_TMP119 *_tmp119;
sensors_event_t _cachedTemp = {0};
unsigned long _lastRead = 0;
bool _readSensor() {
unsigned long now = millis();
if (_lastRead != 0 && now - _lastRead < ONE_SECOND_IN_MILLIS)
return true;
if (!_tmp119->getEvent(&_cachedTemp))
return false;
_lastRead = now;
return true;
}
public:
bool begin() {
_tmp119 = new Adafruit_TMP119();
if (!_tmp119->begin((uint8_t)_sensorAddress, _i2c))
return false;
_tmp119->setMeasurementMode(TMP117_MODE_CONTINUOUS);
_tmp119->setAveragedSampleCount(TMP117_AVERAGE_8X);
return true;
}
bool getEventAmbientTemp(sensors_event_t *tempEvent) {
if (!_readSensor())
return false;
*tempEvent = _cachedTemp;
return true;
}
Step 2 — Register in WipperSnapper_I2C.h
#include "drivers/WipperSnapper_I2C_Driver_TMP119.h"
WipperSnapper_I2C_Driver_TMP119 *_tmp119 = nullptr;
Step 3 — Init block in WipperSnapper_I2C.cpp
} else if (strcmp("tmp119", msgDeviceInitReq->i2c_device_name) == 0) {
_tmp119 = new WipperSnapper_I2C_Driver_TMP119(this->_i2c, i2cAddress);
if (!_tmp119->begin()) {
WS_DEBUG_PRINTLN("ERROR: Failed to initialize TMP119!");
_busStatusResponse =
wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL;
return false;
}
_tmp119->configureDriver(msgDeviceInitReq);
drivers.push_back(_tmp119);
WS_DEBUG_PRINTLN("TMP119 Initialized Successfully!");
}
Step 4 — Library dependency
platformio.ini already has adafruit/Adafruit TMP117 and library.properties already has
Adafruit TMP117 — no changes needed since TMP119 lives in that package.
Step 5 — Component definition
Wippersnapper_Components/components/i2c/tmp119/definition.json:
{
"displayName": "TMP119",
"vendor": "Texas Instruments",
"productURL": "https://www.adafruit.com/product/6482",
"documentationURL": "https://learn.adafruit.com/adafruit-tmp119-high-precision-temperature-sensor",
"published": false,
"i2cAddresses": ["0x48", "0x49", "0x4A", "0x4B"],
"subcomponents": ["ambient-temp", "ambient-temp-fahrenheit"]
}
Simple strings — unambiguous for a temperature-only sensor. Image: product API, 400x300, compress.
Files changed
| File | Action |
|---|
src/components/i2c/drivers/WipperSnapper_I2C_Driver_TMP119.h | New — driver with explicit mode/averaging config |
src/components/i2c/WipperSnapper_I2C.h | Modified — include + private pointer |
src/components/i2c/WipperSnapper_I2C.cpp | Modified — init block in initI2CDevice() |
platformio.ini | No change — TMP117 library already listed |
library.properties | No change — TMP117 library already listed |
Wippersnapper_Components/components/i2c/tmp119/definition.json | New |
Wippersnapper_Components/components/i2c/tmp119/image.jpg | New |