README.md
Source code
# Apis_Library
[](https://doi.org/10.5281/zenodo.4572371)
Arduino library for the [Apis](https://github.com/NorthernWidget/Project-Apis) LiDAR rangefinder board. Apis manages power supply, firmware watchdog, and I2C communication for a LiDAR Lite unit, and reads a MEMS accelerometer to report pitch and roll — useful when the sensor is not mounted level.
**Requires firmware patch 5 or later** (the [NW-Device-Specification](https://github.com/NorthernWidget/NW-Device-Specification) Schema 1 register map, on Project-Apis `master`) on a board provisioned with [NW-Provision](https://github.com/NorthernWidget/NW-Provision). The default I2C address is `0x41`. Boards running the v0.1.x firmware (address `0x50`) are refused by `begin()`; reflash and provision them, or use Apis_Library v0.1.0. Patch 3 runs the accelerometer at 10 Hz, serves its own temperature beside the axes, and takes the Hall-effect zero (started by a momentary magnet, at boot or later) as an average of samples until it settles, with the temperature at the zero stored beside the offsets. Every row therefore ends with `AccelT [C]`, the chip's relative temperature digit, so that a per-unit drift correction can be fitted from logged data later; `getAccelTemperature()` and `getZeroTemperature()` return the two digits. From patch 5 the unit keeps a record of its zeros: the current one and the two before it on Page 1, with a count of zeros stored since manufacture that every reading carries. `getZeroGeneration()` returns that count as read with the last orientation reading, `zeroChanged()` is true when it differs from the one seen at the reading before (or at `begin()`), and `dumpZeros(Serial)` prints the record, newest first, one line per zero as `generation,X,Y,Z,T`. `printStatus(out)` prints one status line for a logger's status file (name, serial, hardware version, firmware patch and build commit from Page 0, this library's `APIS_LIBRARY_VERSION` and `APIS_LIBRARY_COMMIT`, the last report as code and note, Pages 0 to 2 in hex), for the logger to write with its timestamp whenever `reportKind()` is not zero. The two commits come from the NW-Build wrapper and are blank in an IDE build. Pages renumbered 2026-09-23 (spec 4c3b18d): calibration is Page 1 at 0x20, data Page 2 at 0x40.
**Installation:** included in [NorthernWidget-libraries](https://github.com/NorthernWidget/NorthernWidget-libraries). Also available via the Arduino Library Manager. Requires [NW_Core](https://github.com/NorthernWidget/NW_Core), the shared foundation (device protocol, readings and statistics, faults), which the Library Manager installs as a dependency once both are registered; until then, install it beside this library.
```cpp
#include <Apis.h>
Apis rangefinder;
void setup() {
Serial.begin(9600);
if (!rangefinder.begin()) { // false: no ACK, not Schema 1, wrong name, or firmware too old
Serial.print("Apis refused; firmware patch ");
Serial.println(rangefinder.getFirmwareVersion());
while (1);
}
rangefinder.printDataHeader(Serial); // straight to the port: no row is built in RAM
Serial.println();
}
void loop() {
rangefinder.updateMeasurements(); // take the readings
rangefinder.printDataRow(Serial); // print what they left
Serial.println();
delay(1000);
}
See examples/ for a complete demo, a many-readings logger, and Margay logger integration (the Margay example reports a failed begin() or a latched device fault in one word through the logger’s Note column, serial monitor, and LED).
Readings
updateMeasurements() takes a reading, or several with statistics when configured, and printDataRow(out) prints the row those readings left. Both the header and the row go straight into any Arduino Print – an SdFat File, Serial – so no row is ever composed in RAM. For writing many individual readings rather than one summary row, use the reading interface, which prints the same way:
rangefinder.beginReadings(Apis::RANGE, 100); // Apis::ALL, Apis::RANGE, or Apis::ORIENT; 100 readings follow
rangefinder.printHeader(file); // "Range [cm],"
for (int i = 0; i < 100; i++) {
rangefinder.logReading(file); // takes ONE reading and prints it: "312,"
file.println();
}
rangefinder.endReadings();
Serial.println(rangefinder.getRangeMean()); // statistics of those 100, no further acquisition
The count passed to beginReadings() is written to the device (registers 0x44-0x45), which then keeps the LiDAR powered and initialised for exactly that many readings instead of powering it up and down around each one; updateMeasurements() does the same for its N readings. A run that stops short is abandoned by the device after 2 s with a report.
Every acquisition, whether from updateMeasurements(), logReading(), or a single-chip update, appends to that measurement’s array; updateMeasurements() and beginReadings() start a fresh set. The scalar getters return the latest reading and the statistics getters compute from the array on demand, so there is one record of the readings and no second acquisition to summarise them.
printReading(out) prints the stored reading without acquiring; updateMeasurements(component) reads one chip alone (Apis::RANGE or Apis::ORIENT) or both; getRangeCount() and getOrientCount() report how many valid readings are behind the current statistics.
Every reading is requested from the device and waited for through its reading counter, so repeated readings are independent measurements. setRangeReadings(n) and setOrientReadings(n) set how many are taken per updateMeasurements() (clamped to APIS_RANGE_CAPACITY, default 64, and APIS_ORIENT_CAPACITY, default 8; override either before the include). Statistics over them: getRangeMean(), getRangeStd(), getRangeSterr(), getRangeMedian(), and the same for pitch and roll, plus getRangeCount(). With setRangeStats(true) the std and sterr columns join printDataHeader() and printDataRow().
Handshake and faults, for sketches that want them: requestReading(), ready(), newReading(); faulted(chip), anyFault(), reportChip(), reportKind(), printReport(Serial), printNote(out) (one word, e.g. LiDARTimeout, for a logger’s note column), printNote(out, true) (why begin() refused, one word); getHardwareMajor(), getHardwareMinor(), getFirmwareVersion().
The v0.1.x names beginRawReadings(), takeRawReading(buf, offset), endRawReadings() and the NW_READING_* selectors still work and are deprecated.
Testing
extras/test/run.sh compiles the library on a desktop against stub Arduino.h and Wire.h and checks that printDataHeader(), printDataRow() and the reading interface produce byte-identical output to extras/test/baseline.txt for fixed register images. Run it after any change; --record rewrites the baseline when an output change is intended.
Full API reference: https://docs.northernwidget.com/Apis_Library/ ```
Updated on 2026-10-04 at 04:51:39 +0000