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.

Wednesday, 6 May 2015

Measuring Lego Motors with Wedo

If you've read through Lego's intro to wedo material (and the related simple mechanical machines) you'll find its an excellent introduction to gearing and ratios. Using a big and a small cog you get taught how to speed up, slow down and reverse the direction of rotation from either a hand crank or a motor.

But that section of the material doesn't actually use the Wedo... perhaps we could use it to put some numbers on those observations! How fast does a lego motor actually turn, and do the gear ratio's do what they're supposed to?

Here's the experimental setup:



The motor (driven by the wedo) drives the propeller blades which pass in front of the distance sensor (motor on connector 2, sensor on 1).  The first thing to do is check that the setup is viable:

make wedo device
make wedoConnector number
make wedoValue number

when start
.set wedoConnector to 2
.set wedoValue to 0
.tell wedo to "setMotorSpeed"
.forever
..set wedoConnector to 1
..forever
...tell wedo to "readDistance"
...say [wedoValue]
...wait 0.5 secs


This just turns the motor off, and prints out the reading from the sensor. Turning the blades slowly by hand shows that the value reported by the sensor does change. While we don't really care about the exact value, we can detect that the blade passes in front of the sensor when the value drops below 0.1.

when start
.set wedoConnector to 2
.set wedoValue to 0.3
.tell wedo to "setMotorSpeed"
.forever
..set wedoConnector to 1
..repeat until wedoValue >0.1
...tell wedo to "readDistance"
..say "ping"
..repeat until wedoValue <0.1
...tell wedo to "readDistance"
..say "pong"

Now we can check to see if there's a blade present - wait until there isn't (print ping), wait until there isn't (print pong). As the motor turns we're not tracking its rotation.

when start
.set wedoConnector to 2
.set wedoValue to 0.3
.tell wedo to "setMotorSpeed"
.forever
..reset timer
..set wedoConnector to 1
..repeat until wedoValue >0.1
...tell wedo to "readDistance"
..repeat until wedoValue <0.1
...tell wedo to "readDistance"
..say [1/(timer*2)]



Now we just measure the time it takes us to go around the loop. As there are two blades to the propeller, each iteration represents half a turn. A full turn takes twice that. We could print that out as time per revolution, but if we take the reciprocal that gives us revolutions per second.

For my motor (regular Wedo M-size) 0.3 is about as slow as it can go (in fact it takes a nudge to get started at this speed). We can measure speed for a few power settings:



0.3 Power = 0.85rps
0.5 Power = 1.55rps
0.75 Power =2.5rps
1(full) power=3.5rps



Theres a bit of variation, as the wedo probably only updates the reported value on a periodic basis, but the results are fairly consistent and representative.

Now lets mod the setup, and add an extra gear. This makes the fan turn faster (according to the lego introduction to cogs!)

0.3 Power = 2.5
0.5 Power = 4.6
0.75 Power = 7rps
1(full) power=10.4rps

In each case the new speed is almost exactly three times the previous one... The exception to that is the 0.75 which is a little low... However looking at the raw data for this particular measurement, it seemed to present the most variation 6.94 was the most common reading, but it was also the lowest, with occasional readings of 7.6 and 7.9 - no other reading had any where near such a wide variation, so it looks like this speed doesn't mesh well with the underlying sampling rate - lets just put it down to experimental error.


Now the moment of truth... Lets count the number of teeth on the cogs: The small one has 8, while the big one has 24... a factor of THREE!!!! One turn of the big cog turns the little cog three times, so the blade spins three times faster.
Awesome!


Tuesday, 5 May 2015

Lego Wedo Alligator

The Lego Wedo is awesome! It comes with a great set lesson plans, with lots of great projects, and ideas for how you can use the items you build as part of larger topics. However they're firmly aimed at KS2, which is a great shame as everyone loves Logo and having bought expensive hardware, far too many schools use it for a few sessions in year 4, then put it back in the cupboard.

The Wedo software is nice but limited and expensive, and its probably the main reason that Lego haven't made the Wedo available outside of primary education. Fortunately the Wedo works with Scratch and Sniff. This means there's lots of opportunity for frameworking and progression: we can revisit familiar (and fun) projects which have been completed in the Wedo Software, and rebuild them in Scratch and subsequently Sniff.

My favourite official project from the Lego set is the Hungry Alligator. Building instructions are built into the Wedo software, but you can download regular instructions here: https://education.lego.com/en-us/lesi/support/product-support/wedo/wedo-base-set-9580/building-instructions

Now lets code this in Sniff:

make wedo device
make wedoConnector number
make wedoValue number

when start
.set wedoConnector to 1
.
.forever
..set wedoValue to -1
..tell wedo to "setMotorSpeed"
..
..wait 0.4 secs
..
..set wedoValue to 1
..tell wedo to "setMotorSpeed"
..
..wait 0.35 secs

We start by making a wedo hub device. You can connect any number of hubs (more or less!), but we're just going to have one. Each hub as two connectors, and we've plugged the motor into connector 1 (the left 1). We set the wedoConnector and wedoValue, and the tell the wedo to "setMotorSpeed". -1 closes the mouth, while +1 opens it (that's just the way this model is built/geared), so this code closes the mouth, then opens it, repeating forever. We close for slightly longer than we open to ensure that the jaws fully close - the drive bands will slip when we get that far.

Having got the jaws snapping, we can move that into a separate script.

when snap
.set wedoConnector to 1
.repeat 3
..set wedoValue to -1
..tell wedo to "setMotorSpeed"
..wait 0.4 secs
..set wedoValue to 1
..tell wedo to "setMotorSpeed"
..wait 0.35 secs
.set wedoValue to 0
.tell wedo to "setMotorSpeed"

when start
.forever
..set wedoConnector to 2
..tell wedo to "readDistance"
..repeat until wedoValue <0.07
...tell wedo to "readDistance"
..broadcast snap and wait
..wait 0.2 secs

Then we use the distance sensor to wait until there's something to eat! The sensor is on connector 2, and we readDistance until it reads less than 8cm, at which point we fire off the snap script. You may need to tweak the timings, and range value to get everything working smoothly (so the jaws don't trigger another Snap)


It's impossible not to love this guy! Just leave him sitting on your desk, and feed him occasionally! If you've got some Wedo kicking around from working with younger year groups, then get them out, and get excited about building and programming!

Release 17: Hungry Alligator

Release 17 contains a lot of minor improvements to the compiler and runtime that should make it nicer to use. Programs now quit once all scripts have completed, and the compiler will now warn you if your program contains scripts and/or broadcasts that are never used (typically because you've miss-spelled one of the script names).

The big headline feature is that Wedo is now supported on all host platforms: Windows, Linux and Mac. This has been a priority for some time, but the code needed to support it was complex. Now its in place everything should just work. Wedo was the last Unix only feature, and now windows and Unix platforms are basically equivalently supported.

There are of course the usual round of bug fixes, and examples, including all the numerical code from the last few blog posts, and the multi shield Arduino code.

Download  R17 for Windows
Download R17 for all other Platforms

update: There was a minor, but quite obvious bug in the Unix release, which has been updated.

Friday, 1 May 2015

Raspberry Pi Desktop Shortcut for Sniff

Last year we put a lot of effort into getting the core of Sniff right. It's now a solid programming system, with a rich set of libraries, running on a wide range of platforms. We're not working with teachers to iron out the wrinkles and make it easy to use for non-techies.

Sniff is essentially command line based, with a  compile command which generates an executable. You can use it with any IDE, or simply a text editor. To make things easier for new users we wrote SniffPad - a simple IDE for Sniff, written in Sniff. However as that's a Sniff program you generally need to run it from the command line.

To make it even easer, on a Raspberry Pi or other Linux system you can make a desktop shortcut to Sniffpad, so users can just click the icon, and just straight into writing Sniff code.

The first thing to setup is the shortcut itself. Create a file called Sniff.desktop in your Desktop folder, containing the following text:

[Desktop Entry]
Name=SniffPad
Comment=Setup Sniff, and run Sniffpad
Icon=/home/ian/Sniff/utils/PiShortcut/sniff.bmp
Exec=/home/ian/Sniff/utils/PiShortcut/runSniffpad
Type=Application
Encoding=UTF-8
Terminal=false
Categories=None;


You'll need to edit the Icon and Exec lines to point wherever you've installed Sniff. In this case I've unpacked the Sniff distribution into /home/ian/Sniff. In future releases that's all you'll need to do, but if you want to try this out right now, then you need to go to the Snif/utils directory and make a folder called PiShortcut. Inside there put an image file sniff.bmp. 

Finally we need a script which is actually going to run. We can't link directly to SniffPad, as we first need to set up the Sniff environment. Place the following text in a file called runSniffpad inside the PiShortcut folder, and make sure its executable (chmod 755 runSniffpad).

DIR=`dirname $0`

cd $DIR/../..
. ./setup

mkdir -p $HOME/SniffProjects
cd $HOME/SniffProjects
sniffpad


This finds the Sniff folder based on where the runSniffPad command is, and runs setup. It then cd's to a folder called SniffProjects, where you can save your work.

Now just double click and you're good to go!

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"
.forever
..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
.forever
..tell serialPort to "getString"
..if not serialData = ""
...say serialData
...if serialData="reset"
....delete all of dataX
....delete all of dataY
...else
....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:

.forever
..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.






Tuesday, 14 April 2015

A simple guessing game (binary search!)

Binary search is a pretty neat (and tricky) concept. We have a sorted list of numbers, and we want to find out if it contains a particular number. We start by checking the middle number of the list (the median!). If the number we're looking for is higher then its in the second half of the list. If its lower then its in the first half of the list (if at all). Because it halfs the size of the list each time, it very quickly find the number even for large lists.

However its harder to implement than it sounds. This is famously evidenced by the first paper on the subject which demonstrated the algorithm in 1946. The first correct demonstration was in 1962. For 16 years this approach was known, documented and used, yet never once implemented correctly! There's more details on what can go wrong here.

The reason this crossed my mind was when Alex challenged me to guess his favourite number. Having established that it was between 1 and 100, we then proceeded to play higher or lower until I established it was in fact 95, which took me 7 guesses. It took me 7 guesses because I used a variant of binary search. The algorithm isn't exactly the same, but it uses the same divide and conquer approach. I realised that coding up this guessing game would be a neat introduction to these kinds of problem. You could get kids to just play the game, and see you long it takes them to develop an optimal strategy, then get them to write down how they would do it, before finally coding it.

Cutting to the chase... here's the code in Sniff.

make highGuess number
make lowGuess number
make guess number

when start
.set highGuess   to 100
.set lowGuess to 0
.say "think of a number from 1 to 100"
.repeat until letter 1 of answer = "y"
..set guess to round ((highGuess  +lowGuess )/2)
..ask join "is it " [guess] and wait
..if not letter 1 of answer="y"
...ask "is your number higher or lower"  and wait
..if letter 1 of answer="h"
...set lowGuess  to guess
..if letter 1 of answer="l"
...set highGuess to guess


We initially know that the the number is greater than 0 and less than or equal to 100. We then avererage our upper and lower limit, and round to the nearest whole number to produce our guess. Note that because we round up values ending in 0.5, if we've narrowed the range down to a gap of two (0...1, 99...100 or n...n+1) then the next number will be the upper of the two numbers. For that reason we need the range to be 0...100, rather than 1...100.

we then ask if our guess is correct (if it is then we're done!), and if not then if the secret number is higher or lower. We reduce our range based on the answer (strictly we should reduce highGuess to guess-1, but its not essential for the game - these are exactly the sort of things that make binary search tricky though).

The code will win the game within 7 guesses at most! Other strategies might get lucky (maybe I could guess that Lightning McQueen might have influenced Alex's choice of number), but in the long haul this is an optimal approach.

Having coded it to run on-screen, I figured it would be fun to run it on something more portable. The Arduino Multishield has been a handy toy the last few weeks: it has a numeric display, and three buttons so seemed ideal!

make highGuess number
make lowGuess number
make guess number

when start
.set buzzer to high
.
.forever
..set highGuess   to 100
..set lowGuess to 0
..repeat until not button2
...set guess to round ((highGuess  +lowGuess )/2)
...set message to [guess]
...if not button3
....set lowGuess  to guess
....wait 0.1 secs
....wait until button3
...if not button1
....set highGuess   to guess
....wait 0.1 secs
....wait until button1

Now we run forever, with button1 meaning lower, button3 meaning higher, and button2 for spot on (which resets the device).

The great thing of course is that though it takes 7 guesses to handle numbers up to 100, it only takes another 3 to handle numbers up to 100. How many guesses to handle numbers up to 1million?

From here you could easily go on more advanced divide and conquer algorithms, from binary search all the way through to quicksort, and FFT's.

Wednesday, 8 April 2015

Arduino breathalizer (with IR remote!)

Ages ago I picked up one of these MQ3 alcohol sensors. They're part of a whole series of similar sensors which can detect the levels of various gases. Most of them are a few pounds so if you have an application where you might be worried about specific chemicals then you can pick an appropriate one. The exception to this is the Carbon Dioxide sensor which is hard to get and expensive, which is unfortunate as it would actually be interesting so see how levels change in rooms as they fill up with people. As I just wanted to play around with it, I picked the alcohol vapour sensor - I figured alcohol  was something I had easy access to that I could use safely.

Though you can get them separately, its easiest to buy the sensor already mounted on a board, like this one.  These generally have four pins. Two are for gnd/power, and the other two are outputs. Of those one is digital, in that it either turns on or off when the gas level reaches a certain threshold, setable by a small pot on the back. If you just want an alarm, you can hook this straight to a piezo buzzer, and you're good to go.

We're more interested in the other output - the analog output which produces a voltage dependant on the gas level. This will connect straight in to an Arduino, and as I just got a multifunction shield, I though I'd use that.

We'll keep all of the display code from the last two posts, and just write the new code:

make alcohol analog input A5

make min number

when start
.set buzzer to high #or it will make noise!
.forever
..set message to [alcohol*100]
..wait 0.1 secs


Easy! we just read the value (which will have a value between 0 and 1) and scale by 100.

But what does that value mean?

Others have conducted this experiment and failed to accurately calibrate these sensors. This is in part due to them forgetting to write down the results during the more intensive parts of the test regime, but there's general consensus that its hard to get any specific numbers.

In my tests the reading generally started around 20 when I turned it on, but as the sensor warmed up (its got a heating element in it), the value dropped to around 3. Putting it near the top of a bottle of vodka instantly spiked the value to around 20 again, but it dropped back again quickly when moved. Drinking a tiny amount (in the name of SCIENCE!) so there would be alcohol on my breath, produced similar results (15-20), but the level on my breath dropped back down again fairly quicky, as the vapour dissipated from my breath.

A more extended test involving half a bottle of red wine (no one said this was going to be easy) produced a reading of around 15 some time after I'd stopped drinking. As there was alcohol in my bloodstream, the level on my breath was fairly constant.

Putting these together I came up with a fairly simple ad-hoc approach which should detect the basics - record the minimum reading the sensor has ever returned as a baseline. If you blow double that you've been drinking but are probably quite sober. If you blow 4 or 5 times the minimum you're probably having a good time. If you blow 8 times the minimum, then you're wasted!

make min number

when start
.set buzzer to high #or it will make noise!
.set min to 1
.forever
..set message to [alcohol*100]
..if alcohol < min
...set min to alcohol
..set buzzer to not alcohol > 4*min
..wait 0.1 secs

That's pretty easy to code. Each time around we just check against the minimum, and update the minimum if the new version is lower. We check if the reading is higher than 4 times the minimum and sound the buzzer if it is. You can adjust the threshold for this depending on if you want to scare your kids, or set a frat house drinking challenge, but 4 seemed a fair value to me to call someone drunk and set of the alarm.

It's important to remember that this calibration is pretty arbitrary, and even if it was correct it only applies to the sensor I bought, not the one you have - under no circumstances you should you think you're safe to drive (or do pretty much anything) because some circuit you found online says so!

So given this is just for fun, lets have some fun with it!



make min number
make fakeResults boolean


when start
.set fakeResults to no
.set buzzer to high #or it will make noise!
.set min to 1
.forever
..if alcohol < min
...set min to alcohol
..
..if fakeResults
...set message to [alcohol*5*100]
...set buzzer to off
..else
...set message to [alcohol*100]
...set buzzer to not alcohol > 4*min
..wait 0.1 secs

Here I've added a fakeResults flag. When fakeResults is true it sets the reading to 5 times what it should be, and sounds the alarm (remember the buzzer is logic low). In regular mode it works as before.

So how do we control the mode? Well we could just use a button, but it might be a giveaway that you're cheating. The multifunction shield has a socket for an IR receiver. If you get a tsop4838 (about $1 on eBay) then it will plug straight in to the left three empty sockets. To use it all we need to do is make a receiveIR device on pin 2. Then tell it to "read". It sets a variable keyPressed to a different number for each key (just write a program to print out keyPressed to find the appropriate values for any remote).



make irSensor receiveIR device 2
make keyPressed number

when start
.forever
..tell irSensor to "read"
..if keyPressed=18615
...set fakeResults to not fakeResults
..wait 0.1 secs

Then we check if a key is pressed and use it to toggle fakeMode.