datalogd.plugins.serial_datasource module

class datalogd.plugins.serial_datasource.SerialDataSource(sinks=[], port='', board_id=None)[source]

Bases: datalogd.DataSource

Receive data from an Arduino connected via a serial port device.

See the datalog_arduino.ino sketch for matching code to run on a USB-connected Arduino.
datalog.ino
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>

////////////////////////////////////////////////////////////
// Device Configuration Settings
////////////////////////////////////////////////////////////

// An ID string for this Arduino
#define BOARD_ID_STRING "A"

// Interval between reads of devices
#define READ_INTERVAL 2000
// Interval between empty "keep alive" messages to maintain connection
#define KEEPALIVE_INTERVAL 1000

// Select which types of sensors to use
#define USE_DIGITAL_PINS true
#define USE_ANALOG_PINS true
#define USE_DS18B20_TEMPERATURE true
#define USE_BH1750_LUX false
#define USE_COUNTER false

////////////////////////////////////////////////////////////
// Pin Definitions and Sensor Configuration
////////////////////////////////////////////////////////////

#if USE_ANALOG_PINS
  // Set of analog input pins to read
  const int analog_pins[] = {A0, A1, A2, A3, A4, A5};
#endif

#if USE_DIGITAL_PINS
  // Set of digital input pins to read
  const int digital_pins[] = {4, 5, 6};
#endif

#if USE_DS18B20_TEMPERATURE
  // Run the 1-wire bus on pin 12
  const int onewire_pin = 12;
#endif

#if USE_COUNTER
  // Flow sensor pulse pin input, must be interrupt enabled
  // These are pins 0, 1, 2, 3, 7 for a Leonardo board
  // Note that Leonardo needs pins 0+1 for Serial1 and 2+3 for I2C
  const int counter_pin = 7;
  // Pin where an LED is connected, will toggle LED in sync with incoming pulses
  // Set to 0 to disable
  const int led_pin = 13;
#endif

////////////////////////////////////////////////////////////


#if USE_DS18B20_TEMPERATURE
  #include <OneWire.h>
  #include <DallasTemperature.h>
  // Initialise the 1-Wire bus
  OneWire oneWire(onewire_pin);
  // Pass our 1-Wire reference to Dallas Temperature
  DallasTemperature thermometers(&oneWire);
#endif

#if USE_BH1750_LUX
  #include <hp_BH1750.h>
  // Reference to the BH1750 light meter module over I2C
  hp_BH1750 luxmeter;
#endif

#if USE_COUNTER
  // Number of pulses read from the flow meter
  volatile unsigned long counter_count = 0;
  // Stored start time and pulse count for flow rate calculation
  unsigned long counter_start_millis = 0;
  unsigned long counter_start_count = 0;
  volatile unsigned int led_state = LOW;
#endif


// Variable to record last data acquisition time
unsigned long measurement_start_millis = 0;
unsigned long keepalive_start_millis = 0;

// Variable to keep track of whether record separators (comma) needs to be prepended to output
bool first_measurement = true;


#if USE_DS18B20_TEMPERATURE
  // Format a DS18B20 device address to a 16-char hex string
  String formatAddress(DeviceAddress address) {
    String hex = "";
    for (uint8_t i = 0; i < 8; i++) {
      if (address[i] < 16) hex += "0";
      hex += String(address[i], HEX);
    }
    return hex;
  }
#endif

// Print out a measurement to the serial port
void printMeasurement(String type, String id, String value, String units="") {
  // A comma separator needs to be prepended to measurements other than the first
  if (first_measurement) {
    first_measurement = false;
  } else {
    Serial.print(",");
  }
  Serial.print("{\"type\":\"");
  Serial.print(type);
  Serial.print("\",\"source\":\"");
  Serial.print(BOARD_ID_STRING);
  Serial.print("\",\"id\":\"");
  Serial.print(BOARD_ID_STRING);
  Serial.print("_");
  Serial.print(id);
  Serial.print("\",\"value\":\"");
  Serial.print(value);
  if (units.length() > 0) {
    Serial.print("\",\"units\":\"");
    Serial.print(units);
  }
  Serial.print("\"}");
}

#if USE_COUNTER
  // Interrupt handler for a pulse from the flow meter
  void counterIncrement() {
    counter_count++;
    if (led_pin != 0) {
      digitalWrite(led_pin, led_state = !led_state);
    }
  }
#endif

void setup(void)
{
  // Open serial port
  Serial.begin(115200);

  #if USE_DS18B20_TEMPERATURE
    // Initialise I2C bus
    Wire.begin();
    pinMode(onewire_pin, INPUT_PULLUP);
  #endif

  #if USE_DIGITAL_PINS
    // Configure set of digital input pins
    for (uint8_t i = 0; i < uint8_t(sizeof(digital_pins)/sizeof(digital_pins[0])); i++) {
      pinMode(digital_pins[i], INPUT);
    }
  #endif

  #if USE_COUNTER
    // Configure the flow meter input pin and interrupt for pulse counting
    pinMode(counter_pin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(counter_pin), counterIncrement, RISING);
    // LED to toggle if defined
    if (led_pin != 0) {
      pinMode(led_pin, OUTPUT);
      digitalWrite(led_pin, led_state);
    }
    counter_start_millis = millis();
  #endif
}


void loop(void)
{
  // Record current time
  unsigned long current_millis = millis();
  // Check if it's time to take some new measurements
  if (current_millis - measurement_start_millis >= READ_INTERVAL) {
    measurement_start_millis = current_millis;
    // The first measurement in this cycle doesn't need a comma delimiter prepended
    first_measurement = true;

    // Print message start
    Serial.print("{\"board\":\"" + String(BOARD_ID_STRING) + "\",");
    Serial.print("\"timestamp\":\"" + String(measurement_start_millis) + "\",");
    Serial.print("\"message\":\"measurement\",\"data\":[");
    
    ///////////////////////////////////////////////////////////////////////////
    // Arduino Digital Pins
    ///////////////////////////////////////////////////////////////////////////
    #if USE_DIGITAL_PINS
      // Read digital pins
      unsigned int d = 0;
      for (uint8_t i = 0; i < uint8_t(sizeof(digital_pins)/sizeof(digital_pins[0])); i++) {
        d += digitalRead(digital_pins[i]) << i;
      }
      printMeasurement("digital", "0", String(d));
    #endif

    ///////////////////////////////////////////////////////////////////////////
    // Arduino Analog Pins
    ///////////////////////////////////////////////////////////////////////////
    #if USE_ANALOG_PINS
      // Read analog pins
      for (uint8_t i = 0; i < uint8_t(sizeof(analog_pins)/sizeof(analog_pins[0])); i++) {
        printMeasurement("analog", String(i), String(analogRead(analog_pins[i])));
      }
    #endif

    ///////////////////////////////////////////////////////////////////////////
    // DS18B20 Temperature Probes
    ///////////////////////////////////////////////////////////////////////////
    #if USE_DS18B20_TEMPERATURE
      // We'll reinitialise the temperature probes each time inside the loop so that
      // devices can be connected/disconnected while running
      thermometers.begin();
      // Temporary variable for storing 1-Wire device addresses
      DeviceAddress address; 
      // Grab a count of temperature probes on the wire
      unsigned int numberOfDevices = thermometers.getDeviceCount();
      // Loop through each device, set requested precision
      for(unsigned int i = 0; i < numberOfDevices; i++) {
        if(thermometers.getAddress(address, i)) {
          thermometers.setResolution(address, 12);
        }
      }
      // Issue a global temperature request to all devices on the bus
      if (numberOfDevices > 0) {
        thermometers.requestTemperatures();
      }
      // Loop through each device, print out temperature data
      for(unsigned int i = 0; i < numberOfDevices; i++) {
        if(thermometers.getAddress(address, i)) {
          printMeasurement("temperature", formatAddress(address), String(thermometers.getTempC(address), 2), "C");
        }
      }
    #endif

    ///////////////////////////////////////////////////////////////////////////
    // BH1750 Lux Meter
    ///////////////////////////////////////////////////////////////////////////
    #if USE_BH1750_LUX
      // Attempt to initialise and read light meter sensor
      if (luxmeter.begin(BH1750_TO_GROUND)) {
        luxmeter.start();
        printMeasurement("lux", "0", String(luxmeter.getLux(), 0), "lux");
      }
    #endif

    ///////////////////////////////////////////////////////////////////////////
    // Fluid Flow Meter
    ///////////////////////////////////////////////////////////////////////////
    #if USE_COUNTER
      unsigned long counter_end_count = counter_count;
      unsigned long counter_end_millis = millis();
      // Total volume in sensor pulses
      printMeasurement("counter_total", "0", String(counter_end_count), "counts");
      // Current flow rate in pulses per minute
      float counter_rate = 1000.0*(counter_end_count - counter_start_count)/(counter_end_millis - counter_start_millis);
      printMeasurement("counter_rate", "0", String(counter_rate, 4), "Hz");
      counter_start_count = counter_end_count;
      counter_start_millis = counter_end_millis;
    #endif

    // Print message end
    Serial.println("]}");
  } else if (current_millis - keepalive_start_millis >= KEEPALIVE_INTERVAL) {
    // Send keepalive packet to maintain serial communications
    keepalive_start_millis = current_millis;
    // Print empty message
    Serial.print("{\"board\":\"" + String(BOARD_ID_STRING) + "\",");
    Serial.print("\"timestamp\":\"" + String(keepalive_start_millis) + "\",");
    Serial.println("\"message\":\"measurement\",\"data\":[]}");
  }
}
Parameters:
  • port – Path of serial device to use. A partial name to match can also be provided, such as “usb”.
  • board_id – ID label provided by the Arduino data logging board, to select a particular device in case multiple boards are connected.
class SerialHandler(parent)[source]

Bases: sphinx.ext.autodoc.importer._MockObject

A class used as a asyncio Protocol to handle lines of text received from the serial device.

Parameters:parent – The parent SerialDataSource class.
handle_line(line)[source]

Accept one line of text, parse it to extract data, and pass the data on to any connected sinks.

Parameters:line – Line of text to process.