Classes

  Name
class Libelle
Library for the Libelle multi-spectral shortwave pyranometer.

Types

  Name
enum Orientation { UP = 0, DOWN = 1}

Attributes

  Name
constexpr float LIBELLE_ERROR

Defines

  Name
  LIBELLE_FW_MIN_PATCH
  LIBELLE_LIBRARY_VERSION
  LIBELLE_LIBRARY_COMMIT
  LIBELLE_UV_CAPACITY
  LIBELLE_LIGHT_CAPACITY
  LIBELLE_IR_CAPACITY
  LIBELLE_TILT_CAPACITY

Types Documentation

enum Orientation

Enumerator Value Description
UP 0  
DOWN 1  

Attributes Documentation

variable LIBELLE_ERROR

constexpr float LIBELLE_ERROR = NW_ERROR;

Sentinel returned by every getter when it has no reading: the accelerometer is unresponsive (all three axes return identical values), a chip faulted, or the device never answered. The same value as NW_ERROR.

Macros Documentation

define LIBELLE_FW_MIN_PATCH

#define LIBELLE_FW_MIN_PATCH 1

Lowest firmware patch (Page 0 byte 0x0A) this library accepts: patch 1 brought Schema 1 (Page 0, Block 0 handshake, data at 0x48-0x5D).

define LIBELLE_LIBRARY_VERSION

#define LIBELLE_LIBRARY_VERSION "1.0.0"

define LIBELLE_LIBRARY_COMMIT

#define LIBELLE_LIBRARY_COMMIT ""

define LIBELLE_UV_CAPACITY

#define LIBELLE_UV_CAPACITY 8

define LIBELLE_LIGHT_CAPACITY

#define LIBELLE_LIGHT_CAPACITY 8

define LIBELLE_IR_CAPACITY

#define LIBELLE_IR_CAPACITY 8

define LIBELLE_TILT_CAPACITY

#define LIBELLE_TILT_CAPACITY 8

Source code

/******************************************************************************
Libelle.h
Library for interfacing with the Libelle shortwave pyranometer
Bobby Schulz @ Northern Widget LLC
7/5/2018
https://github.com/NorthernWidget-Skunkworks/Libelle_Library

Measures solar radiation across six spectral bands from UV-B through
short-wave infrared (~280-1700 nm), with tilt correction via onboard
accelerometer.

"The laws of nature are constructed in such a way as to make the universe
as interesting as possible."
-Freeman Dyson

Distributed as-is; no warranty is given.
******************************************************************************/

#ifndef Libelle_h
#define Libelle_h

#include <Arduino.h>
#include "Wire.h"
#include "math.h"
#include <NW_Core.h>   // NW_Core: NW_Device (Schema 1 protocol), NW_Readings, NW_Report

#define LIBELLE_FW_MIN_PATCH 1

// Build identity: this library's version (held equal to library.properties by
// NW-Tests/version_check.py) and its build commit, set by the NW-Build wrapper from
// git and blank in an Arduino IDE build. Both go into a logger's status file.
#define LIBELLE_LIBRARY_VERSION "1.0.0"
#ifndef LIBELLE_LIBRARY_COMMIT
#define LIBELLE_LIBRARY_COMMIT ""
#endif

// Readings per updateMeasurements() are kept in static arrays of this
// capacity (one per chip group; no heap); set<Group>Readings(n) clamps to it.
// Override before the include to trade RAM for a longer batch. The default is
// 8, not the 16 of the other libraries: Libelle stores ten fields and a
// logger usually carries two units (UP and DOWN).
#ifndef LIBELLE_UV_CAPACITY
  #define LIBELLE_UV_CAPACITY 8      // VEML6075: UVA, UVB
#endif
#ifndef LIBELLE_LIGHT_CAPACITY
  #define LIBELLE_LIGHT_CAPACITY 8   // VEML6030: ALS, white, lux
#endif
#ifndef LIBELLE_IR_CAPACITY
  #define LIBELLE_IR_CAPACITY 8      // ADS1115: IR short, IR mid, thermistor temperature
#endif
#ifndef LIBELLE_TILT_CAPACITY
  #define LIBELLE_TILT_CAPACITY 8    // ADXL343: roll, pitch
#endif

enum Orientation { UP = 0, DOWN = 1 };

constexpr float LIBELLE_ERROR = NW_ERROR;

class Libelle : public NW_Sensor
{
    public:
        static constexpr uint8_t DEFAULT_ADDRESS_UP = 0x4C;
        static constexpr uint8_t DEFAULT_ADDRESS_DOWN = 0x0C;
        enum Component : uint8_t {
            VEML6075 = 0x01,  
            VEML6030 = 0x02,  
            ADS1115  = 0x04,  
            ADXL343  = 0x08,  
            BRIDGE   = 0x07,  
            ALL      = 0x0F
        };
        Libelle(Orientation orientation = UP);
        bool begin(uint8_t ADR_ = 0);
        float getRoll(bool update = false);
        float getPitch(bool update = false);
        long getUVA(bool update = false);
        long getUVB(bool update = false);
        long getALS(bool update = false);
        long getWhite(bool update = false);
        float getLux(bool update = false);
        float getIR_Short(bool update = false);
        float getIR_Mid(bool update = false);
        float getTemp(bool update = false);
        String getHeader();
        String getString();

        bool updateMeasurements(uint8_t component = ALL);
        bool updateUV();
        bool updateLight();
        bool updateIR();
        bool updateTilt();
        uint16_t setUVReadings(uint16_t n);
        uint16_t setLightReadings(uint16_t n);
        uint16_t setIRReadings(uint16_t n);
        uint16_t setTiltReadings(uint16_t n);
        void setUVStats(bool enable);
        void setLightStats(bool enable);
        void setIRStats(bool enable);
        void setTiltStats(bool enable);
        uint16_t getUVCount();
        uint16_t getLightCount();
        uint16_t getIRCount();
        uint16_t getTiltCount();

        // --- Statistics getters ---
        // Computed two-pass in 32-bit float over the readings stored by the last
        // updateMeasurements() (NW_Readings); LIBELLE_ERROR when none. Lux,
        // temperature, roll and pitch are stored per reading in their physical
        // units, since none is linear in the register values.
        float getUVAMean();      float getUVAStd();      float getUVASterr();      float getUVAMedian();
        float getUVBMean();      float getUVBStd();      float getUVBSterr();      float getUVBMedian();
        float getALSMean();      float getALSStd();      float getALSSterr();      float getALSMedian();
        float getWhiteMean();    float getWhiteStd();    float getWhiteSterr();    float getWhiteMedian();
        float getLuxMean();      float getLuxStd();      float getLuxSterr();      float getLuxMedian();
        float getIR_ShortMean(); float getIR_ShortStd(); float getIR_ShortSterr(); float getIR_ShortMedian();
        float getIR_MidMean();   float getIR_MidStd();   float getIR_MidSterr();   float getIR_MidMedian();
        float getTempMean();     float getTempStd();     float getTempSterr();     float getTempMedian();
        float getRollMean();     float getRollStd();     float getRollSterr();     float getRollMedian();
        float getPitchMean();    float getPitchStd();    float getPitchSterr();    float getPitchMedian();

        // --- Reading interface (NW standard) ---
        size_t printHeader(Print& out);
        size_t printReading(Print& out);
        size_t logReading(Print& out);
        void beginReadings(uint8_t component = ALL, uint16_t n = 0);
        void endReadings();

        // --- Faults (status byte, live; Report register, latched) ---
        bool faulted(uint8_t chip);
        bool anyFault();
        uint8_t reportChip();
        uint8_t reportKind();
        size_t printReport(Print& out);
        String reportNote();
        size_t printStatus(Print& out, bool boot = false) override;
        // --- NW_Sensor: the logger's view (Margay::watch) ---
        const char* name() const override { return "Libelle"; }
        bool reportIsFault() override;
        uint8_t bootReportKind() override;
        void clearBootReport() override;
        String beginFailure();
        uint8_t getHardwareMajor();
        uint8_t getHardwareMinor();
        uint8_t getFirmwareVersion();

    private:
        const float LuxRes = 0.0036;

        const float A = 0.003354016;
        const float B = 0.0003074038;
        const float C = 1.019153E-05;
        const float D = 9.093712E-07;

        NW_Device _dev;
        uint8_t Accel_ADR = 0x1D;
        Orientation _orientation = UP;
        bool _bridgeOk = false;   // the bridge passed begin()'s gates
        bool _accelOk = false;    // the accelerometer acknowledged at begin()
        bool _accelFault = false; // the last accelerometer reading failed (not answering or all axes equal)
        // Means of the last updateMeasurements() (or the last logReading()); LIBELLE_ERROR when none.
        float _roll = LIBELLE_ERROR, _pitch = LIBELLE_ERROR;       // [deg]
        long _uva = (long)LIBELLE_ERROR, _uvb = (long)LIBELLE_ERROR; // compensated counts
        long _als = (long)LIBELLE_ERROR, _white = (long)LIBELLE_ERROR; // raw counts
        float _lux = LIBELLE_ERROR;                                  // [lx]
        float _irShort = LIBELLE_ERROR, _irMid = LIBELLE_ERROR;      // [V]
        float _temp = LIBELLE_ERROR;                                 // [C]
        // Readings as the bridge serves them where the scaling is linear (raw
        // counts), else in physical units, one array per field.
        NW_Readings<int32_t, LIBELLE_UV_CAPACITY> _uvaReadings;
        NW_Readings<int32_t, LIBELLE_UV_CAPACITY> _uvbReadings;
        NW_Readings<uint16_t, LIBELLE_LIGHT_CAPACITY> _alsReadings;
        NW_Readings<uint16_t, LIBELLE_LIGHT_CAPACITY> _whiteReadings;
        NW_Readings<float, LIBELLE_LIGHT_CAPACITY> _luxReadings;     // [lx]
        NW_Readings<uint16_t, LIBELLE_IR_CAPACITY> _irShortReadings;
        NW_Readings<uint16_t, LIBELLE_IR_CAPACITY> _irMidReadings;
        NW_Readings<float, LIBELLE_IR_CAPACITY> _tempReadings;       // [C]
        NW_Readings<float, LIBELLE_TILT_CAPACITY> _rollReadings;     // [deg]
        NW_Readings<float, LIBELLE_TILT_CAPACITY> _pitchReadings;    // [deg]
        NW_ReadingsConfig _uvCfg;    // Readings per updateMeasurements() and stats columns, VEML6075 group
        NW_ReadingsConfig _lightCfg; // VEML6030 group
        NW_ReadingsConfig _irCfg;    // ADS1115 group
        NW_ReadingsConfig _tiltCfg;  // ADXL343 group
        uint8_t _component = ALL;    // Selection of the current beginReadings() run
        bool initAccel();
        float getG(uint8_t Axis);
        bool readUV(uint8_t* d);     // Append one served VEML6075 reading (8 bytes from 0x50) unless faulted
        bool readLight(uint8_t* d);  // Append one served VEML6030 reading (6 bytes from 0x48) unless faulted
        bool readIR(uint8_t* d);     // Append one served ADS1115 reading (6 bytes from 0x58) unless faulted
        bool readData();             // One 22-byte read of the three bridge chips, appended
        void resetReadings(uint8_t component);
        void summarise(uint8_t component); // Means into the single-value fields, LIBELLE_ERROR when no reading
        NW_Report report();           // The bridge's report, or the library's own for the accelerometer
        String column(const char* name, const char* unit, bool stats); // "R_u [deg]," plus std and sterr columns
        float TempConvert(float V, float Vcc, float R, float A, float B, float C, float D, float R25);
        void PrintAllRegs();
        bool WriteByte(uint8_t Adr, uint8_t Pos, uint8_t Val);
};

#endif

Updated on 2026-09-23 at 22:43:23 +0000