Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Tuesday, 28 April 2015

Host Sniff and Embedded Sniff working together

Sniff is first and format designed to help kids learn programming. However it turns out that it works pretty well for a lot of tasks. In particular it makes writing simple Arduino programs to collect data from hardware really easy. Quite often I've used these to do simple physics experiments, either plotting the collected data on an Arduino TFT screen, or recording it to and SD card for later analysis.

A third option is to simply send it back to the "big" computer for processing there. I'd noticed a few Arduino programs written in C++ that throw data to a companion app on the PC, which does something with it. For example there's a LittleBits demo which uses a sensor to control a pong game. However there's something weird about all of these demos: The Arduino code is written in C++, and then on the host side they tend to use Processing which is Java. The standard Arduino system looks like Processing but the code is in a different language. That's just crazy! While "real" programmers should certainly consider using the appropriate language for different parts of the problem (Sniff itself has components written in C, Objective C, Bash, Yacc and even Sniff itself), its really not going to help someone learning C++/Arduino to jump back and forth with Processing/Java.

Sniff on the other hand runs great on Arduino and Host machines (by which we mean the machine with a proper keyboard and screen that you write Sniff programs on). In fact it already contains all of the pieces we need to write a Sniff program to run on your PC to talk to a Sniff program on an Arduino - this is exactly what sniffterm does when you click on "Terminal" in sniffpad, and its all written in Sniff.

I'm planning to run some computing & science workshops soon where were kids can do some of the experiments that are here on the Sniff website. The budget will run to the cost of Arduino's and some sensors, but probably not screens. Plus connecting screens restricts what other hardware we can use due to physical and electrical conflicts. If we can throw that data over to the Host then we can draw some graphs there. This approach will also mean that the graphing code can be written once, and then reused, while the Arduino side code which collects data can just print out the data

Arduino Side Code

In Sniff one of the very first "embedded science" experiments was plotting a graph of coffee going cold. We drop a DS18d20 into some coffee, measure the temperature and plot a graph. If all is going well then we should get an asymptotic curve - the coffee never quite reaches room temperature. This is such a neat, and simple result that I just keep coming back to it, so lets use it yet again, but this time just print out the data:

make thermometer ds18 device A4
make temperature number

make message string

when start
.say "reset"
..tell thermometer to "start"
..wait 1 secs
..tell thermometer to "read"
..set message to [ timer-0.5 ]
..set message to join message ","
..set message to join message [ temperature ]
..say message

We start by sending a reset message to the Host telling it that this is a new set of data, then simply read the temperature every second. We join the current time with the data sample and print it out. This automatically goes over the serial connection and we're done.

Host Side Code

The interesting bit all takes place on the Host (Windows/Mac/Linux Machine).

make serialPort device
make serialData string

when start
.make index number
.if serialData = ""
..say "Arduino device not specified"
...tell system to "quit"
.say join "Connecting to " serialData
.tell serialPort to "open"
.if not serialData=""
..say serialData
..tell system to "quit"
.broadcast receiveChars

To talk to the Arduino we need to use the serialPort device. This has been around for a while now, as its used internally by sniffterm, but we've not talked about it as this is the first time I've used it in an example program.

The serialPort uses the string serialData to communicate. During initialisation the device sets serialData to be the name of the serial port that the system thinks the Arduino is connected to. This is the one we want, so we check that's set to something valid. If not we quit (you could of course set the port explicitly in your code at this point). We then tell the serial port to "open". This time serialData contains potential error messages, so we check if we've received and error message, and once again quit if we have. The code I use in the final version of my graph plotter is slightly more complex, as it displays these messages in a window, but that's irrelevant to the serialPort device.

If all is good we start the receiveChars script which collects data.

when receiveChars
.make tmpString string
.make index number
..tell serialPort to "getString"
..if not serialData = ""
...say serialData
...if serialData="reset"
....delete all of dataX
....delete all of dataY
....set tmpString to ""
....set index to 1
....repeat until index>length of serialData or letter index of serialData=","
.....set tmpString to join tmpString letter index of serialData
.....change index by 1
....add value of tmpString to dataX
....change index by 1
....set tmpString to ""
....repeat until index>length of serialData
.....set tmpString to join tmpString letter index of serialData
.....change index by 1
....add value of tmpString to dataY
...broadcast drawScreen and wait

This script runs forever, calling "getString", which fills serialData with complete lines from the Arduino (it collects them internally, so the user code never sees partial lines). If its the string "reset" then it deletes all of the data currently on the screen.

Otherwise it goes through the string looking for a comma, indicating the end of the abscissa (X part), and start of the ordinate (Y part).  These get added to two lists of numbers: dataX and dataY respectively. It then calls "drawScreen" which redisplays the graph (I'll not go over that as I've covered it in other posts).

Because Sniff allows scripts to execute at the same time as each other we can run all of this in parallel with code to read from the keyboard:

..tell display to "getEvent"
..if answer="q"
...tell system to "quit"
..if answer="r"
...delete all of dataX
...delete all of dataY
...broadcast drawScreen
..if answer = "y"
...set yScale to 1000000
...repeat length of dataY using index
....if (item index of dataY*yScale)+40>440
.....set yScale to (440-40)/item index of dataY
..if answer = "x"
...set xScale to 1000000
...repeat length of dataX using index
....if (item index of dataX*xScale)+40>600
.....set xScale to (600-40)/item index of dataX
..set answer to ""
..wait 0.1 secs

This code checks if the user has pressed "r","y","x" or "q" and resets the data, autoscales in y and x or quits as appropriate.

And here are the results. In fact I was out of coffee, so I just blew hot air onto the ds18. We can see that its temperature peaks at a little over 30 degrees (each axis tick is 10 units), and 90 seconds later is almost back to normal. Best of all we can clearly see that the graph is curved.

The graph plotting program will be in the Hosted examples of the next Sniff release, along with the Arduino genGraph.sniff code. However the important thing to remember is that this is just one of the things you can do combining Sniff on an Arduino with Sniff on a PC - how about using hardware connected to the Arduino to control Minecraft on a Pi? The point is that the Arduino just has to print data to the serial port, and the PC can pick it up and use it, and both sides are written in the same programming language.

No comments:

Post a Comment