Peripheral Protocol Handler (perif://)

Background

TQLEngine supports Peripheral Protocol Handler out-of-the-box to handle communication with sensors and actuators that are connected to microcontroller unit over a Serial interface.

Peripheral Protocol Handler Flow

DateLocationVersion
7/17/2020

Download

ExpressionParser

2.0.0

Programming Microcontroller Pins

It is important to note that Peripheral Handler does not provide provisions for interfacing directly with non-serial microcontroller ports. Example of non-serial ports on the microcontroller board are :

  • General Purpose Input/Output (GPIO)
  • Inter-Integrated Circuit Bus (I2C)
  • Serial Peripheral Interface Bus (SPI)
  • Analog to Digital Convertor (ADC)
  • Digital to Analog Convertor (DAC)
  • Universal Asynchronous Receiver/Transmitter (UART)
  • AT Command Devices (XBee)

In modern day microcontroller boards, users will have to write a sketch to program the microcontroller. sketch is the name that Arduino uses for a program. It's the unit of code that is uploaded to and run on an Arduino board.

Writing Sketch  Tips

  • Frequency of Write Operation - Use only once per loop of iteration. Number of println's will result in frequency of data on the USB. 
  • Output format - Create a output format that can be parsed easily in a meaningful way.
  • Keep it Generic - Keep the logic in sketch strictly for controlling or programming pins. Any device specific logic can be abstracted to a ThingFacet.

Sketch Examples

Controlling the Solenoid Switch connected to Intel Arduiono UNO. 

Solenoid Switch Sketch
int incomingByte = 0;   // for incoming serial data
int solenoidPin = 5;    //This is the output pin on the Arduino we are using

void setup() {
  Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
  // put your setup code here, to run once:
  pinMode(solenoidPin, OUTPUT);           //Sets the pin as an output
}

void loop() {
  if (Serial.available() > 0) {
     // read the incoming byte:
     incomingByte = Serial.read();
     
     // say what you got:
     //Serial.print("Received: ");
     //Serial.println(incomingByte, DEC);
     String responseStr = "Received:" + String(incomingByte);
     if (isDigit(incomingByte)) {
        int controlValue = int(incomingByte - '0');
        //Serial.print("Control: ");
        responseStr = responseStr + "#Control:";
        
        if (controlValue > 0){
             digitalWrite(solenoidPin, HIGH);    //Switch Solenoid ON
             //Serial.println("ON");
             responseStr = responseStr + "ON";
        }
        else if (controlValue == 0) {
             digitalWrite(solenoidPin, LOW);     //Switch Solenoid OFF
             //Serial.println("OFF");
             responseStr = responseStr + "OFF";
        }
     }
     Serial.println(responseStr);  
  }
}

Reading Sensor Data (Temperature, Humidity, and Light)

Reading Sensor Data
#include <math.h>
// Temperature Analog Sensor
int sensorPinT = A0; // select the input pin for the potentiometer
int sensorPinL = A4; // select the input pin for the potentiometer
 
double Thermistor(int RawADC) {
  double Temp;
  Temp = log(10000.0*((1024.0/RawADC-1))); 
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
  Temp = Temp - 273.15;            // Convert Kelvin to Celcius
   //Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
   return Temp;
}

String getTemperature() {
 String tempStr = "TEMPC:";
 int readVal=analogRead(sensorPinT);
 double tempC =  Thermistor(readVal);
 // now convert to Fahrenheit
 double tempF = (tempC * 9.0 / 5.0) + 32.0;
 tempStr = "TEMPC:" + String(tempC) + "#TEMPF:" + String(tempF);
 return tempStr; 
}

//KY015 DHT11 Temperature and humidity sensor 
int DHpin = 8;
byte dat [5];
byte read_data () {
  byte data;
  for (int i = 0; i < 8; i ++) {
    if (digitalRead (DHpin) == LOW) {
      while (digitalRead (DHpin) == LOW); // wait for 50us
      delayMicroseconds (30); // determine the duration of the high level to determine the data is '0 'or '1'
      if (digitalRead (DHpin) == HIGH)
        data |= (1 << (7-i)); // high front and low in the post
      while (digitalRead (DHpin) == HIGH); // data '1 ', wait for the next one receiver
     }
  }
return data;
}
 
void start_test () {
  digitalWrite (DHpin, LOW); // bus down, send start signal
  delay (30); // delay greater than 18ms, so DHT11 start signal can be detected
 
  digitalWrite (DHpin, HIGH);
  delayMicroseconds (40); // Wait for DHT11 response
 
  pinMode (DHpin, INPUT);
  while (digitalRead (DHpin) == HIGH);
  delayMicroseconds (80); // DHT11 response, pulled the bus 80us
  if (digitalRead (DHpin) == LOW);
  delayMicroseconds (80); // DHT11 80us after the bus pulled to start sending data
 
  for (int i = 0; i < 4; i ++) // receive temperature and humidity data, the parity bit is not considered
    dat[i] = read_data ();
 
  pinMode (DHpin, OUTPUT);
  digitalWrite (DHpin, HIGH); // send data once after releasing the bus, wait for the host to open the next Start signal
}

 
String getHumidity () {
  start_test ();
  String hum = "";
  hum = "HUMPCT:" + String(dat [0], DEC) + "." + String(dat [1], DEC);
  return hum;
}

int ledPin = 13; // select the pin for the LED

String getLight() {
  String amb = "";
  int sensorValue = 0; // variable to store the value coming from the sensor
  sensorValue = analogRead(sensorPinL);
  digitalWrite(ledPin, HIGH);
  delay(sensorValue);
  digitalWrite(ledPin, LOW);
  delay(sensorValue);
  amb = "AMB:" + String(sensorValue, DEC);
  return amb;
}

 
void setup () {
  Serial.begin (9600);
  pinMode (DHpin, OUTPUT);
  pinMode (ledPin, OUTPUT);
}

void loop() {
  //TEMPC:25#TEMPF:110#HUMPCT:40#AMB:20
  Serial.println(getTemperature() + "#" + getHumidity() + "#" + getLight());
  delay (1000);
}

Pde to Libelium Waspmote

Reading data from Waspmote
// Include libraries
#include <WaspXBee802.h>
#include <WaspFrame.h>
#include <WaspSX1272.h>
#include <WaspSensorAgr_v20.h>

// Define BROADCAST MAC address
//////////////////////////////////////////
char RX_ADDRESS[] = "000000000000FFFF";
//////////////////////////////////////////

// Define the Waspmote ID
char WASPMOTE_ID[] = "node_01";

// define variable
uint8_t error;

void setup() {

  RTC.ON();
  ACC.ON();
  frame.setID( WASPMOTE_ID );
  // init XBee
  xbee802.ON();

  SensorAgrv20.ON();
}


uint8_t pressure;
uint8_t humidity;
uint8_t watermark;
uint8_t radiation;
uint8_t leafwetness;
uint8_t temperature;

void loop() {

  SensorAgrv20.setSensorMode(SENS_ON, SENS_AGR_PRESSURE);
  SensorAgrv20.setSensorMode(SENS_ON, SENS_AGR_WATERMARK_1);
  SensorAgrv20.setSensorMode(SENS_ON, SENS_AGR_RADIATION);
  SensorAgrv20.setSensorMode(SENS_ON, SENS_AGR_LEAF_WETNESS);
  SensorAgrv20.setSensorMode(SENS_ON, SENS_AGR_SENSIRION);
 
  delay(10000);
  
  watermark = SensorAgrv20.readValue(SENS_AGR_WATERMARK_1); 
  pressure = SensorAgrv20.readValue(SENS_AGR_PRESSURE);
  radiation = SensorAgrv20.readValue(SENS_AGR_RADIATION);
  // Conversion from voltage into umol·m-2·s-1
  radiation = radiation / 0.0002;
  leafwetness = SensorAgrv20.readValue(SENS_AGR_LEAF_WETNESS);
  temperature = SensorAgrv20.readValue(SENS_AGR_SENSIRION, SENSIRION_TEMP);
  delay(100);	 //the sensor sleeps until the next measurement
  humidity = SensorAgrv20.readValue(SENS_AGR_SENSIRION, SENSIRION_HUM);
  
  // Turn off the sensor
  SensorAgrv20.setSensorMode(SENS_OFF, SENS_AGR_LEAF_WETNESS);
  SensorAgrv20.setSensorMode(SENS_OFF, SENS_AGR_PRESSURE);
  SensorAgrv20.setSensorMode(SENS_OFF, SENS_AGR_HUMIDITY);
  SensorAgrv20.setSensorMode(SENS_OFF, SENS_AGR_WATERMARK_1);
  SensorAgrv20.setSensorMode(SENS_OFF, SENS_AGR_RADIATION);  
  
   ///////////////////////////////////////////
  // 1. Create ASCII frame
  /////////////////////////
  frame.createFrame(ASCII);  
  
  // add frame fields
  frame.addSensor(SENSOR_STR, "AGR:=");
  frame.addSensor(SENSOR_PAR,radiation);
  frame.addSensor(SENSOR_LW,leafwetness);
  frame.addSensor(SENSOR_SOIL,watermark);
  frame.addSensor(SENSOR_TCB,temperature);
  frame.addSensor(SENSOR_HUMB,humidity);
  frame.addSensor(SENSOR_PA,pressure); 
  frame.showFrame(); 

  ///////////////////////////////////////////
  // 2. Send packet
  ///////////////////////////////////////////  

  // send XBee packet
 error = xbee802.send( RX_ADDRESS, frame.buffer, frame.length );   
  
  // check TX flag
  if( error == 0 )
  {
    USB.println(F("send ok"));
    
    // blink green LED
    Utils.blinkGreenLED();
    
  }
  else 
  {
    USB.println(F("send error"));
    
    // blink red LED
    Utils.blinkRedLED();
  }

}

Installing Atomiton Perif Driver

Refer to TQL CLI Protocol Install  section for installation of perif driver. 

Troubleshooting Atomiton Perif Driver

1)  While running the engine if you run into error saying wrong ELF as shown below

It means that the version (32 bit or 64 bit) of jdk and processor architecture are different, change the JDK to match the processor architecture. 

Peripheral Protocol Handler Parameter Description

Parameter NameParameter Description
Interface

The interface is the transport on which the peripherals can be communicated.

The Interface Can be Serial, Xbee, or any medium that can be used to send or receive messages from the devices,
If we are using an onboard peripheral, than peripheral is same as Interface. Example, Raspberry-Pi receiving data from a

temperature sensor connected to the Arduino UNO. In this case Arduino UNO is attached to Pi via Serial USB Interface.

InterfacePort

The device node value. Device nodes are special files that allow applications to interface with device drivers. 

For example, /dev/ttyUSB0 if the Port on which a Peripheral may be connected over USB.

Bauderate

Baudrate. The baud rate is the rate at which information is transferred in a communication channel.

In the serial port context, "9600 baud" means that the serial port is capable of transferring a maximum of 9600 bits per second.

Peripheral

This is Device I/O type on which the device will respond. For example, it can be serial, Xbee, GPIO, ADC, etc.

UniqueId

Every connection that is established with the peripheral should have a unique id value.

This unique id is used to open a new connection to send / receive data from a peripheral.

For example, if you want to send and receive data from same peripheral attached to a board (same TQLEngine) then:

perif//<UniqueId1> Results in one communication channel and perif://<UniqueId2> results in second communication channel.

Here UniqueId1 and UniqueId2 are actual values of UniqueId parameter passed as part of two different actions of a ThingFacet.

Operation

The Operation is the type of communication we need to perform with the type of peripheral. For example:

Peripheral : Serial

Operation: transmit (or) receive

PayloadPayload is the data to be sent as part of the transmit operation.
FormatThe format of data to be transmitted. This can be either ascii or number. Applicable only if Operation is transmit
Command

This is specific to XBee I/O type. The command to be performed in case of a AT XBee device.

Possible values are: atcommand, remoteatcommand, txrequest, arbitrary

Address

This is specific to XBee I/O type. The 64 bit address of destination XBee device.

NWAddressThis is specific to XBee I/O type. The 16 bit address of destination XBee device.
IdentifierThis is specific to XBee I/O type.  A unique identifier for frame to be sent, can be 0.

Acceleration

This is specific to Pololu Servo Motor to control the speed
PositionThis is specific to Pololu Servo Motor - destination at which the Servo Motor must be stopped.

Usage of Peripheral Protocol Handler

  • Serial Read: Performing Read operation over Serial

    Reading data over Serial
    <ThingFacet Name="SerialReadFacet">
    	<String Name="Peripheral"/>
    	<String Name="InterfacePort" />
    	<String Name="Interface"/>
    	<String Name="UniqueId" default="56789"/>
    	<String Name="Baudrate" />
    	<String Name="format" default="ascii"/>
    	<String Name="Operation"/>
    	<String Name="Payload" />
    	<String Name="received" update="auto" KnownBy="xbeeAction" />
    	<AA>[:#o#Event.Argument:]</AA>
    	<Action Name="xbeeAction" Documentation="Start the Zbee Device">
    		<Workflow Limit="1" Live="1" Timeout="-1">
    			<Task name="Main" ><!--while="true"> -->
    				<Event name="Argument" as="ActionArgument" />
    				<Invoke name="InvokeZbee" waitFor="Argument" post="perif://1234">
    					<Message>
    						<Value>
    							<InterfacePort>"[%:[:AA:].InterfacePort.Value:%]"</InterfacePort>
    							<Baudrate>"[%:[:AA:].Baudrate.Value:%]"</Baudrate>
    							<Interface>"[%:[:AA:].Interface.Value:%]"</Interface>
    							<UniqueId>"[%:[:AA:].UniqueId.Value:%]"</UniqueId>
    							<Operation>"[%:[:AA:].Operation.Value:%]"</Operation>
    							<format>"[%:[:AA:].format.Value:%]"</format>
    							<Payload>"[%:[:AA:].Payload.Value:%]"</Payload>
    							<Peripheral>"[%:[:AA:].Peripheral.Value:%]"</Peripheral>
    						</Value>
    					</Message>
    				</Invoke>
    				<Output name="Result" as="ActionResult">
    					<Value>
    						<received>[%:[%:@Output:%]Invoke.InvokeZbee.Message.Value.received:%]</received>
    					</Value>
    				</Output>
    			</Task>
    		</Workflow>
    	</Action>
    </ThingFacet>
  • Serial Write: Performing Write Operation over Serial

    Posting a command (Write) over Serial
    <ThingFacet Name="SerialWriteFacet">
    	<String Name="Peripheral"/>
    	<String Name="InterfacePort" />
    	<String Name="Interface"/>
    	<String Name="UniqueId" default = "12345"/>
    	<String Name="Baudrate" />
    	<String Name="format" default="ascii"/>
    	<String Name="Operation"/>
    	<String Name="Payload" KnownBy="XbeeservoAction"/>
    	<AA>[:#o#Output.Argument:]</AA>
    	<Action Name="XbeeservoAction" Documentation="Start the Servo Motor">
    		<Workflow Limit="1" Live="1" Timeout="-1">
    			<Task name="Main">
    				<Output name="Argument" as="ActionArgument" />
    				<Invoke name="InvokeZbeeServo" scope="local" waitFor="Argument" post="perif://5678">
    					<Message>
    						<Value>
    							<InterfacePort>"[%:[:AA:].InterfacePort.Value:%]"</InterfacePort>
    							<Baudrate>"[%:[:AA:].Baudrate.Value:%]"</Baudrate>
    							<Interface>"[%:[:AA:].Interface.Value:%]"</Interface>
    							<UniqueId>"[%:[:AA:].UniqueId.Value:%]"</UniqueId>
    							<Operation>"[%:[:AA:].Operation.Value:%]"</Operation>
    							<format>"[%:[:AA:].format.Value:%]"</format>
    							<Payload>"[%:[:AA:].Payload.Value:%]"</Payload>
    							<Peripheral>"[%:[:AA:].Peripheral.Value:%]"</Peripheral>
    						</Value>
    					</Message>
    				</Invoke> 
    				<Log Message="***** After Invoke *****" /> 
    				<Output name="Result" as="ActionResult">
    					<Value>
    						<Payload>Hello</Payload>
    					</Value>
    				</Output>
    			</Task>
    		</Workflow>
    	</Action>
    </ThingFacet>
  • Xbee Commands over Serial: Sending Xbee commands over Serial interface.

    Sending AT (Xbee) command over Serial
    <ThingFacet Name="XbeeFacet">
    	<String Name="Interface"/>
    	<String Name="Peripheral"/>
    	<String Name="UniqueId"/>
    	<String Name="Operation"/>
    	<String Name="InterfacePort"/>
    	<String Name="Baudrate" />
    	<String Name="Command" />
    	<String Name="Identifier"/>
    	<String Name="Frame"/>
    	<Number Name="status"/>
    	<String Name="Address"/> 
    	<String Name="NwAddress"/> 
    	<String Name="Payload" KnownBy="XbeeAction"/>
    	<AA>[:#o#Event.Argument:]</AA>
    	<Action Name="XbeeAction" Documentation="Start the Xbee Device">
    		<Workflow Limit="1" Live="1" Timeout="-1">
    			<Task name="Main">
    				<Event name="Argument" as="ActionArgument" />
    				<Invoke name="InvokeXbee" scope="local" waitFor="Argument" post="perif://91011">
    				<Message>
    					<Value>
    						<InterfacePort>"[%:[:AA:].InterfacePort.Value:%]"</InterfacePort>
    						<Baudrate>"[%:[:AA:].Baudrate.Value:%]"</Baudrate>
    						<Interface>"[%:[:AA:].Interface.Value:%]"</Interface>
    						<UniqueId>"[%:[:AA:].UniqueId.Value:%]"</UniqueId>
    						<Operation>"[%:[:AA:].Operation.Value:%]"</Operation>
    						<Command>"[%:[:AA:].Command.Value:%]"</Command>
    						<Payload>"[%:[:AA:].Payload.Value:%]"</Payload>
    						<Peripheral>"[%:[:AA:].Peripheral.Value:%]"</Peripheral>
    						<Identifier>"[%:[:AA:].Identifier.Value:%]"</Identifier>
    						<Address>"[%:[:AA:].Address.Value:%]"</Address>
    						<NwAddress>"[%:[:AA:].NwAddress.Value:%]"</NwAddress>
    						<Frame>"[%:[:AA:].Frame.Value:%]"</Frame>
    					</Value>
    				</Message>
    		</Invoke>
    				<Output name="Result" as="ActionResult">
    					<Value>
    						<Status>[%:[%:@Output:%]Invoke.InvokeXbee.Message.Value.Status:%]</Status>
    						<Payload>[%:[%:@Output:%]Invoke.InvokeXbee.Message.Value.Payload:%]</Payload>
    						<Command>[%:[%:@Output:%]Invoke.InvokeXbee.Message.Value.Command:%]</Command>
    					</Value>
    				</Output>
    			</Task>
    		</Workflow>
    	</Action>
    </ThingFacet>
  • Pololu: Controlling Pololu Servo Motor Device

    Controlling Pololu Servo Motor
    <ThingFacet Name="PololuFacet">
    	<String Name="Interface"/>
    	<String Name="Peripheral"/>
    	<String Name="UniqueId"/>
    	<String Name="Operation"/>
    	<String Name="InterfacePort"/>
    	<String Name="Baudrate" />
    	<String Name="Speed"/> 
    	<String Name="Channel"/>
    	<String Name="Acceleration"/>
    	<String Name="Position" KnownBy="pololuAction"/>	
    	<AA>[:#o#Event.Argument:]</AA>
    	<Action Name="pololuAction" Documentation="Start the pololu Device">
    		<Workflow Limit="1" Live="1" Timeout="-1">
    			<Task name="Main">
    				<Event name="Argument" as="ActionArgument" />
    				<Invoke name="InvokePololu" scope="local" waitFor="Argument" post="perif://151611">
    				<Message>
    					<Value>
    						<InterfacePort>"[%:[:AA:].InterfacePort.Value:%]"</InterfacePort>
    						<Baudrate>"[%:[:AA:].Baudrate.Value:%]"</Baudrate>
    						<Interface>"[%:[:AA:].Interface.Value:%]"</Interface>
    						<UniqueId>"[%:[:AA:].UniqueId.Value:%]"</UniqueId>
    						<Operation>"[%:[:AA:].Operation.Value:%]"</Operation>
    						<Speed>"[%:[:AA:].Speed.Value:%]"</Speed>
    						<Channel>"[%:[:AA:].Channel.Value:%]"</Channel>
    						<Peripheral>"[%:[:AA:].Peripheral.Value:%]"</Peripheral>
    						<Acceleration>"[%:[:AA:].Acceleration.Value:%]"</Acceleration>
    						<Position>"[%:[:AA:].Position.Value:%]"</Position>
    					</Value>
    				</Message>
    		</Invoke>
    				<Output name="Result" as="ActionResult">
    					<Value>
    						<Position>"0"</Position>
    					</Value>
    				</Output>
    			</Task>
    		</Workflow>
    	</Action>
    </ThingFacet>