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
Date | Location | Version |
---|---|---|
7/17/2020 | 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. A 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.
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)
#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
// 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 Name | Parameter 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, 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 |
Payload | Payload is the data to be sent as part of the transmit operation. |
Format | The 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. |
NWAddress | This is specific to XBee I/O type. The 16 bit address of destination XBee device. |
Identifier | This 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 |
Position | This 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>