A web-based alternative to Scratch

Coming Up!

Next, you need to create a server on your Pi to field your block's query. As usual, Python to the rescue! Python is great for this task because, apart from having a comprehensive module for dealing with the Pi's GPIO pins, it also has a simple web server module that has everything you need to grab and answer petitions coming from Snap!.

Take a look at Listing 3. Note that this is only a fragment of the whole RPiGPIO.py server program. You can find the complete program online [5], but what is shown here (and in Listing 4) is the skeleton of what you need to set up a web server to listen on port 8280 on your Pi.

Listing 4

RPiGPIO.py – Request Handler

01 .
02 .
03 .
04 class CORSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
05   def send_head(self):
06     path = self.path
07
08     self.pin=0
09     self.state=False
10
11     GPIO.setmode(GPIO.BCM)
12
13     ospath = os.path.abspath('')
14
15     regex = re.compile(".*pin=([0-9]*).*state=(LOW|HIGH)")
16     m = regex.match(path)
17
18     if 'pinwrite' in path: # write HIGH or LOW to pin
19
20       self.pin = int(m.group(1))
21       self.state = True
22       if m.group(2) == 'LOW':
23         self.state = False
24
25       GPIO.setup(self.pin, GPIO.OUT)
26       GPIO.output(self.pin, self.state)
27 .
28 .
29 .

Listing 3

RPiGPIO.py – Server Setup

01 #!/usr/bin/python
02
03 import SimpleHTTPServer
04 import SocketServer
05 .
06 .
07 .
08 if __name__ == "__main__":
09   PORT = 8280 #R+P in ASCII Decimal
10   Handler = CORSHTTPRequestHandler
11
12   httpd = SocketServer.TCPServer(("", PORT), Handler)
13
14   print "serving at port", PORT
15   print "Go ahead and launch Snap!"
16   httpd.serve_forever()

In your command (Figure 12), note how the second block runs a call to your server. Suppose you want to send a HIGH signal to pin 17 on your Pi, then the final string sent to the RPiGPIO.py server will look like:

http://192.168.1.114:8280/pinwrite?pin=17&state=LOW

Now you need to write into your server the bit that is missing from Listing 3 – namely, a request handler that breaks up the requests that Snap! sends and then executes anything that needs doing on the hardware. Listing 4, another piece of the whole program [5], does exactly that.

You use regular expressions to break up the string you saw earlier and extract the pin you want to change (lines 15 and 16) and the state you want to change it to (21 and 23). You then use the regular GPIO calls from the RPI.GPIO module to set the pins and send out the signal.

You can check how this works by setting up a circuit like the one shown in Figure 15 (note the 10K resistor – important later). Download the code [5], copy the RPiGPIO.py server over to your Pi, and run it with:

$ sudo python RPiGPIO.py
Figure 15: Use this circuit to test your scripts and the server.

Next, import RPiGPIO.xml into Snap!. You will find the block you just created under Looks. Drag it onto the workspace, right-click on it, and choose edit from the pop-up dialog. Change the IP address (currently, 192.168.1.114) to whatever your Rasp Pi's IP is on your network and then click Apply and OK.

Choose 17 for the pin value, and HIGH for the state value. Click the block and watch the LED light up.

Buttoned Up

In the last example, I'll show how to implement a block that not only sends information over to the Pi, but also gets a response back. This time it will be a Predicate block that, given a pin number and a state, will return true or false, whether or not the pin number is in said state (Figure 16). In the hardware setup shown in Figure 15, you can use this block to find out whether or not the button is pressed.

Figure 16: This Predicate block tells you whether a given pin is in a certain state.

The block description shown in Figure 17, and in XML form in Listing 5, is really not much more complicated than that of the two previous blocks. By embedding the HTTP call (which contains the GET method) within a predicate block, you tell Snap! to expect an answer back from the server. In this case, the answer will be either true or false.

Listing 5

pinread.xml

01 <blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1">
02   <block-definition s="pin %'pin' is %'state'" type="predicate" category="operators">
03     <header/>
04     <code/>
05     <inputs><input type="%n" readonly="true"><options>0
06 1
07 4
08 7
09 8
10 9
11 10
12 11
13 14
14 15
15 17
16 18
17 21
18 22
19 23
20 24
21 25</options></input><input type="%txt" readonly="true">LOW<options>LOW
22 HIGH</options></input></inputs>
23     <script>
24       <block s="doIfElse">
25         <block s="reportEquals">
26           <block s="reportURL">
27             <block s="reportJoinWords">
28               <list>
29                 <l>192.168.1.114:8280/pinread?pin=</l>
30                 <block var="pin"/>
31                 <l>&state=</l>
32                 <block var="state"/>
33               </list>
34             </block>
35           </block>
36           <l>true</l>
37         </block>
38         <script>
39           <block s="doReport">
40             <block s="reportTrue"/>
41           </block>
42         </script>
43         <script>
44           <block s="doReport">
45             <block s="reportFalse"/>
46           </block>
47         </script>
48       </block>
49     </script>
50   </block-definition>
51 </blocks>
Figure 17: The block description of the pin-reading block.

On the server side, the request handler portion that fields the request looks like what you see in Listing 6. Again, it starts off by breaking up the GET data with the regular expressions seen in Listing 4 (lines  6 to  9).

Listing 6

RPiGPIO.py – Request Handler Part I

01 .
02 .
03 .
04     elif 'pinread'in path: # Read state of pin.
05
06         self.pin = int(m.group(1))
07         self.state = True
08         if m.group(2) == 'LOW':
09             self.state = False
10
11         f = open(ospath + '/return', 'w+')
12
13         GPIO.setup(self.pin, GPIO.IN)
14         if(GPIO.input(self.pin) == self.state):
15           f.write(str(True))
16         else:
17           f.write(str(False))
18
19         f.close()
20         f = open(ospath + '/return', 'rb')
21         ctype = self.guess_type(ospath + '/rpireturn')
22         self.send_response(200)
23         self.send_header("Content-type", ctype)
24         fs = os.fstat(f.fileno())
25         self.send_header("Content-Length", str(fs[6]))
26         self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
27         self.send_header("Access-Control-Allow-Origin", "*")
28         self.end_headers()
29         return
30 .
31 .
32 .

It then creates a file (filehandle f on line 11), reads the GPIO pin (lines 13 to 17), and writes True or False into the file. Then, it sends all the correct headers back to Snap! and finally returns the contents of f (i.e., True or False).

If you followed everything in this article up to this point, you have imported RPiGPIO.xml into Snap!, and you are running RPiGPIO.py on your Pi, check it out!

You will find this new block in Operators. Drag it onto the workspace and, using the circuit shown in Figure 15, poll pin 24 to see if it is HIGH. Hold down the button on your breadboard and click the block. A speech bubble with the word True should pop up. If you let go of the button and then click the block, False will pop up.

Buy this article as PDF

Express-Checkout as PDF

Pages: 9

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