Joining Raspberry Pi and Arduino applications with SIMPL

tclSurrogate Protocol

The previous article introduced the SIMPL concept of surrogates. Surrogates are generic daemon processes that enable SIMPL messages to operate on TCP/IP networks transparently. We saw that through the use of surrogates, the SIMPL application developer need not be concerned with any networking details. In many cases, the code is completely identical for local messaging and wide-area messaging.

The SIMPL toolkit sports several flavors of surrogates. The one we are concerned with in this article goes by the name tclSurrogate [2]. This surrogate was originally developed to allow Tcl/Tk applets to participate in a SIMPL application, hence the name. However, the underlying TCP/IP protocol has much more general applicability than the applet case. Today, the tclSurrogate SIMPL surrogate is most often deployed in conjunction with deeply embedded devices such as the Arduino.

Some background discussion is in order. The general-purpose SIMPL surrogate discussed and demonstrated in the previous article is a full SIMPL module in its own right. When used, it connects to other full SIMPL modules in the normal fashion. The tclSurrogate, on the other hand, envisions the SIMPL module as being composed of two parts: the generic network part and the business logic part.

Together, these two parts present themselves to the rest of the SIMPL application as a single SIMPL module. The two halves communicate with each other via a TCP/IP socket, which connects by default at port 8000. This means that the halves of this SIMPL module need not run on the same network node. The SIMPL module represented by this pair will appear to reside on the node where the generic network part is running. More importantly, the business logic half need not even run on a Linux node. In our case, the business logic half will run as an Arduino sketch. The generic half will run on a Linux node, for example, the Rasp Pi.

The tclSurrogate daemon parent process is run as a background process on the Rasp Pi. If the SIMPL toolkit path is configured according to the first article, it should be simply a matter of typing:

tclSurrogate -n TCLSUR &

In this mode, the default port 8000 is made available for connections. If port 8000 is open and available, then the Arduino sketch can connect via a tclSurrogate child process. If a different port is desired than the default 8000, you can just run tclSurrogate with an additional command-line argument to specify the port:

tclSurrogate -n TCLSUR -p <myPort#> &

You can check that this tclSurrogate process is running by typing

fcshow

A module called TCLSUR should be running.

The next element of this little demo application is the Arduino sketch itself.

Arduino and SIMPL

The tclSurrogate protocol has been realized for the Arduino via a library called (you guessed it) Simpl. It should have been downloaded to the Rasp Pi using the self-installing archive [4] and then moved to the Arduino development host. The Simpl library uses the Ethernet library, which in turn uses the SPI library. Accordingly, all of these libraries are included in any Arduino sketch that uses SIMPL tclSurrogate protocols.

To enable the SIMPL toolkit connectivity, the Arduino needs to be equipped with an Ethernet shield. You'll need to locate and record the MAC address on a label that is usually found on the underside of the shield PCB. This MAC address is needed by the sketch (Ethernet library) that we will be running on the Arduino.

Our demonstration hardware (middle box in Figure 2) consists of an Arduino Uno with an Ethernet shield and a PCB breakout with eight LEDs. The eight LEDs are connected to Arduino Uno I/O ports 2-9 via current-limiting 330-ohm (Ohm) resistors and transistors in a switch configuration. The I/O ports are configured for output in the sketch. Eight LEDs connected to the I/O ports through resistors valued from 100Ohm to 1kOhm on the anode and ground on the cathode will do just as well.

On the networking side, the Arduino is connected to an Ethernet hub. The Rasp Pi is also connected to this hub. A router is connected to this hub and acts a DHCP server. In this little network, the Rasp Pi receives an IP address via DHCP. It will also be necessary to record the value of the IP address given to the Rasp Pi. In our case, it is 10.0.1.32. You can get the IP address by issuing the ifconfig command on a terminal/console command line.

The code in Listing 1 is a SIMPL receiver running on the Arduino Uno. It is called eightLeds.ino. The line numbers have been added to help with the description in the "Listing 1 Annotation" box.

eightLeds.ino Annotation

Lines 1 and 2 are required headers.

Line 3 is the required header for the SIMPL library.

Lines 6-13 define the structure of the message to be received by the SIMPL receiver process running on the Arduino. This is a simple example of tokenized messaging, where the leading data item is some well-defined parameter that defines the action to be taken by the receiver. In this case, it is called BRINKY_RITE.

Line 17 is the start of the setup() function, which is generic to Arduino programs.

Line 19 is the port number that the tclSurrogate is waiting on. The default number for the tclSurrogate daemon is 8000. If that port number is unavailable, a new port number must be assigned on the command line of the tclSurrogate and changed in this Arduino code.

Line 20 is the IP address of the Rasp Pi where the tclSurrogate is running, and line 21 is the IP address of the cloud host that is running tclSurrogate. (More on this later.)

Line 22 is the MAC address given on the Ethernet shield attached to the Arduino. This is unique (at least in theory) and will have to be changed to reflect the specific Ethernet shield used.

Lines 25-27 start the serial monitor that is available with the Arduino IDE. It is very useful for getting feedback from the receiver process.

Lines 30-36 initialize the Ethernet shield.

Lines 39-44 deal with the SIMPL name attach. This must precede any form of SIMPL communications. Note that the fourth argument passed to the function is "ARDUINO." This will be the Arduino's unique SIMPL name required in all forms of SIMPL communications.

Lines 47-57 deal with initializing the I/O used for turning the LEDs on and off.

Line 60 is the start of the loop() function that is also generic to Arduino sketches. This function will be contained within an infinite loop, hence the name.

Lines 62-65 declare needed variables. Note that on line 64, we use a single-character byte for setting the state of the bank of LEDs. Each bit will correspond to On or Off for each LED.

Line 68 is the SIMPL Receive() function. This function waits until a message arrives.

Lines 69-98 deal with the arrival of a message; lines 69-74 deal with an error situation (note the SIMPL name_detach() call, which essentially tells the tclSurrogate child process on the Rasp Pi that it is no longer required), and lines 75-98 deal with the case of a normal message communication.

In lines 78-83, SIMPL replies with a null message to the sending process, thus releasing it to carry on with its business. Recall that the S in SIMPL stands for synchronized message passing.

Lines 86-91 check the value of the incoming message token and takes action on an unknown value. Note that this rather simple example has only one type of message token and one set of corresponding actions.

Lines 94-97 set the LEDs to the On/Off states requested by the eightLeds.py sender program running on the Rasp Pi.

Line 99 is the end of the loop, and we start again back at the top of the loop where Receive() blocks and awaits another incoming message.

Listing 1

Arduino Sketch Description – eightLeds.ino

01 #include <SPI.h>
02 #include <Ethernet.h>
03 #include <Simpl.h>
04
05  // message token definition
06 #define BRINKY_RITE  0
07
08 // structure definition of the received/replied message
09 typedef struct
10     {
11     unsigned short token; // message token indicating what to do
12     unsigned char state; // state of the LEDs; on/off; eight bits-eight LEDs
13     } MSG;
14 .
15  EthernetClient client;
16
17 void setup()
18 {
19 const unsigned port = 8000; // port that the tclSurrogate is waiting on
20 char *serverName = "10.0.1.32"; // Rasp Pi host where tclSurrogate lives
21 // char *serverName = "www.icanprogram.ca";
22 byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x0D, 0x77}; // ethernet shield MAC address
23
24  // start serial messaging
25 Serial.begin(9600);
26 Serial.println();
27 Serial.println("starting up");
28
29 // start ethernet
30 if (!Ethernet.begin(mac))
31     {
32     Serial.println("error on ethernet");
33     while(1);
34     }
35 // give the ethernet shield a chance to initialize
36 delay(1000);
37
38 // remotely SIMPL name attach the name "ARDUINO" on RasPi
39 if (nameAttach(serverName, port, &client, "ARDUINO") == -1)
40     {
41     Serial.println(whatsMyError());
42     client.stop();
43     while(1);
44     }
45
46 // initialize the digital pins as an output and test LEDs
47 for (int ledPin = 2; ledPin <= 9; ledPin++)
48     {
49     pinMode(ledPin, OUTPUT);
50     digitalWrite(ledPin, HIGH);
51     }
52 delay(3000);
53 for (int ledPin = 2; ledPin <= 9; ledPin++)
54     {
55     digitalWrite(ledPin, LOW);
56     }
57 Serial.println("initialization complete");
58 }
59
60 void loop()
61 {
62 long senderId;     // the unique id of the sending program
63 MSG msg;           // incoming/outgoing message variable
64 int ret;                // function return value
65 unsigned char state;    // 8 led state masking variable
66
67 // check for an incoming message
68 ret = Receive(&client, &senderId, &msg, sizeof(MSG));
69 if (ret == -1) // problem
70    {
71    Serial.println(whatsMyError());
72    nameDetach(&client);
73    while(1);
74    }
75 else if (ret > 0) // incoming message
76    {
77    // reply instantly to the remote sender
78    if (Reply(&client, &senderId, NULL, 0) == -1)
79        {
80        Serial.println(whatsMyError());
81        nameDetach(&client);
82        while(1);
83        }
84
85     // check the message token for veracity
86     if (msg.token != BRINKY_RITE)
87        {
88        Serial.println("Incorrect msg token");
89        nameDetach(&client);
90        while(1);
91        }
92
93     // set the blinky lights according to the message state variable
94     for (int ledPin = 2, state = 1; ledPin <= 9; ledPin++, state <<= 1)
95        {
96        digitalWrite(ledPin, msg.state & state);
97        }
98    }
99 }

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content