The NW-Device-Specification Schema 1 device protocol over I2C.

Classes

  Name
class NW_Device
One NW Schema 1 device on the I2C bus: identity gates, the reading handshake, batches, faults.

Defines

  Name
  NW_REG_SCHEMA
0x01 = Schema 1
  NW_REG_NAME
7-byte device name, null-padded (0x01-0x07)
  NW_REG_HW_MAJOR
  NW_REG_HW_MINOR
  NW_REG_FW_PATCH
written by the firmware
  NW_REG_I2C_ADDR
writable; takes effect at the device’s next boot
  NW_REG_CAL
Page 1: calibration, stored; device-specific (0x20-0x3F)
  NW_REG_STATUS
bit 0 ready; bits 1-6 chip faults; bit 7 pan-fault
  NW_REG_CTRL
writable: bit 0 trigger; bits 1-6 chip select; bit 7 sleep
  NW_REG_COUNTER
reading counter, uint16 little-endian (0x42-0x43)
  NW_REG_REQUEST
readings requested (batch size), uint16 little-endian, writable (0x44-0x45)
  NW_REG_CONFIG
writable, device-specific, volatile
  NW_REG_REPORT
report code; cleared by any Control write
  NW_REG_DATA
first device data byte
  NW_BIT_READY
  NW_BIT_PANFAULT
  NW_CTRL_TRIGGER
  NW_CTRL_SLEEP
  NW_WIRE_CHUNK
the AVR Wire buffer; longer reads are split into transactions of this size
  NW_DATA_RETRIES
readData() re-reads at most this many times when the counter moved during a read

Macros Documentation

define NW_REG_SCHEMA

#define NW_REG_SCHEMA 0x00

0x01 = Schema 1

define NW_REG_NAME

#define NW_REG_NAME 0x01

7-byte device name, null-padded (0x01-0x07)

define NW_REG_HW_MAJOR

#define NW_REG_HW_MAJOR 0x08

define NW_REG_HW_MINOR

#define NW_REG_HW_MINOR 0x09

define NW_REG_FW_PATCH

#define NW_REG_FW_PATCH 0x0A

written by the firmware

define NW_REG_I2C_ADDR

#define NW_REG_I2C_ADDR 0x1F

writable; takes effect at the device’s next boot

define NW_REG_CAL

#define NW_REG_CAL 0x20

Page 1: calibration, stored; device-specific (0x20-0x3F)

define NW_REG_STATUS

#define NW_REG_STATUS 0x40

bit 0 ready; bits 1-6 chip faults; bit 7 pan-fault

define NW_REG_CTRL

#define NW_REG_CTRL 0x41

writable: bit 0 trigger; bits 1-6 chip select; bit 7 sleep

define NW_REG_COUNTER

#define NW_REG_COUNTER 0x42

reading counter, uint16 little-endian (0x42-0x43)

define NW_REG_REQUEST

#define NW_REG_REQUEST 0x44

readings requested (batch size), uint16 little-endian, writable (0x44-0x45)

define NW_REG_CONFIG

#define NW_REG_CONFIG 0x46

writable, device-specific, volatile

define NW_REG_REPORT

#define NW_REG_REPORT 0x47

report code; cleared by any Control write

define NW_REG_DATA

#define NW_REG_DATA 0x48

first device data byte

define NW_BIT_READY

#define NW_BIT_READY 0x01

define NW_BIT_PANFAULT

#define NW_BIT_PANFAULT 0x80

define NW_CTRL_TRIGGER

#define NW_CTRL_TRIGGER 0x01

define NW_CTRL_SLEEP

#define NW_CTRL_SLEEP 0x80

define NW_WIRE_CHUNK

#define NW_WIRE_CHUNK 32

the AVR Wire buffer; longer reads are split into transactions of this size

define NW_DATA_RETRIES

#define NW_DATA_RETRIES 2

readData() re-reads at most this many times when the counter moved during a read

Source code


#ifndef NW_Device_h
#define NW_Device_h

#include <Arduino.h>
#include <Wire.h>
#include "NW_Report.h"

// Page 0 (identity, served from EEPROM) and Page 2 Block 0 (status and control),
// as NW-Device-Specification defines them for every device.
#define NW_REG_SCHEMA    0x00  
#define NW_REG_NAME      0x01  
#define NW_REG_HW_MAJOR  0x08
#define NW_REG_HW_MINOR  0x09
#define NW_REG_FW_PATCH  0x0A  
#define NW_REG_I2C_ADDR  0x1F  
#define NW_REG_CAL       0x20  
#define NW_REG_STATUS    0x40  
#define NW_REG_CTRL      0x41  
#define NW_REG_COUNTER   0x42  
#define NW_REG_REQUEST   0x44  
#define NW_REG_CONFIG    0x46  
#define NW_REG_REPORT    0x47  
#define NW_REG_DATA      0x48  
#define NW_BIT_READY     0x01
#define NW_BIT_PANFAULT  0x80
#define NW_CTRL_TRIGGER  0x01
#define NW_CTRL_SLEEP    0x80
#define NW_WIRE_CHUNK    32    
#define NW_DATA_RETRIES  2     

class NW_Device {
  public:
    bool begin(uint8_t address, const char* name, uint8_t minPatch, unsigned long bootTimeoutMs = 0);
    String beginFailure() const;
    uint8_t address() const          { return _adr; }
    uint8_t hardwareMajor() const    { return _hwMajor; }
    uint8_t hardwareMinor() const    { return _hwMinor; }
    uint8_t firmwareVersion() const  { return _fwPatch; }

    bool setI2CAddress(uint8_t newAddress);
    size_t printSnapshot(Print& out, const char* const* chipNames, uint8_t nChips, bool boot = false,
                         const char* lib = "", const char* libCommit = "");
    void setTimeout(unsigned long ms) { _timeout = ms; }
    unsigned long timeout() const     { return _timeout; }

    // --- Handshake ---
    bool ready();
    uint16_t readCounter();
    bool newReading();
    bool requestReading(uint8_t chips);
    bool waitReading();
    bool captureReading();
    bool takeReading(uint8_t chips);

    // --- Batches (NW-Device-Specification 0x24-0x25) ---
    bool writeBatch(uint16_t n);
    void resetBatch()                 { _absentChips = 0; }
    bool beginBatch(uint16_t n)       { if (n > 1) return writeBatch(n); resetBatch(); return true; }
    template <typename F>
    uint16_t takeReadings(uint8_t chips, uint16_t n, F readOne) {
      beginBatch(n);
      uint16_t taken = 0;
      for (uint16_t i = 0; i < n; i++) {
        if (readOne()) taken++;
        else if (batchFaulted(chips)) break;
      }
      return taken;
    }
    bool batchFaulted(uint8_t chips = 0x3F) const { return (_absentChips & chips) != 0; }

    // --- Reports (the Report register, latched) and faults (status bits, live) ---
    const NW_Report& report() const     { return _report; }
    bool faulted(uint8_t chip) const  { return _report.chipFaulted(chip); }
    bool anyFault() const             { return _report.any(); }
    uint8_t reportChip() const         { return _report.chip(); }
    uint8_t reportKind() const         { return _report.kind(); }

    // --- Registers ---
    bool readBytes(uint8_t reg, uint8_t* buf, uint8_t n);
    bool readData(uint8_t reg, uint8_t* buf, uint8_t n);
    bool dataMoved() const            { return _dataMoved; }
    const NW_Report& bootReport() const { return _bootReport; }
    void clearBootReport()            { _bootReport.code = 0; _bootReport.status = 0; }
    bool writeByte(uint8_t reg, uint8_t value);
    uint8_t readConfig();
    bool writeConfig(uint8_t value)   { return writeByte(NW_REG_CONFIG, value); }
    bool sleep()                      { return writeByte(NW_REG_CTRL, NW_CTRL_SLEEP); }

  private:
    uint8_t _adr = 0;
    uint8_t _hwMajor = 0, _hwMinor = 0, _fwPatch = 0;
    uint8_t _beginFailure = 0;          // 0 none, 1 no ACK, 2 read failed, 3 schema, 4 name, 5 firmware
    unsigned long _timeout = 500;
    uint16_t _lastCounter = 0xFFFF;     // counter of the last captured reading
    uint16_t _counterBefore = 0xFFFF;   // counter seen at the last request
    uint8_t  _chips = 0;                // chips selected at the last request
    uint8_t _absentChips = 0;           // chips that reported absent since the last writeBatch()/resetBatch()
    bool _dataMoved = false;            // readData() exhausted its retries
    NW_Report _report;
    NW_Report _bootReport;              // what begin() captured, until the logger clears it
};

#endif

Updated on 2026-09-29 at 05:58:59 +0000