A control system framework for personal automation.
We’ll now show you how implement bi-directional communication with the node. To illustrate, we’ll create a new service routine on the Arduino that simply adds two numbers together and returns the result.
If our goal is for the Arduino to add two numbers, we need a way of both transporting those numbers to the Arduino, and then returning the result. To do this we’ll create both outbound and inbound packet templates.
The following packet template objects should be added to the virtual node’s initPackets() function:
self.additionRequestPacket = packets.template('additionRequest',
packets.fixedPoint('input1', 6, 10),
packets.fixedPoint('input2', 6, 10)) #5 integer bits + sign, 10 fractional
self.additionResponsePacket = packets.template('additionResponse',
packets.fixedPoint('result', 6, 10))
We’ve also introduced the fixedPoint packet token. This will allow us to send decimal values to the Arduino and have them operated on. The notation of the input parameters is a bit tricky. The “6” is the number of integer bits, including the sign bit. The “10” is the number of fractional bits to the right of the decimal point. We can therefore transmit values that range between approximately 31.999 and -31.999, with a resolution of about 0.001. As an aside, you won’t need to worry about this on the firmware side, because in this example the Arduino will simply add the two numbers without worrying about where the decimal point is located.
We now need to connect a new service routine (which we’ll write next) to a new port number, and to the packet templates above. Add the following function call to the virtual node’s initPorts() function:
self.bindPort(port = 11, outboundFunction = self.sumNumbers, outboundTemplate = self.additionRequestPacket,
inboundTemplate = self.additionResponsePacket)
Let’s now write the virtual node’s service routine function which can be called by the user to add two numbers together. It should:
The following function should be added to the service routine section of the virtual node:
class sumNumbers(core.actionObject):
"""Remotely sums two numbers and returns the result."""
def init(self, value1, value2):
"""Initializes the actionObject.
value1 -- the first number to be added
value2 -- the second number to be added
Returns value1 + value2
"""
self.setPacket(input1 = value1, input2 = value2)
if self.transmitUntilResponse(): #Transmits multiple times if necessary until a reply is received from the node.
result = self.getPacket()['result'] #retrieves the result from the response packet
return result #return the result
else: #didn't receive a response from the node
notice(self.virtualNode, 'got no response to addition request') #generate a notice to the user.
return False
We are now done modifying the virtual node. Let’s move on to write the necessary firmware functionality.
Modify the Gestalt Ports section to include a new entry:
#define LEDControlPort 10
#define sumPort 11
Of course, the new port number must match what we used in the virtual node.
Modify the userPacketRouter() function to include a new case statement:
void userPacketRouter(uint8_t destinationPort){
// This function is responsible for calling the appropriate service routine for a given inbound packet.
// destinationPort -- the port number of the inbound packet
switch(destinationPort){
case LEDControlPort: //a message was sent to the LED port
svcControlLED(); //call the LED control service routine
break;
case sumPort: //a message was sent to the sum port
svcSumNumbers(); //call the number addition service routine
break;
// add additional case statements for new ports here, following the pattern immediately above.
}
Here’s where the action happens! Add the following function to the Service Routine section:
void svcSumNumbers(){
// Sums two numbers and returns the result.
int16_t value1 = (int16_t)((uint16_t)rxBuffer[payloadLocation] +
(((uint16_t)rxBuffer[payloadLocation+1])<<8)); //read in value1 from rxBuffer
int16_t value2 = (int16_t)((uint16_t)rxBuffer[payloadLocation+2] +
(((uint16_t)rxBuffer[payloadLocation+3])<<8)); //read in value2 from rxBuffer
int16_t result = value1 + value2; //sum inputs
txBuffer[payloadLocation] = (uint8_t)(result & 0x00FF); //write out result
txBuffer[payloadLocation+1] = (uint8_t)((result & 0xFF00)>>8);
transmitUnicastPacket(sumPort, 2); //transmit the result as a unicast packet
};
In a terminal, navigate to the directory containing the virtual node. Then run the following commands:
>> python
>>> import arduino_basicNode as arduino
>>> myArduino = arduino.virtualNode()
>>> myArduino.sumNumbers(1.25,-8.72)
You should very quickly receive the following result:
-7.4697265625
While the exact answer is of course -7.47, the error is caused by quantization of both the inputs and the result. This was a lot of work to just sum a couple numbers. But it is kinda cool that we just did some remote math! And now you know how to set up bi-directional communication between a virtual and physical node.
Let’s say you want to use your Arduino for a lot of remote math. Instead of typing the terminal commands each time, you can add them to a python script. Check out the below Python script (also in the example file remoteAddition.py):
import arduino_basicNode as arduino #import the virtual node module, with an easier name
import sys #for getting input arguments
inputValues = sys.argv #contains all of the arguments provided when the script was called.
value1 = float(sys.argv[1]) #first input argument, converted from string to float
value2 = float(sys.argv[2]) #second input argument, converted from string to float
myArduino = arduino.virtualNode()
print myArduino.sumNumbers(value1, value2)
Now, from the terminal, run the python script with a couple values you want to sum:
python remoteAddition.py 10.8 4.8
Should return:
15.599609375