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.

Friday, 24 March 2017

Robot arm upgrades


One of the most popular posts on the Sniff blog is my documentation of the robot arm build - lots of people get the kits on eBay or aliExpress and then they arrive without any instructions. You literally get a bag of black metal shapes and some screws!





When I built the original I was careful to document everything I did, as even searching online there were no good instructions. However my research did reveal that there was a "better" kit out there, that had previously been sold, but that the kits currently being sold contained 1 less set of brackets/servo holders. As I noted in the original post, all of the weight and stress from the robot is carried by the base servo, which really isn't great.

It's fairly easy to find the brackets sold separately, so I bought an extra bracket, and finally got round to rebuilding the base of the arm. While the base servo is still taking a lot of the weight, its now supported by the bracket, spreading the stress around, and making the whole thing a lot more stable.


Here are some pics of the new base during assembly:




You can see that the base servo is removed from its original bracket, and placed in a new one, which is bolted to the old one... while this may seem redundant, it means there's now clearance underneath to attach a u-bracket around the whole servo.

The bracket for the next servo is connected essentially as before, but now the bolts connect the bracket to both the servo horn and the new u-bracket. That means when the arm's weight starts to tip itself over, that sideways force is transmitted (at least in part) to the bearing at the bottom, rather than being fully carried by the neck of the servo.

The result is much more stable. The extra metal work does place some restrictions on range of travel, but only in positions where the gripper would probably be crashing into the table anyway, So generally its looks like a good upgrade.

Wednesday, 22 March 2017

Putting the bars in barcodes

For some reason I was thinking about barcodes... I'm not sure why, but I started wondering if we could make them in Sniff... turns out you can!

A traditional barcode that you find on a tin of baked beans (technically a UPC-A barcode) represents 12 digits. This first 6 are the manufacturers code, assigned to a company (which applies, and gets given its own number), and the last 5 which represent a specific item, as chosen by the manufacturer:

make manufacturerID string "097855"
make itemID string "08839"

make barcodeValue string


But wait a minute - that's only 11. The final digit is a checksum. When the barcode is printed the final digit is calculated from the other 11, and added to the end. When its read the same sum is calculated by the reader, and if the final digit doesn't match it knows something's gone wrong and it can reject the code.


when start

.if not length of manufacturerID = 6
..say "Bad Manufacturer ID"
..stop all
.if not length of itemID = 5
..say "Bad Item ID"
..stop all
.set barcodeValue to join manufacturerID itemID
.broadcast makeChecksum and wait
.say barcodeValue

Here's the beginnings of the main script. We check that the manufacturer and item ID's are the correct length and join them together to make the beginnings of the barcodeValue - the number that is printed along the bottom. You'll note that they're all strings, as the barcode isn't really a number - its more a sequence of digits, and its easier to handle them as strings.

when makeChecksum
.make counter number
.make sum number
.set sum to 0
.
.set counter to 1
.repeat 6
..change sum by value of letter counter of barcodeValue
..change counter by 2
.set sum to sum*3
.
.set counter to 2
.repeat 5
..change sum by value of letter counter of barcodeValue
..change counter by 2
.
.repeat until sum < 11
..change sum by -10
.set sum to 10-sum
.set barcodeValue to join barcodeValue [sum]

To calculate the checksum, we add up the odd placed digits and multiply by 3. Then we add the even placed digits. Once we have the sum we need to find the number to make the sum add up to a multiple of 10. i.e. if the sum is 74 then the final answer is 6, which would make it up to 80. There are a few ways of doing that, but the simplest is to keep subtracting 10 until its less that 11 (i.e. its now 1...10), then subtract it from 10. This is the checksum value and we tag it on the end of the barcodeValue.

Using our example code, the checksum is a 0, so we get:

097855088390

If you look closely at a bar code you'll notice that it has lines and spaces of varying thickness. In fact both the lines and spaces can be between 1 and 4 units wide. Each digit is represented by two lines and two spaces. To store that information we make a list called symbols:


make symbols list of strings

when initSymbols
.add "3211" to symbols
.add "2221" to symbols
.add "2122" to symbols
.add "1411" to symbols
.add "1132" to symbols
.add "1231" to symbols
.add "1114" to symbols
.add "1312" to symbols
.add "1213" to symbols
.add "3112" to symbols

These represent the digits 0 to 9.


make barcodeWidths string



when makeBarWidths

.make counter number
.make index number
.make symbol string
.
.
.set barcodeWidths to ""
.set barcodeWidths to "111 "
.
.set counter to 1
.repeat 6
..set index to value of letter counter of barcodeValue
..change index by 1
..set symbol to item index of symbols
..set barcodeWidths to join barcodeWidths symbol
..set barcodeWidths to join barcodeWidths " "
..change counter by 1
.
.set barcodeWidths to join barcodeWidths "11111 "
.
.repeat 6
..set index to value of letter counter of barcodeValue
..change index by 1
..set symbol to item index of symbols
..set barcodeWidths to join barcodeWidths symbol
..set barcodeWidths to join barcodeWidths " "
..change counter by 1
.
.set barcodeWidths to join barcodeWidths "111 "

A barcode begins with two lines 1 unit thick, with a 1 unit space between them, which is represented as 111. Then we add the symbols for the first 6 digits, so if the first digit was a 9 then we'd add 3112, meaning a 3 unit line, a 1 unit space, a 1 unit line and a 2 unit space.

In the middle of every barcode is the code 11111, representing 3 thin lines. We then continue on to the end of the barcodeValue, and then tag a "111" end sequence on.

111 3211 3112 1312 1213 1231 1231 11111 3211 1213 1213 1411 3112 3211 111 

In the case of our test code, that gives the above string. You'll note that program adds some extra spaces to separate each digit. These won't appear in the final barcode, but they're handy for checking that everything is working.

The next step is to turn those widths in to actually bars.


when makeBars

.make counter number
.make width number
.make isLine  boolean
.set isLine to yes
.set barcode to ""
.repeat length of barcodeWidths using counter
..if not letter counter of barcodeWidths = " "
...set width to value of letter counter of barcodeWidths
...repeat width
....if isLine
.....set barcode  to join barcode "|"
....else
.....set barcode  to join barcode   " "
...set isLine to not isLine

We simply loop over the length of the barcodeWidths string we just made and get the value of the digit. If its a line we print a vertical bar, otherwise we print a space. The number of lines or bars we print is the width of the line.



| |   || |   | || ||| || || ||| ||   | ||   | | | |||  | |  |   |  |   |    | ||| |  |||  | | |

And there it is - our barcode! It's not exactly pretty and its hard to read, because a thick lines appear as multiple vertical bars, when they should be solid, but trust me its the right answer...

make nativeFile device
make fileData string

make image bitmap device
make displayColor number
make displayX number
make displayY number


when drawBarcode
.set displayY to 40
.set displayX to length of barcode
.tell image to "new"
.repeat length of barcode using displayX
..if letter displayX of barcode="|"
...set displayColor to 000
..else
...set displayColor to 777
..repeat 40 using displayY
...tell image to "set pixel"
.set fileData to "barcode.bmp"
.tell image to "save"

Of course what we really should do is draw our barcode as a bitmap and save it to disk. Fortunately that turns out it be really easy - just go through the barcode and if its a "|" draw a line of black pixels. If its a space draw a line of white pixels. Once we're done, we save it to disk.

[tech note: currently on Windows, the Bitmap device requires that you also make a Window device - otherwise it gets confused that you want to make an image, without a way to ever display it! There's a fix in the next release]



And here it is!!! A perfect barcode, which looks just like the one on the box I took the original number from. Now all that's left to do is print out a bunch of these on sticky labels and go cause havoc in the school library! (actually books use a slightly different format!).

What's really nice about this project is that it would make a really great group project. It breaks down really neatly into 4 parts of similar difficulty, which all need to work to make the whole thing happen. On pair of students is given the ID's and has to make the barcode value with checksum. The second has to turn that into line widths, the next turn that into ascii bars, and the final pair draws the bars. As we have a worked example you can provide each pair with an example of the input they need to read, and the output they need to generate, so they can all work at the same time, and then when they know each part is working, they can bring them all together!

Tuesday, 21 March 2017

Speak and Spell and Sniff

Everyone loves spelling tests right? No? Well at least parents and teachers love spelling tests? They must do - they always want to do them with their kids! If kids hate taking them, and adults hate setting them, that would make no sense!

What if we wrote a program to test your spellings? Well at least kids could practise, and us adults could do something more fun. No reason for us all to be bored!

Of course the problem is that you can't just print "spell pointless:" on the screen, as it pretty much gives the game away - hence the traditional spelling test where an adult reads out the list of words. Fortunately that's really easy in Sniff using the "speech" device (which should work "out of the box" on Mac and Windows. On Linux you need to install a speech synthesiser).

make words list of strings

make voice speech device
make message string

when loadWords
.delete all of words
.add "soldier" to words
.add "necessary" to words
.add "legend" to words
.add "northern" to words


We're going to need the speech device (which we're calling voice), and a list of words. Just to get started I've added a few good spelling word to the list.


make counter number
when start
.broadcast loadWords and wait

.set message to join "There are " join [length of words] " words"
.tell voice to "speak"
.wait 3 secs
.


To get us started, we just run the loadWords script, and to check its working we say how many words are in the test.

We could go through the list in order and just read them out, but to make it more fun(!) we'll mix them up.


.
.repeat until length of words=0
..set counter to pick random 1 to length of words
..set message to item counter of words
..tell voice to "speak"
..wait 1 secs
..ask "How do you spell that?" and wait
..if answer=message
...set message to "Correct"
...delete item counter of words
..else
...set message to "Sorry, Incorrect"
..tell voice to "speak"
..wait 3 secs

.
.stop all

This is more for practising than a proper test, so we pick a random word from the list, and say it. The user then has to type it, and if its correct we delete it from the list. If its incorrect we leave it in the list, so that you have to spell every word correctly to finish the program.

The program just loops until there are no more words to spell. Alternatively for a regular spelling test, we'd delete the word even if it was wrong, and keep a score (left as an exercise for the reader).

This all works pretty well, but its not ideal (depending on your love of spelling tests!) that it only knows four words, and that you have to edit the program to change them so instead we can load the words from a file:


make nativeFile device
make fileData string
make fileOK boolean
when loadFile
.delete all of words
.set fileData to "spellings.txt"
.tell nativeFile to "start read"
.repeat until not fileOK
..tell nativeFile to "read string"
..if fileOK
...add fileData to words
.tell nativeFile to "end read"

This just copies a list of words from the file spellings.txt. A quick search turned up a list of KS2 (age 7-11) spelling words and dropped them in the file. Unfortunately the program now starts by telling the there are 208 words in the test, which takes rather a long time to complete! To fix this I added a script to delete most of the words and leave a sensible number:


when deleteWords
.repeat until length of words=10
..delete item pick random 1 to length of words of words

This works, but because of the way random numbers work generates the same list off words every time. To get round that we need to start throwing away random numbers, so we get something closer to real randomness.

when start
.make dummy number
.forever
..set dummy to pick random 1 to 10

With this script in place, the beginning of our main script just needs to be tweaked:


when start
.broadcast loadFile and wait
.ask "Press Return to start" and wait
.broadcast deleteWords and wait
.set message to join "There are " join [length of words] " words"
.
.

Now it loads all of the words, and then asks to confirm you're ready. Because everyone will take a slightly different length of time to press return we'll get a truly random set of words.

It turns out writing code to set spelling tests is a lot more fun than doing spelling tests!

Monday, 13 March 2017

Cheerlights meet the Hue

In the pervious post we hooked cheerlights up to first Flotilla, and then NeoPixels on an Arduino using MQTT. However for some reason we didn't implement the most obvious thing - Cheerlights controlling Philips Hue bulbs! It turns out this is really simple...

make server cheerlight device

make brightness number
make hue number
make saturation number


make targetHue number
make targetSaturation number
make targetBrightness number


when start
.forever
..tell server to "update"
..set targetHue to hue
..set targetSaturation to saturation
..set targetBrightness to brightness
..wait 30 secs


The cheerlights bit is pretty simple, and standard... I just read the colour from the cheer light server, and store it in the "target" variables. We need to be a bit careful, as at any time, the value displayed on the actual lights might be different from the desired colour, and if we want to cross fade gently, then we need to record the target colour and the current colour separately.

make bridge hueBridge device
make bridgeAddress string "192.168.0.107"
make authorisation string "87XNzAMNY23423423408O61sF66FyqvHFLq0j"

make light number
make state boolean

make currentHue number
make currentSaturation number
make currentBrightness number

make speed number 0.02

when start
.set light to 5
.set state to on
.tell bridge to "set state"
.set light to 6
.set state to on
.tell bridge to "set state"
.
.set currentBrightness to 1
.set currentHue to 0
.set currentSaturation to 1

Now we can start adding a second script to start talking to the Hue. We make a hue Bridge device, which in this case corresponds to the one on my home network. You need the IP address (or name) of your brigdge and an authorisation key, which you can get from the Hue Bridge (see previous posts). The two coloured bulbs I want to control are numbered 5 and 6 in my system - again you can find the number of your bulbs either by talking to your bridge, or running the hueInfo.sniff program, which will list out your bulbs.

I turn on my two bulbs, and choose an initial value of Red to get them started. 

.forever
..set currentHue to currentHue*(1-speed)+targetHue*speed
..set currentSaturation to currentSaturation*(1-speed)+targetSaturation*speed
..set currentBrightness to currentBrightness*(1-speed)+targetBrightness*speed
..
..set hue to currentHue
..set saturation to currentSaturation
..set brightness to currentBrightness
..
..set brightness to brightness * 0.5
..set light to 5
..tell bridge to "set color"
..set light to 6
..tell bridge to "set color"
..wait 2 secs

Now this slightly clever bit... I want the bulbs to change slowly so I take the current colour and mix it with a small amount of the target colour. If speed was 0 then they would never change, and if speed was 1 they'd change instantly. It only takes a very small value of speed (0.02) to produce a gentle change over about a minute.

Now we know the colour the bulbs should be right now, we send that to the hue bridge. My bulbs are in the TV room which gets used in the late evening, so I also half the brightness to create a more relaxed environment.

The result is really great - though cheerlight traffic is quite slow some evenings, and people pick some boring colours (white? and pink looks boring). Normally the Hue lights look great when you turn them on, but then they're just  static. Having them change over the evening is really fun, and because they change gradually they're not distracting. It's also quite cool knowing that someone random somewhere in the world has changed the colours in your TV room!

Saturday, 11 March 2017

Cheerlights (and a bit of MQTT)

One of the things we added in the last release is a simple cheerlights client. Cheerlights is a system that scans twitter looking for posts are sent to it, using the tag @cheerlights, or simply including the word cheerlights, and a colour. Whenever anyone does that, every cheer light in the world switches over to showing that colour. What's the point? None at all, but its lots of fun knowing that you can control lights all over the world with just a tweet, and that you can build something that's part of a giant network.
The Sniff cheer light client runs on your computer and all you need to do is make a cheer light device:

make server cheerlight device

make red number
make green number
make blue number

make hue number
make saturation number
make brightness number
make shade number

make name string

Rather than just telling you the name of the colour (as it was in the tweet), it can find the numeric representation in lots of different formats, making it easy to use with lots of different hardware. Here's some simple diagnostic code to check in with the server, and print out the latest values.


when start
.forever
..tell server to "update"
..say name
..say join "red:" [red]
..say join "green:" [green]
..say join "blue:" [blue]
..say ""
..say join "hue:" [hue]
..say join "saturation:" [saturation]
..say join "brightness:" [brightness]
..say join "shade:" [shade]
..say ""
..say ""
..wait 10 secs


This tells the server to update every 10 seconds. You might want to think a little about how often up call update, as each time it sends a message to the central cheer lights server. While every 10 seconds isn't exactly going to generate a lot of traffic, and is a reasonable value for testing purposes, you might want to increase that a little if you decide to run a cheerlight client full time. After all checking once every minute or two isn't really going to make much difference if its just running in the corner of a room somewhere.

Now we need to get some coloured lights going, and the easiest way is with a flotilla rainbow:

make server cheerlight device

make hue number
make shade number
make name string

when start
.forever
..tell server to "update"
..wait 30 secs

We can start with the code from above, but chop it down quite a lot, as we don't need to know all those different formats.

make flotilla device
make lights flotillaRainbow device
make rainbowColor list of numbers
make rainbowShade list of numbers

when start
.repeat 5
..add 0 to rainbowColor
..add 0 to rainbowShade
.tell lights to "update"
.
.forever
..wait 0.5 secs
..delete 1 of rainbowShade
..add shade to rainbowShade
..delete 1 of rainbowColor
..add hue to rainbowColor
..tell lights to "update"


Next we make the flotilla rainbow device, and initialise the 5 LED's to be black. Then every half second we shuffle up the colours by deleting the first colour, and then add the current cheer light colour to the end of the list. This means when the lights change we get a nice animation effect.

That's pretty neat, but what if we want to control some more interesting hardware, perhaps attached to an Arduino. Well we could have re-writtten the cheer lights client to run on Arduino, but instead we wrote a cheer lights to MQTT bridge:

when start
.forever
..tell cheerServer to "update"
..broadcast display and wait
..wait 30 secs

We need to create a cheerlights device as before, and then check the status every 30 seconds.

make mqtt device
make clientid string
make message string
make topic string

make networkConnected boolean
make networkPeer string

when start
.set clientid to "sniffSendClient"
.set networkPeer to "raspberrypi.local."
.tell mqtt to "connect"
.
.if not networkConnected
..say "connect failed"
..stop script
.say "connected"
.
.forever
..tell mqtt to "loop"
..wait 1 secs

We also set up an Mqtt device, and tell it to connect to a server on raspberrypi.local (which is running Mosquito).

when display
.set topic to "cheerlights/red"
.set message to [red]
.tell mqtt to "publish"
.
.set topic to "cheerlights/green"
.set message to [green]
.tell mqtt to "publish"
.
.set topic to "cheerlights/blue"
.set message to [blue]
.tell mqtt to "publish"
.
.
.set topic to "cheerlights/hue"
.set message to [hue]
.tell mqtt to "publish"
.
.set topic to "cheerlights/saturation"
.set message to [saturation]
.tell mqtt to "publish"
.
.set topic to "cheerlights/brightness"
.set message to [brightness]
.tell mqtt to "publish"
.
.set topic to "cheerlights/shade"
.set message to [shade]
.tell mqtt to "publish"
.
.set topic to "cheerlights/name"
.set message to name
.tell mqtt to "publish"


After checking the cheer light serverver the code calls runs display, which pushes all of the data out to different topics.

Then we can run an mitt client on Arduino:

make spi device
make ethernet device D10
make networkMAC string "b6:ee:63:ed:95:cb"
make networkIP string "192.168.0.200"
make networkConnected boolean
make networkPort number
make networkPeer string

make mqtt device
make clientid string
make message string
make topic string


make neoPixels ws2811 device A1
make neoColor list of numbers
make neoShade list of numbers

make hue number
make shade number

when start
.set clientid to networkMAC
.set networkPeer to "192.168.0.108" #No DNS on Arduino!
.tell mqtt to "connect"
.if not networkConnected
..say "connection failed"
..stop all
.
.set topic to "cheerlights/hue"
.tell mqtt to "subscribe"
.set topic to "cheerlights/shade"
.tell mqtt to "subscribe"
.
.forever
..tell mqtt to "loop"
..if not topic = ""
...if topic = "cheerlights/hue"
....set hue to value of message
...if topic = "cheerlights/shade"
....set shade to value of message
..wait 1 secs

when start
.repeat 10
..add 0 to neoColor
..add 0 to neoShade
.
.forever
..delete item 1 of neoShade
..add shade to neoShade
..delete item 1 of neoColor
..add hue to neoColor
..tell neoPixels to "show"
..wait 0.25 secs

It connects to the server, and subscribes to the cheer lights topics for hue and shade. Then the neoPixel part of the code scrolls the colours just as we did for flotilla!