datalogd.plugins.serial_datasource module¶
-
class
datalogd.plugins.serial_datasource.
SerialDataSource
(sinks=[], port=None, board_id=None, vid=None, pid=None, serial_number=None, location=None)[source]¶ Bases:
datalogd.DataSource
Receive data from an Arduino connected via a serial port device.
See thedatalog_arduino.ino
sketch for matching code to run on a USB-connected Arduino.#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\":[]}"); } }
Other serial-connected devices should work with this class if they conform to the expected communications protocol. Message data should be encoded in a JSON format. For example
which describes a single temperature measurement data point, encapsulated by a message header. Note that the values encoded in the
"value"
field will be attempted to be decoded using the same logic asparse_dot_json
, so that"20.25"
will be interpreted as the equivalent python float, and special values such asNone
andinf
are supported.If the connection to the serial device cannot be established or is interrupted, regular reattempts will be performed. Note that this means an exception will not be raised if the serial device cannot be found.
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.
-
datalogd.plugins.serial_datasource.
find_device
(vid=None, pid=None, manufacturer=None, product=None, serial_number=None, location=None)[source]¶ Search attached serial ports for a specific device.
The first device found matching the criteria will be returned. Because there is no consistent way to identify serial devices, various parameters are available. The default is to return the first found serial port device. A more specific device can be selected using a unique combination of the parameters.
The USB vendor (
vid
) and product (pid
) IDs are exact matches to the numerical values, for examplevid=0x2e8a
orvid=0x000a
. The remaining parameters are strings specifying a regular expression match to the corresponding field. For exampleserial_number="83"
would match devices with serial numbers starting with 83, whileserial_number=".*83$"
would match devices ending in 83. A value ofNone
means that the parameter should not be considered, however an empty string value (""
) is subtly different, requiring the field to be present, but then matching any value.Be aware that different operating systems may return different data for the various fields, which can complicate matching.
To get a list of serial ports and the relevant data fields see the
list_devices
method.Parameters: - vid – Numerical USB vendor ID to match.
- pid – Numerical USB product ID to match.
- manufacturer – Regular expression to match to a device manufacturer string.
- product – Regular expression to match to a device product string.
- serial_number – Regular expression to match to a device serial number.
- location – Regular expression to match to a device physical location (eg. USB port).
Returns: First
ListPortInfo
device which matches given criteria.
-
datalogd.plugins.serial_datasource.
find_devices
(vid=None, pid=None, manufacturer=None, product=None, serial_number=None, location=None)[source]¶ Search attached serial ports for specific devices.
Similar to
find_device
exce[pt returns a list of all matching devices. A list is returned even in a single device matches. An empty list is returned if no devices match.Parameters: - vid – Numerical USB vendor ID to match.
- pid – Numerical USB product ID to match.
- manufacturer – Regular expression to match to a device manufacturer string.
- product – Regular expression to match to a device product string.
- serial_number – Regular expression to match to a device serial number.
- location – Regular expression to match to a device physical location (eg. USB port).
Returns: List of
ListPortInfo
devices which match given criteria.
-
datalogd.plugins.serial_datasource.
list_devices
()[source]¶ Return a string listing all detected serial devices and any associated identifying properties.
The manufacturer, product, vendor ID (vid), product ID (pid), serial number, and physical device location are provided. These can be used as parameters to
find_device()
or the constructor of aSerialDataSource
class to identify and select a specific serial device.Returns: String listing all serial devices and their details.