Skip to content

Commit e80b041

Browse files
committed
Strip example down, refine instructions
1 parent c23a8b0 commit e80b041

File tree

1 file changed

+13
-117
lines changed
  • .agents/skills/add-sensor-component-v1

1 file changed

+13
-117
lines changed

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

Lines changed: 13 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ Before writing any code, gather this information:
8585

8686
| What | Where to look |
8787
|------|---------------|
88-
| 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). This is the fastest route to all other info. |
89-
| Learn guide | **Always read the learn guide before writing any code.** Fetch the text view `.md?view=all` version (e.g. `https://learn.adafruit.com/<guide-slug>/overview` goes to `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 (e.g. a board with an SHT41 onboard may have its own dedicated library like `Adafruit_STCC4` — you won't know without reading the guide). There is also a subsequent section for `## Example Code` beneath the arduino section showing a link to the basic example sketch/code. |
88+
| 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). |
89+
| 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. |
9090
| 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. |
9191
| Library API | Read the library header on GitHub — find `begin()` signature and sensor read methods (`getEvent`, `readTempC`, etc.) |
9292
| I2C addresses | Sensor datasheet or Adafruit product page or learn guide or driver. Check https://learn.adafruit.com/i2c-addresses/the-list |
@@ -204,104 +204,21 @@ bool begin() {
204204
}
205205
```
206206

207-
### Then: Write the driver using this template
207+
### Then: Write the driver
208208

209-
This is a header-only class. Use the closest existing driver as a template, but **do not blindly
210-
copy** — older drivers may lack the caching and explicit-defaults patterns described above.
211-
Always apply the patterns from this skill even if the closest driver doesn't use them.
209+
This is a header-only class. Use the closest existing driver as a template. Always apply the patterns from this skill!
212210

213-
> **Note:** Some existing drivers (e.g. TMP117, MCP9808) use simpler patterns that predate
214-
> current best practices. Follow this template, not those older drivers.
215-
216-
```cpp
217-
/*!
218-
* @file WipperSnapper_I2C_Driver_<SENSOR>.h
219-
*
220-
* Device driver for the <SENSOR> <description> sensor.
221-
*
222-
* Adafruit invests time and resources providing this open source code,
223-
* please support Adafruit and open-source hardware by purchasing
224-
* products from Adafruit!
225-
*
226-
* Copyright (c) <AUTHOR> <YEAR> for Adafruit Industries.
227-
*
228-
* MIT license, all text here must be included in any redistribution.
229-
*
230-
*/
231-
#ifndef WipperSnapper_I2C_Driver_<SENSOR>_H
232-
#define WipperSnapper_I2C_Driver_<SENSOR>_H
233-
234-
#include "WipperSnapper_I2C_Driver.h"
235-
#include <<Adafruit_Library_Header>.h>
236-
237-
/**************************************************************************/
238-
/*!
239-
@brief Class that provides a driver interface for a <SENSOR> sensor.
240-
*/
241-
/**************************************************************************/
242-
class WipperSnapper_I2C_Driver_<SENSOR> : public WipperSnapper_I2C_Driver {
243-
public:
244-
WipperSnapper_I2C_Driver_<SENSOR>(TwoWire *i2c, uint16_t sensorAddress)
245-
: WipperSnapper_I2C_Driver(i2c, sensorAddress) {
246-
_i2c = i2c;
247-
_sensorAddress = sensorAddress;
248-
}
249-
250-
~WipperSnapper_I2C_Driver_<SENSOR>() { delete _<sensor_ptr>; }
251-
252-
bool begin() {
253-
_<sensor_ptr> = new <Adafruit_Class>();
254-
if (!_<sensor_ptr>->begin((uint8_t)_sensorAddress, _i2c))
255-
return false;
256-
// Pin library defaults explicitly (found by reading library _init/begin):
257-
// _<sensor_ptr>->setMeasurementMode(...);
258-
// _<sensor_ptr>->setAveragedSampleCount(...);
259-
return true;
260-
}
261-
262-
// --- One getEvent*() per sensor reading type ---
263-
// All go through _readSensor() for caching
264-
265-
bool getEventAmbientTemp(sensors_event_t *tempEvent) {
266-
if (!_readSensor())
267-
return false;
268-
*tempEvent = _cachedTemp;
269-
return true;
270-
}
271-
272-
protected:
273-
<Adafruit_Class> *_<sensor_ptr>; ///< Pointer to <SENSOR> sensor object
274-
275-
// Cached readings and time guard
276-
sensors_event_t _cachedTemp = {0};
277-
unsigned long _lastRead = 0;
278-
279-
/*******************************************************************************/
280-
/*!
281-
@brief Reads sensor data, with 1-second cache to avoid redundant
282-
I2C reads when multiple getEvent*() calls occur per cycle
283-
(e.g. °C then °F, or temp then humidity). The calling order
284-
of getEvent methods is not guaranteed, so every method must
285-
go through this function.
286-
@returns True if cached data is available or a fresh read succeeded.
287-
*/
288-
/*******************************************************************************/
289-
bool _readSensor() {
290-
if (_lastRead != 0 && millis() - _lastRead < 1000)
291-
return true; // use cached values
292-
// Do actual I2C read — adapt to library API:
293-
if (!_<sensor_ptr>->getEvent(&_cachedTemp))
294-
return false;
295-
_lastRead = millis();
296-
return true;
297-
}
298-
};
299-
300-
#endif // WipperSnapper_I2C_Driver_<SENSOR>_H
301-
```
211+
> **Good References:**
212+
> - Look at `WipperSnapper_I2C_Driver_SCD30.h` for a clean example of the new style, including read caching and setting explicit defaults in `begin()`.
213+
> - 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.
302214
303215
### Key decisions when writing the driver:
304216

217+
- **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.
218+
- If the library requires polling a `dataReady()` method, implement a short, limited test in your read loop.
219+
- 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.
220+
- 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.
221+
305222
- **Library API style:** Some Adafruit libraries use Unified Sensor (`getEvent(sensors_event_t*)`)
306223
which fills the event struct directly. Others expose raw read methods like `readTempC()`. If the
307224
library uses raw reads, assign to the appropriate field (e.g. `tempEvent->temperature = readTempC()`)
@@ -327,28 +244,7 @@ protected:
327244
4. Have `getEvent*()` return the cached values instead of doing a fresh I2C read.
328245

329246
See `WipperSnapper_I2C_Driver_SGP30.h` for a complete example — the SGP30 datasheet requires
330-
~1 Hz polling to maintain its IAQ algorithm, so the driver defines:
331-
332-
```cpp
333-
#define SGP30_FASTTICK_INTERVAL_MS 1000
334-
335-
void fastTick() override {
336-
if (!_sgp30) return;
337-
uint32_t now = millis();
338-
if (now - _lastFastMs >= SGP30_FASTTICK_INTERVAL_MS) {
339-
if (_sgp30->IAQmeasure()) {
340-
_eco2 = (uint16_t)_sgp30->eCO2;
341-
_tvoc = (uint16_t)_sgp30->TVOC;
342-
}
343-
_lastFastMs = now;
344-
}
345-
}
346-
347-
bool getEventECO2(sensors_event_t *senseEvent) override {
348-
senseEvent->eCO2 = _eco2; // return cached value
349-
return true;
350-
}
351-
```
247+
~1 Hz polling to maintain its IAQ algorithm.
352248

353249
Most simple sensors (temperature, pressure, humidity) do NOT need this — they can be read
354250
on-demand at whatever interval WipperSnapper requests. Only use `fastTick()` when the

0 commit comments

Comments
 (0)