airsenseur: airquality monitoring open framework technical presentation
TRANSCRIPT
AirSenSeurAirSenSeurAir Air qualityquality monitoring open framework monitoring open framework
Technical presentationTechnical presentationMarco Signorini – Liberaintentio S.r.l.
January 2016 2
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 3
AirSensEUR System OverviewAirSensEUR System Overview
Composed by two different actors: “Shield” and “Host”
The Shield is targeted to interface to physical sensors The Host is in charge of retrieve data from the shield, aggregate
with time and positioning information, store locally and periodically push the overall samples to an external server
Shield and Host are linked together by a serial line Hardware references and software sources released under
public licences (www.airsenseur.org)
January 2016 4
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 5
Shield HardwareShield Hardware
ATMega328 with optiboot installed (Arduino UNO compatible) Four independent Programmable Analog Front Ends (AFE) for
two and three leads chemical sensors Four 16bits high precision Analog to Digital converters Four indepentent 4 channels 12bits high precision Digital to
Analog converters for onboard reference signals synthesis I2C digital temperature, pressure and humidity sensors Opto-coupled USB interface for optional PC connection Low noise linear power supply regulators onboard
January 2016 6
Shield HardwareShield Hardware
AFE
A/D
D/As
AFE
A/D
D/As
AFE
A/D
D/As
AFE
A/D
D/As
Chemical sensors analog front end
and data acquisition blocks
ATMega328uController
T and %RH
Pressure
I2C sensorboard
Local Power Supply and level shifters
Chemical Sensors
Power Supply
+6V DC
TTL serialTo host
Arduino USB2Serialadapter connector
Vin-Vad
Vafe
Vout
January 2016 7
Shield Hardware (single channel)Shield Hardware (single channel)I2C Programmable AFE
(current to voltage conversion)
Analog to Digital conversionthrough SPI
I2C Digital to AnalogGenerates voltage
references
Compatibility withmultiple sensor brands
January 2016 8
Shield Hardware (AFE)Shield Hardware (AFE)
Texas Instruments LMP91000 Configurable Potentiostat Connected via I2C Generates a voltage proportional to the chemical sensor cell
current Fully programmable (bias, internal zero reference, load, gain) Supports internal or external voltage references (Vafe)
January 2016 9
Shield Hardware (ADC)Shield Hardware (ADC)
Texas Instruments ADC161S626 16bit, 50 to 250kSPS Connected via SPI External voltage reference (Vad) range of +0.5V to 5V True differential input: allows to inject external zero reference
(Vn-)
January 2016 10
Shield Hardware (DAC)Shield Hardware (DAC)
Analog Devices AD5694R quad channels 12bit DAC Connected via I2C Very low drift (2ppm/C) internal voltage reference Selectable gain for 0 to 2.5V or 0 to 5V full scale output Used to generate references for
Analog Front End (Vafe) Analog to Digital negative reference (Vn-) Analog to Digital full scale reference (Va)
January 2016 11
Shield Hardware (THP and supply)Shield Hardware (THP and supply)
Factory calibratedI2C Humidity and
Temperature sensor
Factory calibratedI2C Pressure sensor
Linear 5V regulator
January 2016 12
Shield Hardware (THP and supply)Shield Hardware (THP and supply)
TecnoSens UR100CD Temperature and Humidity sensor Calibration factory parameters stored into embedded EEPROM Connected via I2C
Bosh BMP180 Pressure sensor Fully calibrated MEMS sensor Connected via I2C
Linear Voltage regulator Provides 5V from 6-10V external power supply
January 2016 13
Shield Hardware (ATMega328)Shield Hardware (ATMega328)
ATMega 328Running at 16MHz
Arduino(tm) USB2Serialconnector onboard
Programmable LEDonboard
January 2016 14
Shield Hardware (ATMega328)Shield Hardware (ATMega328)
Connected to ADC via SPI and four dedicated chip select lines Connected to AFE via I2C Connected to DAC via I2C and four dedicated gain selection
lines Connected to THP sensors via I2C Serial line shared with external programmer and host Programmable LED on digital line (shared) Optional external serial RAM via SPI and shared chip select line
January 2016 15
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 16
Shield FirmwareShield Firmware
Arduino IDE 1.6.5 compatible Implemented mainly in C++ .INO file wrapper for C++ root object instance
Initializes peripherals and sensors Handles serial communication protocol Samples sensors Performs filtering and average calculations Generates timestamps for outgoing samples
January 2016 17
Shield Firmware (Data Flow)Shield Firmware (Data Flow)
IIR IIR DecimationFilter Averager
Sampler
Period(Fs)
Coefficient
CoefficientRatio Length
BufferTimer
Timestamp Last
Sam
ple
Sen
sor
Fs
Fs Fs
Fs/R
atio
Fs/(R
atio
*Len
gth)
January 2016 18
Shield Firmware (.INO wrapper)Shield Firmware (.INO wrapper)
Is the main entry point for the Arduino framework Two functions: init and loop References two external C functions where all the magic is
performed by C++ code. “_impl” functions are implemented in ChemSensorBoardImpl.cpp
extern void setup_impl();extern void loop_impl();void setup() { setup_impl();}void loop() { loop_impl();}
January 2016 19
Shield Firmware (setup_impl)Shield Firmware (setup_impl)
Initializes the hardware and the software data structures Initializes the timer and registers a callback
void setup_impl() { // Initialize the heartbeat pin pinMode(HBLEDPIN, OUTPUT); digitalWrite(HBLEDPIN, LOW); hbTimer = 0; // Initialize the serial line Serial.begin(9600); // Instantiate the main objects sensorBoard = new SensorsArray(); commProtocol = new CommProtocol(sensorBoard); // Initialize the timer at 10ms Timer1.initialize(10000); Timer1.attachInterrupt(timerInterrupt);}
January 2016 20
Shield Firmware (loop_impl)Shield Firmware (loop_impl)
Calls the main sensorBoard loop, handles heartbeat and the serial line
void loop_impl() { // Propagate to the sensor array bool newSample = sensorBoard->loop(); // Heartbeat led if (newSample && (hbTimer == 0)) { hbTimer = HBLED_NEWSAMPLE_TMR; } // Handle the serial line if (Serial.available()) { unsigned char val = Serial.read(); commProtocol->onDataReceived(val); }}
January 2016 21
Shield Firmware (timer callback)Shield Firmware (timer callback)
Propagate timing callback to the main objects, handles the heartbeat LED
void timerInterrupt() { if (sensorBoard) { sensorBoard->timerTick(); } if (commProtocol) { commProtocol->timerTick(); } // Heartbeat led if (hbTimer != 0) { hbTimer--; digitalWrite(HBLEDPIN, HIGH); } else { digitalWrite(HBLEDPIN, LOW); }}
January 2016 22
Shield Firmware (global objects)Shield Firmware (global objects)
Two global objects are instantiated in the setup_impl function:
SensorsArray* sensorBoard;CommProtocol* commProtocol;
SensorsArray is the main entry point for all sensors management and samples retrieval. It's implemented in SensorsArray.cpp file
CommProtocol is responsible for handling serial data communications with the host. It's implemented in CommProtocol.cpp file. It receives a SensorArray reference so it's able to interact with it.
January 2016 23
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
OnDataReceived handles the communications with the Host timerTick should be called periodically to properly handle
protocol timeouts Valid commands are wrapped by a specific private callback
class CommProtocol {public: CommProtocol(SensorsArray *sensors); void timerTick(); void onDataReceived(unsigned char pivotChar);private: void reset(); void processBuffer(); static bool sampleEnable(...); static bool sampleDisable(...);
January 2016 24
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
typedef struct _commandinfo { unsigned char commandID; unsigned char parNum; bool (*handler)(CommProtocol* context, unsigned char cmdOffset);} commandinfo; private: static const commandinfo validCommands[];
Each valid command is associated to a callback function and the number of expected parameters
A list of valid commands is statically defined and stored in the FLASH memory
January 2016 25
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
typedef enum _rxstatus {RX_IDLE,RX_HEADER_FOUND
} rxstatus;private:
unsigned char buffer[COMMPROTOCOL_BUFFER_LENGTH]; unsigned char offset; rxstatus rxStatus;
The internal state machine is based on two possible statues: “waiting for a valid header” and “header found” (i.e. Idle or running)
Incoming data are stored on a RAM buffer at the “offset” position. Offset is changed on each incoming data
January 2016 26
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
void CommProtocol::onDataReceived(unsigned char pivotChar) {
switch (rxStatus) {case RX_IDLE: {
// Searching for an header...
}break;case RX_HEADER_FOUND: {
// Searching for a trailerif(pivotChar == COMMPROTOCOL_TRAILER) {ProcessBuffer();...
}break;
}}
onDataReceived is responsible for data framing and processing
January 2016 27
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
void CommProtocol::processBuffer() {
// .. we expect the command ID, validate it...
// Execute the actiontypedef bool (*fpointer)(CommProtocol* context, unsigned
char cmdOffset);fpointer handler = &(validCommands[offsetId].handler);if (valid && handler != 0) {
valid = (*handler)(this, offsetId);}// Signal an invalid/fault conditionif (!valid) {
strcpy_P((char*)buffer, commProtocolErrorString);}
// Send back the resultSerial.print((char*)buffer);
ProcessBuffer calls the proper callback and send back results
January 2016 28
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
bool CommProtocol::setSampleDecimation(CommProtocol* context, unsigned char cmdOffset) { unsigned char channel = context->getParameter(0); unsigned char decimation = context->getParameter(1); if (context->sensorsArray->setSampleDecimation(channel, decimation)) { return context->renderOKAnswer(cmdOffset, channel); } return false;}
Command handler example: no dynamic answer to the host
January 2016 29
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
bool CommProtocol::getSampleDecimation(CommProtocol* context, unsigned char cmdOffset) { unsigned char channel = context->getParameter(0); unsigned char decimation; if (!context->sensorsArray->getSampleDecimation(channel, &decimation)) { return false; } context->buffer[0] = COMMPROTOCOL_HEADER; context->buffer[1] = validCommands[cmdOffset].commandID; context->buffer[2] = 0; context->writeValue(channel, false); context->writeValue(decimation, true);
return true;}
Command handler example: dynamic result sent to the host
January 2016 30
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
Is the “container” of all sensor implementation objects and related data averagers and FIRs
Provides a type-unaware interface for all sensor channels Orchestrates the sampling process for all sensors through a pair
of functions: loop and timerTick.
January 2016 31
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
class SensorsArray {...
private:static const LMP91000 AFEList[NUM_OF_CHEM_SENSORS];static const ADC16S626 ADCList[NUM_OF_CHEM_SENSORS];
static const AD5694R DACList[NUM_OF_CHEM_SENSORS]; static UR100CD ur100cd;static SFE_BMP180 bmp180;static DitherTool ditherTool; Sampler* samplers[NUM_OF_TOTAL_SENSORS];SamplesAverager* averagers[NUM_OF_TOTAL_SENSORS]; ...
};
Sensors implementations are statically defined and allocated in FLASH area, when possible
January 2016 32
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
class SensorsArray {...
public: unsigned char setSamplePrescaler(...); bool getSamplePrescaler(...); unsigned char setSamplePostscaler(...); bool getSamplePostscaler(...); bool getLastSample(...); bool writeDACRegisters(...); bool readDACRegisters(...);
...};
Provides type-unaware interface for all sensor channels
January 2016 33
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
class SensorsArray {public: SensorsArray(); virtual ~SensorsArray();
Public:
bool timerTick(); bool loop();
...};
Implements the entry point functions to be called periodically by the main loop and timer
January 2016 34
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
The constructor is responsible for: Initialize the internal coefficients for the pressure sensor Initialize the samplers and averagers arrays Allocate and initialize the sampler units Allocate the data averagers Link averagers with the dithering tool Initialize DACs Load preset stored on the EEPROM
January 2016 35
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
The timerTick function propagates the event to all samplers Samplers with unread samples shall return true; false otherwise
bool SensorsArray::timerTick() { bool result = false; // Loop on each sensor samplers for (unsigned char n = 0; n < NUM_OF_TOTAL_SENSORS; n++) { if (samplers[n] != 0) { result |= samplers[n]->sampleTick(); } } // Increase the internal timestamp timestamp++; return result;}
January 2016 36
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
The timerTick function propagates the event to all samplers Samplers with unread samples shall return true; false otherwise
bool SensorsArray::loop() {...
bool result = false; for (unsigned char n = 0; n < NUM_OF_TOTAL_SENSORS; n++) { if (samplers[n] != 0) { if (samplers[n]->sampleLoop()) { // A new sample is ready to be averaged result |= averagers[n]->collectSample(samplers[n]->getLastSample(), timestamp); }; } } return result;}
January 2016 37
Shield Firmware (SensorsArray)Shield Firmware (SensorsArray)
The loop function propagates the event to all samplers. When a new sample is available, it propagates to the proper averager.
bool SensorsArray::loop() { // If sampling is disabled, skip this function if (!samplingEnabled) return false; // Otherwise loop on each sensor sampler and averager bool result = false; for (unsigned char n = 0; n < NUM_OF_TOTAL_SENSORS; n++) { if (samplers[n] != 0) { if (samplers[n]->sampleLoop()) { // A new sample is ready to be averaged result|=averagers[n]->collectSample(samplers[n]->get LastSample(), timestamp); }; } } return result;}
January 2016 38
Shield Firmware (Sampler)Shield Firmware (Sampler)
Sampler is an abstract class and is the base class for other specialized sampler units
Implements common sampler funcionalities: prescaler, IIR and decimation filter.
It contains a reference to a singleton DitherTool used by IIR to reduce the data DC offset introduced by rouding math
Two functions needs to be implemented by specialized classes:
sampleTick() sampleLoop()
January 2016 39
Shield Firmware (Samplers hierarchy)Shield Firmware (Samplers hierarchy)
Sampler
ChemSensorSampler
HumSensorSampler
PressSensorSampler
TempSensorSampler
January 2016 40
Shield Firmware (Sampler Subclasses)Shield Firmware (Sampler Subclasses)
Two functions needs to be implemented by specialized classes:
sampleTick() SampleLoop()
Specialized classes dialogate with the hardware through a set of objects providing low level hardware abstraction. Those objects are allocated by the SensorsArray constructor and linked to the specialized object at construction time.
January 2016 41
Shield Firmware (Sampler Subclasses)Shield Firmware (Sampler Subclasses)
The sampleTick function defines the sampling time by comparing an internal counter to a prescaler value.
Time for sampling is signalled by setting the “go” variable to true
bool ChemSensorSampler::sampleTick() { if (timer == prescaler) { // It's time for a new sample timer = 0; go = true; return true; } timer++; return false;}
January 2016 42
Shield Firmware (Sampler Subclasses)Shield Firmware (Sampler Subclasses)
The sampleLoop function reads a new sample from the sensor (sensor.getSample) then calls the onReadSample function.
The sampleLoop is responsible for applying IIR and decimation filters
bool ChemSensorSampler::sampleLoop() { // Take the new sample if (go) { onReadSample(sensor.getSample()); go = false; // Filter with two cascade single pole IIRs applyIIRFilter(IIR1); applyIIRFilter(IIR2); // Apply the decimation filter return applyDecimationFilter(); } return false;}
January 2016 43
Shield Firmware (Sampler)Shield Firmware (Sampler)
The applyIIRFilter calculates the IIR filter from samples stored on workSample.
The result is stored on workSample. This allows multiple calls of the same function when multiple poles are required
void Sampler::applyIIRFilter(unsigned char iiRID) { unsigned char *denom = iIRDenum + iiRID; // The filter is disabled if denominator == 0 if (*denom == 0) return; double* S = iIRAccumulator + iiRID; // S(n) = S(n-1) + 1/den * (I(n) - S(n-1)) *S = *S + ((double)workSample - *S)/(*denom); workSample = (unsigned short)(ditherTool->applyDithering(*S));}
January 2016 44
Shield Firmware (Sampler)Shield Firmware (Sampler)
The applyDecimationFilter skips samples based on the decimation parameter.
It returns true each “decimation” samples so the calling procedure could evaluate it.
bool Sampler::applyDecimationFilter() { if (decimationTimer == decimation) { decimationTimer = 0; lastSample = workSample; } else { decimationTimer++; } return ((decimationTimer == 0) || (blankTimer != 0));}
January 2016 45
Shield Firmware (SamplesAverager)Shield Firmware (SamplesAverager)
Implements the last filter in the chain: average Historical samples are stored in a dynamically allocated array
so it's possible to change the buffer's deep at runtime Averaged results are released at 1/buffersize rate Fast implementation. The most important funcion is
collectSample Dithering is used to prevent DC insertion due to round floats
and limited C toolchain float support
January 2016 46
Shield Firmware (SamplesAverager)Shield Firmware (SamplesAverager)
The collectSample function uses an accumulator to count the average
The oldest sample is removed by the accumulator and the newest is added
The result is divided by buffer size and dithered
bool SamplesAverager::collectSample(unsigned short sample, unsigned long _timestamp) { ... accumulator = accumulator - dataBuffer[sampleOffset]; accumulator = accumulator + sample; dataBuffer[sampleOffset] = sample; sampleOffset++; lastAverageSample=ditherTool->applyDithering(accumulator/bSize);}
January 2016 47
Shield Firmware (Driver Classes)Shield Firmware (Driver Classes)
Samplers and SensorsArray use specific class drivers to dialogate with hardware
Driver classes hide the low level details for handshaking I/O lines and connecting onboard peripherals
Driver classes increase software modularity and code reuse Driver classes implementation details are strictly based on
target devices datasheet
January 2016 48
Shield Firmware (Driver Classes)Shield Firmware (Driver Classes)
Several driver classes are provided in the firmware. Each class targets a specific device.
LMP91000ADC16S626
AD5694RUR100CD
SFE_BMP180
January 2016 49
Shield Firmware (Driver Classes)Shield Firmware (Driver Classes)
Driver classes are mainly statically allocated by the SensorsArray class and passed to Samplers via constructors
const LMP91000 SensorsArray::AFEList[NUM_OF_CHEM_SENSORS] = ...const ADC16S626 SensorsArray::ADCList[NUM_OF_CHEM_SENSORS] = ...const AD5694R SensorsArray::DACList[NUM_OF_CHEM_SENSORS] = ... UR100CD SensorsArray::ur100cd = UR100CD();SFE_BMP180 SensorsArray::bmp180 = SFE_BMP180();SensorsArray::SensorsArray() { // Initialize the sampler units samplers[CHEMSEN1]= new ChemSensorSampler(ADCList[CHEMSEN1]); samplers[CHEMSEN2]= new ChemSensorSampler(ADCList[CHEMSEN2]); ... samplers[TEMPSEN] = new TempSensorSampler(ur100cd); samplers[PRESSEN1] = new PressSensorSampler(bmp180); ...
January 2016 50
Shield Firmware (Driver Classes)Shield Firmware (Driver Classes)
A simple driver class example is provided by ADC16S626 A/D converter connected to the microcontroller via an SPI and a chip select line
class ADC16S626 {public: ADC16S626(const unsigned char csPin); virtual ~ADC16S626(); unsigned short getSample() const; private: unsigned short toLinear(unsigned short twoComplSample) const; private: const unsigned char m_csPin;};
January 2016 51
Shield Firmware (Driver Classes)Shield Firmware (Driver Classes)
unsigned short ADC16S626::getSample() const { byte inByteHigh, inByteMid, inByteLow; digitalWrite(m_csPin, LOW); inByteHigh = SPI.transfer(0x00); inByteMid = SPI.transfer(0x00); inByteLow = SPI.transfer(0x00); digitalWrite(m_csPin, HIGH); unsigned short result = ((inByteLow >> 6) & 0x03); result |= (((unsigned short)inByteMid) << 2); result |= (((unsigned short)inByteHigh) << 10); result = toLinear(result); return result;}
January 2016 52
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 53
Java Panel ApplicationJava Panel Application
Targeted to run on an PC connected to the AirSensEUR Shield via RS232 to USB opto-coupled interface
Developed for debug purposes but elected by usage as main console for initial AirSensEUR set-up and configuration tool
Java based: runs on any PC and/or MAC with installed JRE (Java Runtime Environment)
NetBeans project files available It's not perfect... a lot of improvements should be done... so think
to it as a working progress project
January 2016 54
Java Panel ApplicationJava Panel Application Depends on jna and purejavacomm
libraries. They provides direct access to the USB/serial line interface from the JVM
Depends on AirSensEURLib project, mainly for .comm and .exceptions packages shared with other AirSensEUR Host applications (see later)
January 2016 55
Java Panel Application (Communcation Layer)Java Panel Application (Communcation Layer)
ChemSensorBoard is an abstract class providing the basic interface for connect/disconnect and read/write to the AirSensEUR Shield
ChemSensorCommHandler implements ChemSensorBoard with the help of SerialPortHelper and CommProtocolHelper classes
ChemSensorBoard
ChemSensorCommHandler
CommProtocolHelper
SerialPortHelper
AirSensEURLib
January 2016 56
AirSensEURLib (SerialPortHelper)AirSensEURLib (SerialPortHelper)
The SerialPortHelper class provides a connection layer between purejavacomm library and AirSensEUR applications
Is used to enumerate available serial interfaces Is used to open/close a known serial interface and to read/write
characters from/to it It implements the SerialPortEventListener.serialEvent defined in
the purejavacomm library. This is called each time the serial line receives data from the AirSensEUR Shield
It defines a SerialReceiverParser.onDataReceived callback interface. This should be implemented by data consumers.
January 2016 57
AirSensEURLib (SerialPortHelper)AirSensEURLib (SerialPortHelper)
public static interface SerialReceiverParser {
void onDataReceived(InputStream inputStream);} private class SerialReceiverListener implements SerialPortEventListener { @Override public void serialEvent(SerialPortEvent event) { if(event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { if (inputStream.available() != 0) { rxDataParser.onDataReceived(inputStream); } } }}
January 2016 58
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
The CommProtocolHelper class implements the protocol engine for connecting to the AirSensEUR Shield via a list of logical DataMessage(s)
Defines the DataMessage class used to encapsulate high level commands sent and received by the overlying application
Translates DataMessage instances to/from data stream to be sent/received via serial line
The CommProtocolHelper encapsulates low level AirSensEUR Shield protocol details providing high level function calls for the Java applications
January 2016 59
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
Three set of methods are provided:
“render” methods “eval” methods “data list” methods
January 2016 60
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
“render” methods are used by the application to “send” commands to the AirSensEUR Shield
void renderSamplerPrescaler(int channelId, int prescaler);void renderSamplerPrescalerRead(int channelId);public void renderSensorInquiry(int channelId);public void renderGetLastSample(int channelId);public void renderStartSample()public void renderStopSample()
January 2016 61
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
“eval” methods are used by the application to “receive and parse” answers coming from the AirSensEUR Shield.
“eval” methods automatically detect contents present on received DataMessage and return “null” for a not matching condition. This allows for multiple safe and distributed evaluations of the same DataMessage.
Integer evalPrescalerInquiry(DataMessage rxMessage, int channel)Integer evalDecimationInquiry(DataMessage rxMessage, int channel) String evalSensorInquiry(DataMessage rxMessage, int channel)List<Integer> evalLastSampleInquiry(DataMessage rxMessage, int channel)
January 2016 62
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
“data list” methods are used to access to internal messaging datastructure in order to push/pop commands/answers
public synchronized void clearCurrentCommandList() public synchronized List<DataMessage> getSafeCurrentCommandList()public synchronized DataMessage getNextRxDataMessage()
January 2016 63
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
The conversion between incoming data from AirSensEUR Shield and DataList items is performed by the function onRxDataReceived
This function should be called by the SerialPortHelper::SerialReceiverParser implementor
// This is the main rx state machine. It aggregates all incoming// data and populates the fromBoard list as a FIFO buffer // containing all the received messagessynchronized public boolean onRxCharReceived(byte value)
January 2016 64
AirSensEURLib (ChemSensorBoard)AirSensEURLib (ChemSensorBoard)
ChemSensorBoard is an abstract class providing the basic interface for connect/disconnect and read/write to the AirSensEUR Shield
public interface ChemSensorBoard { public void connectToBoard(CommPortIdentifier serialPort); public void disConnectFromBoard(); public void writeBufferToBoard(); public List<CommProtocolHelper.DataMessage> getCurrentBuffer();}
January 2016 65
Java Panel Application Java Panel Application (ChemSensorCommHandler)(ChemSensorCommHandler)
ChemSensorCommHandler implements the ChemSensorBoard interface
It contains a SerialPortHelper instance to perform actions through the serial port
It implements the SerialPortHelper.SerialReceiverParser interface by calling the CommProtocolHelper.onRxCharReceived when needed
Allows to the Java application to access to DataMessage Lists and to flush pending commands through the serial line
January 2016 66
Java Panel Application (Dialogs and Panels)Java Panel Application (Dialogs and Panels)
The Java Panel application is implemented using the Swing GUI framework
Swing defines a set of graphical objects targeted to design portable graphic user applications
Graphical objects are grouped in Panels Panels are grouped together to form Dialogs
AirSensEUR Java Panel Application defines a common class for sensor properties Dialogs. This is the SensorSetupDialog
January 2016 67
Java Panel Application (SensorSetupDialog)Java Panel Application (SensorSetupDialog)
Provides a common interface for all setup dialogs The interface contains all methods needed to dialogate with the
AirSensEUR Shield through the CommProtocolHelper
public class SensorSetupDialog extends javax.swing.JDialog { public SensorSetupDialog(java.awt.Frame parent, boolean modal) { super(parent, modal); } public void storeToBoard() { } public void readFromBoard() { } public void evaluateRxMessage(CommProtocolHelper.DataMessage msg) { }}
January 2016 68
Java Panel Application (SensorSetupDialog)Java Panel Application (SensorSetupDialog)
storeToBoard is called by the application asking the setup dialog to dump their current settings to the CommProtocolHelper buffers (in order to be sent to the AirSensEUR Shield)
readFromBuffer is called by the application asking the setup dialog to fill the CommProtocolHelper with query commands for AirSensEUR Shield parameters retrieval
evaluateRxMessage is called by the application each time a new DataMessage has been received back by the AirSensEUR Shield. The dialog may use the coming DataMessage for update their panels status
January 2016 69
Java Panel Application (SensorSetupDialog)Java Panel Application (SensorSetupDialog)
Two setup dialogs are derived from the SensorSetupDialog:
ChemSensorSetupDialog GenericSensorSetupDialog
January 2016 70
Java Panel Application Java Panel Application (ChemSensorSetupDialog)(ChemSensorSetupDialog)
LMP9100Panel
AD5694RPanel
IIRAndAvgPanel
January 2016 71
Java Panel Application Java Panel Application (GenericSensorSetupDialog)(GenericSensorSetupDialog)
IIRAndAvgPanel
Each Dialog propagates to their panels the storeToBoard, readFromBoard, evaluateRxMessage events
Each panel is responsible for dump or updating their proper specific settings through a set of DataMessage
January 2016 72
Java Panel Application (setupPanel example)Java Panel Application (setupPanel example)
The simplest panel is the IIRAndAvgPanel
January 2016 73
Java Panel Application (Setup Panels)Java Panel Application (Setup Panels)
IIRAndAvgPanel
Items in the panel are implemented by a set of Swing widgets Widget options are defined in Data Models Data Models associate a “label” to be shown to the user with a
“value” to be sento to the AirSensEUR Shield registers and viceversa
January 2016 74
Java Panel Application (Data Models)Java Panel Application (Data Models)
Two common typologies are needed:
Slider and Register Data Models
January 2016 75
Java Panel Application (SampleLogger)Java Panel Application (SampleLogger)
SampleLogger is a Swing JPanel derived class providing basic abastraction interface for entities able to manage set of samples
Defines the DataProcessing interface targeted to evaluate incoming samples through a set of mathematical expressions
Implements the DataProcessing interface by providing a standard two complements translation to be applied to AirSensEUR Shield four chemical channels data
Implements the common procedures needed to extract and detect new samples based on associated timestamp
Implements the readFromBoard and evaluateRxMessage common to all derived sample loggers
January 2016 76
Java Panel Application (SampleLogger)Java Panel Application (SampleLogger)
Two SampleLogger derived classes:
LineGraphSampleLoggerPanel TextBasedSampleLoggerPanel
January 2016 77
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 78
Host HardwareHost Hardware
ARM9@400MHz based, 256MB RAM, running Debian Linux from a micro SD card
LiFePO4 Battery and/or USB/Wall adapter operated, with battery parameters available to the CPU through I2C
USB/wall power plugs protected by overvoltage and polarity reversal
Four independent step-up power supply, supervised by the CPU
Onboard USB sockets for WiFi and GPRS dongles Onboard low power GPS module with 1PPS signal feed back to
the CPU and optional high gain external antenna
January 2016 79
Host HardwareHost Hardware
I2C connected front panel with 8 programmable LEDs and 8 programmable pusbuttons
Expansion socket providing access to not yet used onboard peripherals (note: 3v3 operating only) like:
SPI I2C Low res A/D converter lines Low res PWM lines
January 2016 80
Host HardwareHost Hardware
Step up Power Supply
5V@1A
LiFePO4Battery
ARM9 basedmodule
GPSmoduleFront Panel
Step up Power Supply
7V@300mA and level shifters
BatteryManager
+5VDC@3A SerialUSB
Step up Power Supply
5V@1A
Step up Power Supply
5V@1A
Unregulated3v3 line5V line
WiFiDongle
GPRSDongle
AirSensEURShield
I2C
USB
January 2016 81
Host Hardware (Battery Manager)Host Hardware (Battery Manager)LTC4156 I2C programmable
LiFePO4 battery manager
LTC2942 I2CFuel Gauge Engine
Active overvoltage andreverse polarity protection
January 2016 82
Host Hardware (Battery Manager)Host Hardware (Battery Manager)
Built as external module for easy replacement when targeting different battery techologies
Based on Linear Technology LTC4156 dual input, high efficient, I2C programmable LiFePO4 battery manager single chip solution
Inputs are protected from overvoltage and polarity reversal Independent, programmable current limiters for USB and wall
adapter lines Instant-on operation with low battery; operation without battery
installed is allowed Embeds a Linear Technology LTC2942 I2C programmable fuel
gauge with charge counter and voltage converter
January 2016 83
Host Hardware (Main Power Supply)Host Hardware (Main Power Supply)MAX8815 step up converter
5V up to 1APower on and shutdown
signals controlled by a PIC12microcontroller
January 2016 84
Host Hardware (Main Power Supply)Host Hardware (Main Power Supply)
5V up to 1A are generated by a dedicated Maxim MAX8815A step up converter, independently by battery charge status
A PIC12F1571 microcontroller generates the proper power on/shutdown signals (wake and shutdown) based on user request on the Power On pushbutton
Shutdown signal is propagated to the CPU thus allowing a graceful Linux power off
Standard PIC brown out circuit detects low battery status and shuts down the system
January 2016 85
Host Hardware (Shield connection)Host Hardware (Shield connection)TTL to 3v3 bidirectional
translators
CPU controlled7V@300mA Step Up
Power supply
January 2016 86
Host Hardware (Main Power Supply)Host Hardware (Main Power Supply)
A Philips dual channels IC translator interfaces the TTL based serial line coming from the AirSensEUR Shield into a 3v3 lines and viceversa
The AirSensEUR Shield power supply is generated by the Host through a Microchip MCP1661 step up converter.
MCP1661 generates 7V with 300mA max current starting from the unregulated battery voltage.
An active switch, composed by Q3 and Q4, is used to completely shut off the 7V generation if required by the CPU
January 2016 87
Host Hardware (Dongles connection)Host Hardware (Dongles connection)USB host connectors
For USB WiFi and GPRS
MAX8815 step up converter5V max 1A
Control line from theCPU module
January 2016 88
Host Hardware (Dongles connection)Host Hardware (Dongles connection)
Two 5V up to 1A rails are generated by two dedicated Maxim MAX8815A step up converter, independently by battery charge status
Compatible to USB specifications (max 500mA for USB 1 and 2 and up to 900mA for USB3)
Step up converters can be turned on/off by a dedicated GPIO line in the CPU module
January 2016 89
Host Hardware (Front Panel)Host Hardware (Front Panel)PORTB used as input
MCP23017 I2C16 I/O programmable lines
PORTA used as output
January 2016 90
Host Hardware (Front panel)Host Hardware (Front panel)
Based on Microchip MCP23017 I2C 16 bit programmable I/O expander
Supported by Linux driver kernels as a standard GPIO and LED devices
Two available input/output ports used, respectively, for LEDs and pushbuttons
The front panel implements other spare pushbuttons, like PowerOn and Reset, and 3DFix LED, via custom lines
January 2016 91
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 92
Host Linux CustomizationHost Linux Customization
The AirSensEUR Host runs a small Debian Linux distribution Targeted to Debian Wheezy with kernel 4.2.6 for armel
architecture The AirSensEUR Host is an embedded system... no BIOS
installed To properly boot Linux, a suitable bootloader is needed. This is
an open source bootloader provided by Atmel Corporation Kernel and bootloader should be patched to target to the CPU
module and to AirSensEUR framework A 115200bps 8N1 serial debug port is available for reading boot
logs and interact with a bare ASCII shell
January 2016 93
Host Linux Customization (Boot process)Host Linux Customization (Boot process)
Three actors are involved: CPU, bootloader and Linux Kernel The CPU scans for a well known places in order to find a
“signed” binary acting as bootloader The CPU loads the bootloader binary into an onchip RAM
memory and starts running the bootloader The bootloader initializes the onchip peripherals (timers,
memory interface and so on) then loads the kernel image, from a well defined location, into the external RAM
The bootloader loads the kernel configuration image file from a well defined location
The bootloader uncompress the Linux kernel then starts run it
January 2016 94
Host Linux Customization (Boot process)Host Linux Customization (Boot process)
The kernel parses the binary configuration image and initializes the drivers embedded into the kernel image
The kernel “mounts” an external filesystem where other drivers are availables as “modules”
The kernel locates all the available driver modules, loads and configure them based on the binary configuration image details
The kernel starts the application services The kernel runs the user defined initialization script
January 2016 95
Host Linux Customization (Boot process)Host Linux Customization (Boot process)
CPU: AT91SAM9G25 made by Atmel Corporation CPU module: AriettaG25 made by Acme Systems Srl Bootloader: at91bootstrap-3.7 (patched) compiled as boot.bin Kernel: Linux kernel 4.2.6 (patched)
Kernel Image: zImage Kernel binary configuration file tree: acme-arietta.dtb
Linux filesystem: Debian Wheezy generated with Multistrap Kernel modules: located on /lib/modules/4.2.6+ User defined initialization script: bash script located at
/etc/rc.local
January 2016 96
Host Linux Customization (CPU Boot process)Host Linux Customization (CPU Boot process)
AT91SAMG25 is not a “simple” CPU: is a SystemOnChip (SOC) It embeds an ARM CPU core AND a lot of peripherals like: MMU,
MMC, Timers, I/O, ADC, PWM, I2C, SPI, USB, Watchdog, and many other
It provides a very small ROM where a very basic program is stored and is run by the CPU at power on
The CPU scans a set of well known peripherals (USB, SPI, GPIO and MMC) in order to find a predefined byte pattern signature identifying a suitable bootloader.
AirSensEUR Host provides this bootloader in a boot.bin file located in the first partition of the microSD card. This partition should be formatted with a valid FAT16 filesystem
January 2016 97
Host Linux Customization (Bootloader)Host Linux Customization (Bootloader)
At91boostrap code is an open source bootloader provided by Atmel Corporation
It's targeted to a set of SOC made by Atmel and should be patched to target the AriettaG25 module
The bootloader initializes the basic peripherals (timer, watchdog, MMU) in order to allow access to the external RAM memory
The bootloader loads kernel and configuration images from the first partition of the microSD card, formatted with FAT filesystem, by pointing to the files zImage and acme-arietta.dtb
The bootloader deflates the Linux kernel image and jumps to the first kernel opcode
January 2016 98
Host Linux Customization (Kernel boot)Host Linux Customization (Kernel boot)
AirSensEUR Host runs a patched version of the Linux kernel 4.2.6
Patches are related to some missing/mismatching drivers (the battery manager IC, the I2C expander in the front panel and some minor changes in the CPU module)
The kernel parses the binary tree configuration file then starts configuring and executing the drivers found in the uncompressed zImage file
The kernel “mounts” the second partition found in the microSD where an EXT4 filesystem is populated with dynamically compiled driver modules
The kernel loads and configure the external driver modules
January 2016 99
Host Linux Customization (DTS and DTB)Host Linux Customization (DTS and DTB)
The Linux kernel is widely adopted in platform with different architecture, peripherals and options
When running on a PC, a set of enumeration and discovery systems have been provided. This is not true for an embedded system
The solution is to provide a standard configuration file where all configuration details are specified. This is performed by the Device Tree
ASCII readableconfiguration file
source (DTS)
Binary parsableconfiguration file
(DTB)DTS Compiler
January 2016 100
Host Linux Customization (DTS and DTB)Host Linux Customization (DTS and DTB)
The DTS is a “tree” where the hardware relationships and parameters are defined
Bus controllers are linked with bus connected devices Bus connected devices are logically added to low level services
and so on
January 2016 101
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 102
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 103
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 104
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 105
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 106
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
January 2016 107
Host Linux Customization Host Linux Customization (Hardware Abstraction)(Hardware Abstraction)
The Linux Kernel provides an “easy” way to map hardware subsystems into files descriptors.
AirSensEUR Host uses this feature to statically map I/O, serial lines, I2C subsystems into a well known set of file descriptors
High level applications and bash scripts can use those files to access to the hardware “like if” they're interacting with a file
That's one of the sucess keys of the Linux kernel!
echo 1 > /sys/class/leds/led_1/brightnessecho "Hello" > /dev/ttyS1echo “ATDT123456” > /dev/ttyUSB2
January 2016 108
Host Linux Customization Host Linux Customization (Hardware Abstraction)(Hardware Abstraction)
Subsystem Driver File PathDirect GPIO atmel-gpio /sys/class/gpioDirect GPIO leds-gpio /sys/class/ledsI2C LEDs leds-gpio (gpio virtualized
by gpio-mcp23s08 driver)/sys/class/leds
Direct pushbuttons gpio-keys ./sys/class/input/event0I2C pushbuttons gpo-keys-polled (gpio
virtualized by gio-mcp23s08 driver)
/sys/class/input/event1
I2C Battery manager ltc4156_charger /sys/class/power-supply/ltc4156-charger-0
I2C Battery Fuel Gauge ltc2941-battery-gauge /sys/class/power-supply/ltc2943-0
Step up converters, GPS power supply and PPS signals are treated as GPIO lines
January 2016 109
Host Linux Customization (rootfs)Host Linux Customization (rootfs)
The Linux kernel is only part of the game. AirSensEUR Host applications runs over a Linux Debian
Wheezy distribution A Wheezy distribution can be easily obtained though Multistrap Multistap allows defining a set of application packages to be
included in the target file system, thus simplifying the Debian customization process
The target file system should be copied into the microSD second partition, formatted with an EXT4 filesystem, together with the AirSensEUR Host application binaries and shell scripts
January 2016 110
Host Linux Customization (middleware)Host Linux Customization (middleware)
Small programs running in background Provides elementary functions needed to interface the
hardware with high level applications To be compiled from sources and copied on the target rootfs
Eventmonitor rpi_gpio_ntp
January 2016 111
Host Linux Customization (Eventmonitor)Host Linux Customization (Eventmonitor)
Small C custom program for AirSensEUR Host Listen to incoming pushbuttons event and executes the
appropriate batch script Batch script should be located into /etc/eventmonitor folder,
should be named “hadler_xx” where xx is the event number associated to the pushbutton. Script should be marked as executable
January 2016 112
Host Linux Customization (rp_gpio_ntp)Host Linux Customization (rp_gpio_ntp)
Written by Folkert van Heusden and released under GPLv2 Works together with an NTP server running on the same
platform Analyzes a GPIO line where a PPS signal is connected and
transfer information to the NTP server Useful to implement a local precise NTP server when no
network connectivity is present but GPS information is available
January 2016 113
Host Linux Customization (NTP Service)Host Linux Customization (NTP Service)
NTPd https://www.eecis.udel.edu/~mills/ntp/html/ntpd.html Synchronizes the local real time clock with a set of sources Sources can be defined via a configuration script Contacts external NTP server when data network is ready Interact with GPS local services when available
January 2016 114
Host Linux Customization (GPS Service)Host Linux Customization (GPS Service)
GPSd http://www.catb.org/gpsd/ Monitors the AirSensEUR Host GPS module connected through
a serial line Publishes JSON data through a local TCP socket on port 2947 Provides an abstraction layer between GPS module brands and
protocols Provides access to GPS data to multiple processes Interacts with a local NTP server for (second based) precise
local timestamp synchronization with GPS information
January 2016 115
Host Linux Customization (Hostapd)Host Linux Customization (Hostapd)
Hostapd is a user space daemon for access point and authentication servers (https://w1.fi/hostapd/)
It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and more
Licenced under the terms of BSD license. Implementred by Jouni Malinen
AirSensEUR Host requires Hostapd to provide a WiFi access point connection through a WiFi USB dongle.
January 2016 116
Host Linux Customization (WvDial)Host Linux Customization (WvDial)
WvDial https://github.com/wlach/wvdial Is a command line ppp dialer Easy to configure and use Can be run by external batch scripts
AirSensEUR Host requires WvDial to handle GPRS ppp connections via a GPRS USB dongle
January 2016 117
Host Linux Customization (Lighttpd)Host Linux Customization (Lighttpd)
Lighttpd https://www.lighttpd.net/ Small footprint, efficient HTTP server Easy to configure Can provide CGI and integrate with interpreted languages, like
PHP
AirSensEUR Host requires Lighttpd to provide a Web Console Management for easy setup and control
January 2016 118
Host Linux Customization (Many others)Host Linux Customization (Many others)
Many others services and middle layer applications are required by the AirSensEUR Host to properly operate
Some examples are: Shell Cron Network tools SSH Server Java Runtime Environment …
The whole services are downloaded and installed by Multistrap
January 2016 119
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 120
AirSensEUR Host ApplicationsAirSensEUR Host Applications
Two categories
BASH Scripts
Java Applications
January 2016 121
Host Applications (Bash scripts)Host Applications (Bash scripts)
Mainly for very high level, very common, system related actions Start/stop processes and services Check processes and service status Handle network interfaces Handle LEDs and step up converter status
Fast and easy to implement and maintain Mainly located in /usr/local/airsenseur
January 2016 122
Host Applications (Java Applications)Host Applications (Java Applications)
Targeted to data management Connect to the AirSensEUR Shield and other data sources Store collected data in a local sqlite database Perform data transfer to external servers
Can benefit from the pletora of freely available and tested Java libraries
Developing Java is fast and the debug can be performed remotely through a TCP connection
January 2016 123
Host Applications (Java Applications)Host Applications (Java Applications)
Three main Actors:
Data Aggregator Data PushHost
January 2016 124
Host Applications (Java Applications)Host Applications (Java Applications)
Data Aggregator
Sqlite FileGPS Info
AirSesEUR Shield
GPSd
Host/dev/ttyS1
/dev/ttyS2
JSONRPCTCP 8000
JSONTCP 2947
January 2016 125
Host Applications (Java Applications)Host Applications (Java Applications)
Sqlite File
Data Push
JSON, XML,
...
January 2016 126
Host Applications (AirSensEURHost)Host Applications (AirSensEURHost) Depends on jna and purejavacomm
libraries. They provides direct access to the USB/serial line interface from the JVM
Depends on json rpc and jackson libraries
Depends on expr-master libraries providing mathematical expression evaluation
Depends on AirSensEURLib project, mainly for .comm, .json, .persisters and .exceptions packages
January 2016 127
AirSensEURHost (Communcation Layer)AirSensEURHost (Communcation Layer)
ChemSensorBoard is an abstract class providing the basic interface for connect/disconnect and read/write to the AirSensEUR Shield
ChemSensorHostCommHandler implements ChemSensorBoard with the help of SerialPortHelper and CommProtocolHelper classes
ChemSensorBoard
ChemSensorHostCommHandler
CommProtocolHelper
SerialPortHelper
AirSensEURLib
January 2016 128
AirSensEURHost AirSensEURHost (ChemSensorHostCommHandler)(ChemSensorHostCommHandler)
ChemSensorHostCommHandler implements the ChemSensorBoard interface
It contains a SerialPortHelper instance to perform actions through the serial port
It implements the SerialPortHelper.SerialReceiverParser interface by calling the CommProtocolHelper.onRxCharReceived when needed
Defines a ChemSensorMessageHandler.onNewPacketReady callback function used to signal new valid packets received from the AirSensEUR Shield
It automatically connects and re-connects to the serial line if errors where detected on the received data
January 2016 129
AirSensEURHost (ChemSensorHost)AirSensEURHost (ChemSensorHost)
ChemSensorHost implements the ChemSensorMessageHandler interface and extends the Timer functionality
It periodically asks for new samples availability to the AirSensEUR Shield
Each sample received by the AirSensEUR Shield is evaluated and stored on a local buffer (implemented by the CollectedData internal class)
The collected data list contents is extracted by the ChemSensorServiceImpl class (explained later)
January 2016 130
AirSensEURHost (JSONServer)AirSensEURHost (JSONServer)
JSONServer is a wrapper class used to instantiate a JSON RPC server by mean of the jsonrpc4j library
Available procedures are defined by the ChemSensorService interface located on the AirSensEURLib library
January 2016 131
AirSensEURHost (ChemSensorServiceImpl)AirSensEURHost (ChemSensorServiceImpl)
ChemSensorService interface is implemented by ChemSensorServiceImpl class
Each method is called by the JSONServer engine Data are sent back to the client through containers defined in
AirSensEURLib library
January 2016 132
AirSensEURLib (SampleData)AirSensEURLib (SampleData)
January 2016 133
Host Applications (ASDataAggregator)Host Applications (ASDataAggregator) Depends on json rpc and jackson
libraries Depends on sqlite4java and gpsd4java
libraries Depends on AirSensEURLib project,
mainly for .comm, .json, .persisters and .exceptions packages
Depends on AirSensEURSqlLib project, where SQL helpers and containers are shared with other host applications
January 2016 134
AirSensEURDataAggregator AirSensEURDataAggregator
AirSensEURDataAggregator aggregates information coming from multiple sources and store all together in a local sqlite database
Acts as a JSON client for the GPSd daemon Acts ad a JSON client for the AirSensEURHost application Uses local clock to timestamp all information inserted in the
database
All aggregated information are stored as a single entry in the database table and, for convenience, on a .csv local file
January 2016 135
ASDataAggregator (ASDataAggregatorEngine)ASDataAggregator (ASDataAggregatorEngine)
AirSensEURDataAggregatorEngine.task() implements the whole actions. It's periodically called by the main routine
End
Start
GPSDataCollector.poll()
ChemSensorClient.getLastSample
SamplePersisterFile.addSample()
SamplePersisterSQL.addSample()
January 2016 136
ASDataAggregator (GPSDataCollector)ASDataAggregator (GPSDataCollector)
GPSDataCollector is a wrapper class for the GPSEndpoint provided by the gpsd4java library
It connects to the GPSd server and retrieves JSON data GPS information are temporarily stored to be used by the
aggregator engine when required
January 2016 137
AirSensEURLib (ChemSensorClient)AirSensEURLib (ChemSensorClient)
ChemSensorClient is a wrapper for a ChemSensorService proxy implementation
Any call to a function by instances of ChemSensorClient generates an RPC to the server through a socket connection
RPC results are returned as a plain java object
Remote Procedure Calls details are completely hidden to calling methods
January 2016 138
AirSensEURLib (ChemSensorClient)AirSensEURLib (ChemSensorClient)
January 2016 139
AirSensEURLib (SampleData)AirSensEURLib (SampleData)
January 2016 140
AirSensEURLib (SamplesPersister)AirSensEURLib (SamplesPersister)
SamplesPersister is a public interface defining a set of functions common to an abstract entity providing persistance of sample datasets (SampleDataContainer)
January 2016 141
AirSensEURLib (SamplePersisterFile)AirSensEURLib (SamplePersisterFile)
SamplePersisterFile implements a SamplePersister interface by providing a comma separated values file persistence
January 2016 142
AirSensEURSqlLib (SamplePersisterSQL)AirSensEURSqlLib (SamplePersisterSQL)
SamplePersisterSQL implements a SamplePersister interface by providing persistence through sqlite database
January 2016 143
Host Applications (AirSensEURDataPush)Host Applications (AirSensEURDataPush) Depends on sqlite4java, httpclient and
jackson libraries Depends on AirSensEURLib project,
mainly for .comm, .json, .persisters and .exceptions packages
Depends on AirSensEURSqlLib project, where SQL helpers and containers are shared with other host applications
Depends on AirSensEURSOSDbLib project where 52North SOS RPCs are implemented
Depends on AirSensEURInfluxDBLib project where INFLUX DB helpers are implemented
January 2016 144
AirSensEURDataPushAirSensEURDataPush
AirSensEURDataPush reads information stored on a local sqlite database and pushes uncommitted data to a remote data server
It can push data to two remote data server types: 52North SOS server (JSON InsertObservations API) INFLUX DB
January 2016 145
AirSensEURDataPushAirSensEURDataPush
In 52North SOS working mode: For each sensor channel:
Ask for the last valid sample data timestamp Retrieve a chunk of data from the local database with
timestamp greater than the last found timestamp Populate an InsertObservation by filling a multiple set of
Observations Generate an HTTP POST with the JSON(ized)
InsertObservation pointing to the remote server Repeat until no more samples are available in local
database
January 2016 146
AirSensEURDataPushAirSensEURDataPush
In INFLUX DB working mode: Retrieve, from a local history database file, the timestamp for
the last valid commit operation Retrieve, from the local samples database, a chunk of data
with timestamp greater than the last found timestamp Populate a SampleDataSerie with collected information Generate an HTTP POST with the JSON(ized)
SampleDataSerie pointing to the remote server Update the local history database with last updated
timestamp Repeat until no more samples are available in the local
database
January 2016 147
AirSensEURDataPushAirSensEURDataPush
Data Push
History persister
SQL persisterSample persister
Configuration engine
January 2016 148
AirSensEURDataPush (HistoryPersister)AirSensEURDataPush (HistoryPersister)
Used to retrieve the timestamp of a last valid data committed to the server
HistoryPersisterSQL generates a local sqlite file and uses it as a persistance medium
HistorySOSDB executes a GetDataAvailability RPC on SOS server
January 2016 149
AirSensEURDataPush (SamplesPersister)AirSensEURDataPush (SamplesPersister)
SamplePersisterInfluxDB persists data through a set of HTTP JSON post compatible with Influx DBs
SamplePersisterSOSDB generates a set of InsertObservation calls. Each InsertObservation is filled by several Observation(s) in order to increase the network efficiency. Required containers are defined in the .dao package in AirSensEUDSOSDbLib project
January 2016 150
Java Host Application configurationJava Host Application configuration
Configuration is handled by a private Configuration class implemented in all Java Host Applications
Configuration is read from .properties file. Valid tokens and possible values are defined in the Configuration class
configuration.properties files are located at /usr/local/etc and are specified by the shell startup scripts with the following rules
Application Configuration FileAirSensEURHost sensor.propertiesAirSensEURDataAggregator aggregator.propertiesAirSensEURDataPush (SOSDb) datapushsosdb.propertiesAirSensEURDataPush (InfluxDb) influxpush.properties
January 2016 151
AgendaAgenda
System Overview AirSensEUR Shield Hardware AirSensEUR Shield Firmware The Java Panel Application AirSensEUR Host Hardware AirSensEUR Host Linux Customization AirSensEUR Host Applications AirSensEUR Web Management Console
January 2016 152
Web Management ConsoleWeb Management Console
Feedback on AirSensEUR Host activities status Simple configuration of main working parameters Wi-Fi reachable
January 2016 153
Web Management ConsoleWeb Management Console
Server Side Browser Side
HTTPAjaxJSON
January 2016 154
Web Management ConsoleWeb Management Console
Client side developed with ReactJs and styled with Bootstrap Client-Server data exchange in JSON format Server side developed with small PHP scripts targeting a single
function each one PHP scripts interacting with BASH for configuration files
generation, start/stop processes Live data retrieved by PHP scripts through JSON calls to
AirSensEURHost application and sent back to the client Data Push scheduling through custom cron file PHP editors