Skip to content

Commit 6cd8f5f

Browse files
authored
Merge pull request #903 from adafruit/uv-7331
Add Adafruit AS7331 UV A/B/C Sensor (PID 7331)
2 parents 0e8efdb + e8c59b0 commit 6cd8f5f

20 files changed

+297
-25
lines changed

.agents/skills/add-sensor-component-v1/SKILL.md

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,40 @@ postRun: >
1616

1717
# Add I2C Sensor Component to WipperSnapper v1
1818

19+
## Hard Gates — violations of these rules produce broken drivers
20+
21+
These rules exist because agents have previously skipped the learn guide, guessed GitHub URLs for
22+
libraries, silently ignored 404s, and constructed library names from repo names instead of looking
23+
them up. Each of these produced drivers that either called non-existent APIs or failed to compile
24+
because the dependency couldn't be resolved.
25+
26+
1. **Learn guide is a prerequisite, not a nice-to-have.** You must find and read the Adafruit
27+
learn guide for the product before writing any driver code. The research chain is:
28+
web search for product page → extract learn guide URL from the product page → fetch the
29+
`.md?view=all` version of the learn guide → extract the Arduino library name from the
30+
`## Arduino` section. If you cannot find the learn guide after reasonable effort, **STOP and
31+
ask the user** for the library name and API details. Do not guess library names from chip or
32+
sensor part numbers — boards with a known chip onboard may have a dedicated wrapper library
33+
with a completely different name and API.
34+
35+
2. **Library names come from `library.properties`, not from the repo name.** The GitHub repo name
36+
and the Arduino/PlatformIO library name are often different (e.g. repo `Adafruit_AS7331`
37+
library `Adafruit AS7331 Library`). An agent once guessed `adafruit/Adafruit AS7331` for
38+
`platformio.ini` and got `UnknownPackageError: Could not find the package`. The fix is simple:
39+
in Step 0c, fetch the library's `library.properties` file and read its `name=` field. Use that
40+
exact string everywhere — `platformio.ini` `lib_deps` and the firmware's `library.properties`
41+
`depends=` line.
42+
43+
3. **A 404 or failed fetch is a red flag, not an inconvenience to route around.** If any WebFetch,
44+
curl, or gh API call for a library header, example sketch, .cpp file, or learn guide returns
45+
a 404, timeout, or error: **immediately report the failure to the user** with the URL that
46+
failed and what you were trying to find. Do not silently fall through to the next step. Do not
47+
write driver code based on partial or unverified information. A 404 on an expected GitHub URL
48+
often means the library doesn't exist, has a different repo name, or the file structure differs
49+
from what you assumed. Any of these will produce a driver that doesn't compile.
50+
51+
---
52+
1953
Changes span **two repositories**:
2054
1. **Adafruit_Wippersnapper_Arduino** — C++ firmware: new driver + registration
2155
2. **Wippersnapper_Components** — JSON definition + product image
@@ -86,13 +120,66 @@ PRs to both repos run CI. Key checks to pass before submitting:
86120

87121
## Step 0 — Research the Sensor **MUST BE DONE BEFORE WRITING ANY CODE**
88122

89-
Before writing any code, gather this information:
123+
Before writing any code, gather this information. Steps 0a–0c are **sequential gates** — each
124+
must succeed before moving to the next. If any gate fails, stop and ask the user.
125+
126+
### 0a. Find the product page
127+
128+
Search the web for "adafruit <SENSOR>" to find the product page. The product page
129+
(`https://www.adafruit.com/product/<PRODUCT_ID>`) html data has links to the learn guide (do not
130+
use the API or guess URLs). Use a tool like wget or curl:
131+
`curl -sL https://www.adafruit.com/product/5817 | grep "learn.adafruit.com"`
132+
(Replace `5817` with the actual product ID found via search.)
133+
134+
**If this fails:** try WebSearch for "adafruit <SENSOR> site:adafruit.com". If you still can't
135+
find a product page, ask the user for the product URL or learn guide link.
136+
137+
### 0b. Find and read the learn guide (GATE — cannot proceed without this)
138+
139+
Extract the learn guide URL from the product page. Then fetch the **text view** version:
140+
`https://learn.adafruit.com/<guide-slug>.md?view=all`
141+
142+
If the `.md?view=all` URL 404s, try:
143+
1. The HTML version: `https://learn.adafruit.com/<guide-slug>?view=all`
144+
2. WebSearch for `"learn.adafruit.com" "<SENSOR>"` to find the correct slug
145+
3. Ask the user
146+
147+
From the learn guide, extract:
148+
- The **exact Arduino library name** from the `## Arduino` section
149+
- The **example code link** (usually a GitHub URL in the `## Example Code` section)
150+
- The **I2C address(es)**
151+
- What the sensor **measures** (map to subcomponent types)
152+
153+
**If you cannot find or read the learn guide after trying all fallbacks above, STOP and ask the
154+
user.** Do not guess the library name from the sensor part number.
155+
156+
### 0c. Confirm the library exists and get its exact registered name
157+
158+
Verify the library by checking at least one of:
159+
- `gh search repos "<Library Name>" --owner adafruit`
160+
- WebFetch the library's GitHub page or header file
161+
- The library appears in the Arduino Library Manager
162+
163+
**If the library cannot be found**, it may not be released yet, or it may have a different name
164+
than expected. Ask the user before proceeding.
165+
166+
**Then, fetch the library's `library.properties` to get the exact registered name:**
167+
168+
```bash
169+
gh api repos/adafruit/<Repo>/contents/library.properties --jq '.content' | base64 -d
170+
```
171+
172+
Extract the `name=` value — this is the **canonical library name** used by both the Arduino
173+
Library Manager and PlatformIO registry. It often differs from the GitHub repo name (e.g. repo
174+
`Adafruit_AS7331` → library name `Adafruit AS7331 Library`; repo `Adafruit_TMP117` → library
175+
name `Adafruit TMP117`). Guessing the library name from the repo name will produce "package not
176+
found" build failures. Record this name and use it verbatim in Step 4 for both `platformio.ini`
177+
and the firmware's `library.properties`.
178+
179+
### 0d. Gather remaining details
90180

91181
| What | Where to look |
92182
|------|---------------|
93-
| Product page | Search the web for "adafruit <SENSOR>" 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"` (Note: replace `5817` with the actual product ID found via search). |
94-
| Learn guide | **Always read the learn guide before writing any code.** Fetch the text view `.md?view=all` version of the guide found on the product page (e.g., `curl -sL "https://learn.adafruit.com/<guide-slug>.md?view=all"`). The Arduino section (`## Arduino`) names the exact library to use. Do NOT skip this step even if you think you recognise a sub-component. There is also a `## Example Code` section showing the basic example sketch. |
95-
| Adafruit Arduino library | Named in the learn guide. Or: `gh search repos "<SENSOR>" --owner adafruit`. If no dedicated library from adafruit, check related chips (e.g. TMP119 lives inside `Adafruit_TMP117`). Try partial matches like `TMP11` if exact fails. Fall back to 3rd party or ask user. |
96183
| Library API | Read the library header on GitHub — find `begin()` signature and sensor read methods (`getEvent`, `readTempC`, etc.) |
97184
| I2C addresses | Sensor datasheet or Adafruit product page or learn guide or driver. Check https://learn.adafruit.com/i2c-addresses/the-list |
98185
| What it measures | Datasheet — map each reading to a subcomponent type (see table below) |
@@ -173,6 +260,13 @@ gh api repos/adafruit/<Library_Repo>/contents/examples --jq '.[].name'
173260
section usually contains example code link showing the exact API (or embedded code if non-markdown version).
174261
3. **Ask the user:** If tools are restricted, ask them to paste the library header and example.
175262

263+
**If any fetch returns a 404 or error:** Do not silently move to the next fallback and then
264+
forget that all routes failed. If every fallback also fails, you have no verified API to code
265+
against. **Report all failed URLs to the user** and ask them to provide the example code or
266+
confirm the library repo name and structure. Writing driver code without having successfully
267+
read either the example sketch or the learn guide's embedded code is forbidden — you will
268+
produce code that calls methods which may not exist.
269+
176270
From the example, extract the **exact method signatures** used:
177271
- `begin()` — what arguments, what return type
178272
- How readings are triggered (`getEvent()`, `readMeasurement()`, `measureSingleShot()`, etc.)
@@ -194,8 +288,13 @@ gh api repos/adafruit/<Library_Repo>/contents/<Library_Name>.h --jq '.content' |
194288

195289
Or via WebFetch: `https://raw.githubusercontent.com/adafruit/<Library_Repo>/main/<Library_Name>.h`
196290

197-
If GitHub is blocked, the learn guide `.md?view=all` may contain enough API detail from code
198-
snippets. If not, ask the user for the header content.
291+
**If this fetch 404s**, the repo name or header filename may differ from what you assumed. Try:
292+
1. List the repo contents: `gh api repos/adafruit/<Library_Repo>/contents --jq '.[].name'`
293+
2. WebFetch the repo's main page to find the actual header filename
294+
3. Fall back to the learn guide `.md?view=all` which may contain enough API detail from code snippets
295+
296+
If none of these work, **ask the user for the header content.** Do not proceed to write the
297+
driver without having verified the actual method signatures.
199298

200299
**Explicitly set every configuration parameter that the library defaults in `begin()`/`_init()`.**
201300
This pins behavior so library updates can't silently change WipperSnapper.
@@ -307,23 +406,28 @@ Wippersnapper_Components repo.
307406

308407
Skip if the sensor class lives in an already-listed library (e.g. TMP119 in Adafruit_TMP117).
309408

409+
**Use the exact `name=` value from the library's own `library.properties` that you fetched in
410+
Step 0c.** Do not construct the name from the GitHub repo name — they frequently differ
411+
(underscores vs spaces, trailing "Library" suffix, etc.) and a wrong name causes "package not
412+
found" build failures.
413+
310414
### 4a. platformio.ini
311415

312-
Add to `[env]` `lib_deps` in **alphabetical order** ignoring any purpose specific sections of the lib_deps:
416+
Add to `[env]` `lib_deps` in **alphabetical order** ignoring any purpose specific sections of the lib_deps, using the
417+
`adafruit/<exact library.properties name>` format:
313418
```ini
314-
adafruit/Adafruit <Library Name> # released Arduino library
315-
https://github.com/<owner>/<repo>.git # unreleased / third-party
419+
adafruit/Adafruit <Exact Name From library.properties> # released Arduino library
420+
https://github.com/<owner>/<repo>.git # unreleased / third-party
316421
```
317422

318423
### 4b. library.properties
319424

320-
Add the library's **Arduino Library Manager name** to the comma-separated `depends=` line. This
321-
is the name as it appears in the Arduino IDE Library Manager + library.properties, not the GitHub repo name. Add it in
322-
a logical position among the existing entries.
425+
Add the **exact same `name=` value** to the comma-separated `depends=` line. Add it in a logical
426+
position among the existing entries.
323427

324-
Example: to add a library called "Adafruit FooBar":
428+
Example: if the library's `library.properties` contains `name=Adafruit FooBar Library`:
325429
```
326-
depends=..., Adafruit FooBar, ...
430+
depends=..., Adafruit FooBar Library, ...
327431
```
328432
GitHub-only libraries can't go in `library.properties` — note this in the PR description (and advise to fork n release ardu lib).
329433

@@ -538,7 +642,7 @@ protected:
538642

539643
bool _readSensor() {
540644
unsigned long now = millis();
541-
if (_lastRead != 0 && now - _lastRead < 1000)
645+
if (_lastRead != 0 && now - _lastRead < ONE_SECOND_IN_MILLIS)
542646
return true; // recently read, use cached value
543647
if (!_tmp119->getEvent(&_cachedTemp))
544648
return false;

.agents/skills/add-sensor-component-v1/evals/evals.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
"prompt": "Add the BMP581 barometric pressure sensor to WipperSnapper. It measures temperature, pressure, and altitude.",
1919
"expected_output": "Checks if driver already exists (BMP5XX covers BMP580/581/585), identifies the existing driver handles this, and advises that only a components definition may be needed if not already present.",
2020
"files": []
21+
},
22+
{
23+
"id": 4,
24+
"prompt": "Add the AS7331 UV sensor to wippersnapper, publishing UVB as raw and debug-logging UVA/B/C on each read.",
25+
"expected_output": "Finds the Adafruit product page, extracts the learn guide URL, reads the learn guide to confirm the library is 'Adafruit AS7331', fetches and reads the example sketch to learn the API (oneShot, readAllUV, etc.), then writes the driver. If any fetch 404s, reports the failure to the user instead of silently proceeding. Does NOT guess the GitHub URL or write code based on an unverified header file alone.",
26+
"files": []
27+
},
28+
{
29+
"id": 5,
30+
"prompt": "Let's add the STCC4 sensor to WipperSnapper. It has an SHT41 onboard.",
31+
"expected_output": "Does NOT assume the library is Adafruit_SHT4X based on the SHT41 sub-component. Finds the product page, reads the learn guide, and discovers the correct library is 'Adafruit_STCC4' — a dedicated library. The learn guide is the source of truth for the library name, not chip sub-component names.",
32+
"files": []
2133
}
2234
]
2335
}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,5 @@ Doxyfile
6565
.repos/
6666
repos/
6767
Wippersnapper_Components/
68-
Wippersnapper_Boards/
68+
Wippersnapper_Boards/
69+
.claude/settings.local.json

agents.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use skills in `./.agents/skills/`

claude.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See @agents.md

gemini.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See @agents.md

library.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
name=Adafruit WipperSnapper
2-
version=1.0.0-beta.125
2+
version=1.0.0-beta.126
33
author=Adafruit
44
maintainer=Adafruit <adafruitio@adafruit.com>
55
sentence=Arduino application for Adafruit.io WipperSnapper
66
paragraph=Arduino application for Adafruit.io WipperSnapper
77
category=Communication
88
url=https://github.com/adafruit/Adafruit_Wippersnapper_Arduino
99
architectures=*
10-
depends=OmronD6T - Community Fork, SdFat - Adafruit Fork, Adafruit NeoPixel, Adafruit SPA06_003, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit INA260 Library, Adafruit INA237 and INA238 Library, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit MLX90632 Library, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit APDS9999, Adafruit AS5600 Library, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit BMP5xx Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Adafruit SGP41, Sensirion I2C SCD4x, Sensirion I2C SEN5X, Sensirion I2C SEN66, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit QMC5883P Library, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS28, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork, Adafruit BusIO, Adafruit Unified Sensor, Sensirion Core, Adafruit GFX Library, Adafruit LED Backpack Library, Adafruit LiquidCrystal, Adafruit SH110X, Adafruit SSD1306, Adafruit EPD, Adafruit ST7735 and ST7789 Library
10+
depends=OmronD6T - Community Fork, SdFat - Adafruit Fork, Adafruit NeoPixel, Adafruit SPA06_003, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit INA260 Library, Adafruit INA237 and INA238 Library, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit MLX90632 Library, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit APDS9999, Adafruit AS5600 Library, Adafruit AS7331 Library, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit BMP5xx Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Adafruit SGP41, Sensirion I2C SCD4x, Sensirion I2C SEN5X, Sensirion I2C SEN66, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit QMC5883P Library, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS28, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork, Adafruit BusIO, Adafruit Unified Sensor, Sensirion Core, Adafruit GFX Library, Adafruit LED Backpack Library, Adafruit LiquidCrystal, Adafruit SH110X, Adafruit SSD1306, Adafruit EPD, Adafruit ST7735 and ST7789 Library

opencode.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See @agents.md

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ lib_deps =
2525
adafruit/Adafruit AHTX0
2626
adafruit/Adafruit APDS9999
2727
adafruit/Adafruit AS5600 Library
28+
adafruit/Adafruit AS7331 Library
2829
adafruit/Adafruit BME280 Library
2930
adafruit/Adafruit BMP280 Library
3031
adafruit/Adafruit BMP3XX Library

src/Wippersnapper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
#endif
168168

169169
#define WS_VERSION \
170-
"1.0.0-beta.125" ///< WipperSnapper app. version (semver-formatted)
170+
"1.0.0-beta.126" ///< WipperSnapper app. version (semver-formatted)
171171

172172
// Reserved Adafruit IO MQTT topics
173173
#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic

0 commit comments

Comments
 (0)