AirSensEUR is an open framework focused on air quality monitoring using low cost sensors. The project started on 2014 from a group of passionate researchers and engineers. The framework is composed by a set of electronic boards, firmware, and several software applications. The slides present the technical solutions adopted in the framework by entering on hardware and software related details. For more info, look at the project website www.airsenseur.org
2. 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
3. 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)
4. 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
5. 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
6. 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
ATMega328
uController
T and
%RH
Pressure
I2C sensor
board
Local Power
Supply and
level shifters
Chemical Sensors
Power
Supply
+6V DC
TTL serial
To host
Arduino
USB2Serial
adapter connector
Vin-
Vad
Vafe
Vout
7. January 2016 7
Shield Hardware (single channel)Shield Hardware (single channel)
I2C Programmable AFE
(current to voltage
conversion)
Analog to Digital conversion
through SPI
I2C Digital to Analog
Generates voltage
references
Compatibility with
multiple sensor brands
8. 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)
9. 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-)
10. 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)
11. January 2016 11
Shield Hardware (THP and supply)Shield Hardware (THP and supply)
Factory calibrated
I2C Humidity and
Temperature sensor
Factory calibrated
I2C Pressure sensor
Linear 5V regulator
12. 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
13. January 2016 13
Shield Hardware (ATMega328)Shield Hardware (ATMega328)
ATMega 328
Running at 16MHz
Arduino(tm) USB2Serial
connector onboard
Programmable LED
onboard
14. 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
15. 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
16. 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
18. 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();
}
19. 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);
}
20. 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);
}
}
21. 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);
}
}
22. 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.
23. 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(...);
24. 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
25. 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
26. 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 trailer
if(pivotChar == COMMPROTOCOL_TRAILER) {
ProcessBuffer();
...
}
break;
}
}
onDataReceived is responsible for data framing and processing
27. January 2016 27
Shield Firmware (CommProtocol)Shield Firmware (CommProtocol)
void CommProtocol::processBuffer() {
// .. we expect the command ID, validate it
...
// Execute the action
typedef bool (*fpointer)(CommProtocol* context,
unsigned char cmdOffset);
fpointer handler =
&(validCommands[offsetId].handler);
if (valid && handler != 0) {
valid = (*handler)(this, offsetId);
}
// Signal an invalid/fault condition
if (!valid) {
strcpy_P((char*)buffer,
commProtocolErrorString);
}
// Send back the result
Serial.print((char*)buffer);
ProcessBuffer calls the proper callback and send back results
28. 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
29. 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
30. 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.
31. 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
32. 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
33. 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
34. 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
35. 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;
}
36. 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;
}
37. 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;
}
38. 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()
40. 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.
41. 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;
}
42. 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;
}
43. 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));
}
44. 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));
}
45. 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
46. 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);
}
47. 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
48. 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.
LMP91000LMP91000
ADC16S626ADC16S626
AD5694RAD5694R
UR100CDUR100CD
SFE_BMP180SFE_BMP180
49. 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);
...
50. 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;
};
51. 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;
}
52. 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
53. 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
54. 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)
55. 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
ChemSensorBoardChemSensorBoard
ChemSensorCommHandle
r
ChemSensorCommHandle
r
CommProtocolHelperCommProtocolHelper
SerialPortHelperSerialPortHelper
AirSensEURLib
56. 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.
57. 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);
}
}
}
}
58. 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
59. January 2016 59
AirSensEURLib (CommProtocolHelper)AirSensEURLib (CommProtocolHelper)
Three set of methods are provided:
− “render” methods
− “eval” methods
− “data list” methods
60. 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()
61. 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)
62. 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()
63. 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 messages
synchronized public boolean onRxCharReceived(byte value)
64. 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();
}
65. January 2016 65
Java Panel ApplicationJava 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
66. 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
67. 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) {
}
}
68. 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
69. January 2016 69
Java Panel Application (SensorSetupDialog)Java Panel Application (SensorSetupDialog)
Two setup dialogs are derived from the SensorSetupDialog:
− ChemSensorSetupDialog
− GenericSensorSetupDialog
71. January 2016 71
Java Panel ApplicationJava Panel Application
(GenericSensorSetupDialog)(GenericSensorSetupDialog)
IIRAndAvgPanelIIRAndAvgPanel
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
72. January 2016 72
Java Panel Application (setupPanel example)Java Panel Application (setupPanel example)
The simplest panel is the IIRAndAvgPanel
73. January 2016 73
Java Panel Application (Setup Panels)Java Panel Application (Setup Panels)
IIRAndAvgPanelIIRAndAvgPanel
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
74. January 2016 74
Java Panel Application (Data Models)Java Panel Application (Data Models)
Two common typologies are needed:
− Slider and Register Data Models
75. 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
76. January 2016 76
Java Panel Application (SampleLogger)Java Panel Application (SampleLogger)
Two SampleLogger derived classes:
− LineGraphSampleLoggerPanel
− TextBasedSampleLoggerPanel
77. 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
78. 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
79. 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
80. January 2016 80
Host HardwareHost Hardware
Step up
Power Supply
5V@1A
LiFePO4
Battery
ARM9 based
module
GPS
moduleFront Panel
Step up Power
Supply
7V@300mA
and level
shifters
Battery
Manager
+5VDC@3A SerialUSB
Step up
Power Supply
5V@1A
Step up
Power Supply
5V@1A
Unregulated
3v3 line
5V line
WiFi
Dongle
GPRS
Dongle
AirSensEUR
Shield
I2C
USB
81. January 2016 81
Host Hardware (Battery Manager)Host Hardware (Battery Manager)
LTC4156 I2C programmable
LiFePO4 battery manager
LTC2942 I2C
Fuel Gauge Engine
Active overvoltage and
reverse polarity protection
82. 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
83. January 2016 83
Host Hardware (Main Power Supply)Host Hardware (Main Power Supply)
MAX8815 step up converter
5V up to 1A
Power on and shutdown
signals controlled by a PIC12
microcontroller
84. 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
85. January 2016 85
Host Hardware (Shield connection)Host Hardware (Shield connection)
TTL to 3v3 bidirectional
translators
CPU controlled
7V@300mA Step Up
Power supply
86. 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
87. January 2016 87
Host Hardware (Dongles connection)Host Hardware (Dongles connection)
USB host connectors
For USB WiFi and GPRS
MAX8815 step up converter
5V max 1A
Control line from the
CPU module
88. 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
89. January 2016 89
Host Hardware (Front Panel)Host Hardware (Front Panel)
PORTB used as input
MCP23017 I2C
16 I/O programmable lines
PORTA used as output
90. 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
91. 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
92. 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
93. 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
94. 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
95. 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
96. 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
97. 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
98. 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
99. 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 readable
configuration file
source (DTS)
ASCII readable
configuration file
source (DTS)
Binary parsable
configuration file
(DTB)
Binary parsable
configuration file
(DTB)DTS CompilerDTS Compiler
100. 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
101. January 2016 101
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
102. January 2016 102
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
103. January 2016 103
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
104. January 2016 104
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
105. January 2016 105
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
106. January 2016 106
Host Linux Customization (DTS examples)Host Linux Customization (DTS examples)
107. January 2016 107
Host Linux CustomizationHost 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/brightness
echo "Hello" > /dev/ttyS1
echo “ATDT123456” > /dev/ttyUSB2
108. January 2016 108
Host Linux CustomizationHost Linux Customization
(Hardware Abstraction)(Hardware Abstraction)
Subsystem Driver File Path
Direct GPIO atmel-gpio /sys/class/gpio
Direct GPIO leds-gpio /sys/class/leds
I2C LEDs leds-gpio (gpio virtualized
by gpio-mcp23s08 driver)
/sys/class/leds
Direct pushbuttons gpio-keys ./sys/class/input/event0
I2C 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
109. 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
110. 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
111. 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
112. 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
113. 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
114. 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
115. 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.
116. 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
117. 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
118. 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
119. 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
120. January 2016 120
AirSensEUR Host ApplicationsAirSensEUR Host Applications
Two categories
BASH ScriptsBASH Scripts
Java ApplicationsJava Applications
121. 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
122. 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
123. January 2016 123
Host Applications (Java Applications)Host Applications (Java Applications)
Three main Actors:
Data Aggregator Data PushHost
124. January 2016 124
Host Applications (Java Applications)Host Applications (Java Applications)
Data Aggregator
Sqlite FileGPS Info
AirSesEUR
Shield
GPSd
Host/dev/ttyS1
/dev/ttyS2
JSONRPC
TCP 8000
JSON
TCP
2947
125. January 2016 125
Host Applications (Java Applications)Host Applications (Java Applications)
Sqlite File
Data Push
JSON,
XML,
...
126. 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
127. 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
ChemSensorHostCommHandl
er implements
ChemSensorBoard with the
help of SerialPortHelper and
CommProtocolHelper classes
ChemSensorBoardChemSensorBoard
ChemSensorHostCommHandlerChemSensorHostCommHandler
CommProtocolHelperCommProtocolHelper
SerialPortHelperSerialPortHelper
AirSensEURLib
128. January 2016 128
AirSensEURHostAirSensEURHost
(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
129. 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)
130. 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
131. 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
133. 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
134. January 2016 134
AirSensEURDataAggregatorAirSensEURDataAggregator
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
135. January 2016 135
ASDataAggregator (ASDataAggregatorEngine)ASDataAggregator (ASDataAggregatorEngine)
AirSensEURDataAggregatorEngine.task() implements the
whole actions. It's periodically called by the main routine
EndEnd
StartStart
GPSDataCollector.poll()GPSDataCollector.poll()
ChemSensorClient.getLastSampleChemSensorClient.getLastSample
SamplePersisterFile.addSample()SamplePersisterFile.addSample()
SamplePersisterSQL.addSample()SamplePersisterSQL.addSample()
136. 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
137. 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
140. 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)
141. January 2016 141
AirSensEURLib (SamplePersisterFile)AirSensEURLib (SamplePersisterFile)
SamplePersisterFile implements a SamplePersister interface
by providing a comma separated values file persistence
142. January 2016 142
AirSensEURSqlLib (SamplePersisterSQL)AirSensEURSqlLib (SamplePersisterSQL)
SamplePersisterSQL implements a SamplePersister interface
by providing persistence through sqlite database
143. 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
145. 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
146. 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
148. 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
149. 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
150. 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 File
AirSensEURHost sensor.properties
AirSensEURDataAggregator aggregator.properties
AirSensEURDataPush (SOSDb) datapushsosdb.properties
AirSensEURDataPush (InfluxDb) influxpush.properties
151. 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
152. January 2016 152
Web Management ConsoleWeb Management Console
Feedback on AirSensEUR Host activities status
Simple configuration of main working parameters
Wi-Fi reachable
153. January 2016 153
Web Management ConsoleWeb Management Console
Server Side Browser Side
HTTP
Ajax
JSON
154. 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