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.
Showing posts with label Arduino. Show all posts
Showing posts with label Arduino. Show all posts

Wednesday, 10 January 2018

BBoard Theremin



It's been a while, as I sort of got tied up in other things, but one of the devices that's been sitting on my desk for the last few months is a BBoard.



These little boards from Al's Tech Garage have three LED's, two switches, a buzzer and a light sensor, and are designed for plugging into Arduino's. They're pretty nice, though my one gripe would be that the Pins are labeled B1-9, rather than what they actually do, so you'll need to keep this handy table close to hand at all times:




I happen to know Al (of Al's Tech Garage) likes plugging them into Arduino Nano's because they're super cheap. If you're using a Nano, then you can plug them along one side of the board, and use the following Sniff declarations to set up the pins:

make pwr digital output D2
make button2 digital input D4
make button1 digital input D5
make buzzer digital output D6
make greenLight digital output D7
make amberLight digital output D8
make redLight digital output D9

when start
.set pwr to on

This is a little naughty as it uses D2 as the power supply, but the board draws sufficiently small amounts of power that it should be fine.

To plug in to an Uno then you can plug the board in with B1 into A5. You'll find that B7 (the light sensor) falls in the gap where the Arduino has no pins, but then B8 and B9 conveniently land in Vin and 0V. Strictly we shouldn't use Vin here, as if you're using a non-standard power supply it could be greater than 5V, but if you're powering over USB this works great. Of course we can't use the light sensor, but that's probably OK if you're getting started.

In fact the most obvious thing you'll want to do is make a traffic light! Yes - you've got three LED's of the right colours, a couple of buttons and a buzzer all ready to go. what else are you going to do? I've already written about implementing traffic lights a long time ago using the PiBrella. To make that work on the BBoard, just change the pin definitions to the above and you're good to go.

The next thing I wanted to build was a Theremin, using the light sensor to detect how far away my hand was. Unofrtunatly to make that work I couldn't use the "easy plug" method above, so had to use jumper cables to wire up the bboard as:

make light analog input A0
make buzzer digital output A1
make switch digital input A2

To make a sound with the buzzer we need to push it in and out (you can also use it with the Sniff music device, which is really cool, but we won't do that now). That's really easy:


make halfCycle number

make buzzerOn boolean
when start
.forever
..set buzzer to buzzerOn
..wait halfCycle microsecs
..set buzzer to off
..wait halfCycle microsecs


If buzzerOn is "off" then this does nothing. If buzzerOn is "on","yes", or "true" then this pushes the buzzer in and out, making a sound. The frequency is controlled by the variable halfCycle.

For a quick test we can just set the halfCycle something reasonable:

when start
.set halfCycle to 500000/440

440Hz is the standard frequency of the note A, which everything else tunes to. If something is oscillating 440 times per second then the duration of each pulse is 1/440. However we want half of that so we have 0.5/440. Finally we want the duration in microseconds, so we get 500000/440.

Once that was working I could rewrite to use the light sensor:


make frequency number
when start
.forever
..set buzzerOn to switch
..#say [light]
..set frequency to (light*220)+220
..set halfCycle to 500000/frequency
..wait 0.02 secs

the reading of the light sensor can be between 0 and 1, so we set the frequency to a value between 220 and 440Hz (one octave). Then calculate the halfFrequency. We do this 50 times a second, as that was sufficient to produce a smoothly changing note.

And that's it a BBoard Theremin! it works pretty well as long as there's plenty of light.





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!


Friday, 13 January 2017

Round and Round (rotary encoder)

Pots are fun, but they have two limitations - firstly they have a position, which is great feedback, but if it gets out of sync with the "real" value because you've changed it for some other reason then you need a motorised pot (as we saw list time) to get it back to the correct position. The second issue is that they have limited travel - when you reach the end, then you can't go any further.

Both of these are easily fixed using a rotary encoder (which also have the advantage of being digital, so you don't need an analog input, so on a Pi they're ideal).



Our encoder has 5 pins: Gnd and +ve are obvious. SW refers to an inbuilt switch, you can press down separately from the actual rotation. The other two are clk and dt, which are the interesting ones.

make dt digital input D5
make clk digital input D6

when start
.forever
..wait until clk
..say [clk]
..wait until not clk
..say [clk]

Clock will go from high to low and back repeatedly as you turn the shaft, so to get started we wait for it to change.

when start
.forever
..wait until clk
..wait until not clk
..say [dt]

The next thing to do is to check "dt" - direction of turn. Here I've set the code up to check dt whenever the clock goes low and we conveniently find that it is true when we turn clockwise and false when we turn anticlockwise.

All that's left is to add something to keep track of the number of times we turn in each direction:

make counter number
when start
.forever
..wait until clk
..wait until not clk
..if dt
...change counter by 1
..else
...change counter by -1
..say [counter]

This works perfectly well but we can do a little better. For the encoder I got we get 15 clocks per full turn, but we can look a little closer, and get 30 half clocks by checking dt when clock goes high as well as when it goes low. When we check this we see that when we check dt at this point in the cycle it has the opposite value - its low when we go clockwise, and high anti-clockwise. Adding this in doubles the resolution of our encoder:

make counter number
when start
.forever
..wait until clk
..if not dt
...change counter by 1
..else
...change counter by -1
..say [counter]
..wait until not clk
..if dt
...change counter by 1
..else
...change counter by -1
..say [counter]

This works really well and reliably, with one gotcha. It assumes that we're actually running this code regularly. If we go off and run another script at the same time we might miss a clock change. We will spot it eventually, but by that time DT may have changed to an incorrect value - its important to check DT immedialtly the clock changes. If you're running a lot of other code at the same time, then you may need to switch back to the other version which is a little more reliable.

Friday, 2 December 2016

Motorised Pot

I've been looking at a lot of smart home stuff recently and having fun with things like the Philips Hue and cheer lights. Ironically one of the best features of Hue is its light switch! It shouldn't come as much as a surprise that the best way to turn lights on and off is with a switch! Taking your phone out of your pocket is way too clunky. Even using Siri on Apple Watch you need to press the button and say "turn on the lights", when you could just press a button in the first place!

That's not to say Hue isn't really great - it is really great (and addictive and expensive!), but the best bits are having lights turn off automatically in rooms you're not using, being able to dim them exactly as you want, and turning on and off lamps from the switch by the door. Seriously that lamp in the far corner will see a lot more use when you don't have to turn on the main lights, walk across the room, turn it on, walk back then turn off the main light, then walk back and sit down! (then repeat when its time to leave).

Most of the cool tricks involve being able to control the lights in more flexible ways in addition to just a basic switch (and the ability to place that switch anywhere).

We've already implemented Hue support in Sniff so you can use and Arduino or PC as part of that control mechanism. Specifically I wanted to be able to use a Pot connected to an Arduino to dim lights. While an up/button works OK, the good old fashioned dial is something that's sorely missing from our modern lives. It would be really easy to have a pot connected to an Arduino, control the brightness of a Hue light, but with physical controls there's a big catch - turning the dial can easily dim the lights, but what happens when I dim the lights on my phone? Now the dial is out of sync with the lights...

What we need is a motorised pot - which is exactly what it sounds like: a Pot with a motor attached. These can be quite expensive, but I found a cheap source on eBay and picked one up for about £3.

Conceptually they couldn't be easier - the pot at the front can be driven by the motor at the back. The only tricky part was figuring out the pins, as for some reason there are 8 pins on the pot instead of the usual 3. With a bit of googling and poking around with a multi-meter, I found the connections I've penciled on in the picture:
  • Pin 2: V+
  • Pin 6: 0V
  • Pin 7: Signal

Signal just connects to any analog in, and we can read the position of the pot.

make dial analog input A0

when start
.forever
..say [dial]
..wait 1 secs



Hooking up the motor is simple but requires a motor driver. I could have used a motor shield, but I had some stand alone boards around. We don't need much power, so I just powered the motor board from the 5V of the Arduino. Connect the motor wires to the outputs of the board, and then connect two wires from Arduino digital pins to the control pins of the motor board.

Coding this is simple but quite fun:

make dial analog input A0
make motorCW digital output D2
make motorCCW digital output D3

make targetDial number

Here I've set up the pins, and created a variable targetDial. The only thing to watch is that the motor pins are the correct way around, and CW does turn the pot clockwise and CCW counter-clockwise (and similarly that turning CW increases the value of dial). Now we need to just change dial until it matches targetDial:

when adjustDial
.repeat until abs of (dial-targetDial)<0.01
..set motorCW to targetDial>dial
..set motorCCW to targetDial<dial
.set motorCW to no
.set motorCCW to no

We've made a loop, and we keep going round the loop until the difference between the desired and actual values are less than 0.01. We use abs because we don't care if the difference is positive or negative.

The contents of the loop might look a bit odd, but its really pretty simple. When ever we use a greater than (or less than) its actually testing something to see if its true or false, so the answer to the question "is targetDial>dial" is either a yes or a no. So if targetDial is bigger than the actual position (dial) then we set motorCW to yes, and the motor turns clockwise. If it isn't then motorCW becomes no, but motorCCW probably becomes less and we turn the pot CCW.

This will keep adjusting the dial until it reaches the target position. The neat thing is that if it overshoots, then it will adjust back the other way automatically, until its in exactly the right position and the loop exits. Then we turn the motor off, and we're done.

when start
.forever
..say ""
..say join "Current Val:" [dial]
..set targetDial to ( pick random 0 to 10 ) *0.1
..say join "Target Val:" [targetDial]
..broadcast adjustDial and wait
..say [dial]
..wait 5 secs

To test the adjustDial script I wrote the start script above. It picks a random value between 0 and 1, and adjusts the dial to that position. It then waits 5 seconds (during which you can turn the dial manually) before starting over again.

The next step would be to read values from the Hue system, and move the pot appropriately, and when the pot is turned, push values form the pot back to Hue. There are some updates in the next release to make that all very easy, so I'll cover that another time, but in the mean time consider what else needs a nice rotary control...

Sunday, 6 November 2016

Simple Reation Timer

Yesterday I ran the first of a series of workshops, making fun stuff with Sniff on Arduino. We started with an Arduino and an LED, then added a button and a potentiometer (dial). That neatly covers inputs and outputs, both digital and analog, so we can declare all that hardware with something like:

make led1 digital output D13
make led2 analog output D9
make button digital input D4
make dial analog input A0

Then we can use the button to control the "digital led"

when start
.forever
..set led1 to not button

use the potentiometer  to control the brightness of the analog led:

when start
.forever
..set led2 to dial

or we can use the dial to set the speed of a flash:

when start
.forever
..set led1 to on
..wait dial secs
..set led1 to off
..wait dial secs

we can even make the button control a flashing LED

when start
.forever
..if not button
...set led1 to on
...wait dial secs
...set led1 to off
...wait dial secs

The idea was then that we could build something fun or useful using the components. The code for a simple ration timer is:

when start
.forever
..set led to off
..wait pick random 1 to 5 secs
..set led1 to on
..reset timer
..wait until not button
..say [timer]

You can extend this a little to prevent cheating by checking that the button isn't pressed before the LED comes on:

when start
.forever
..set led to off
..wait pick random 1 to 5 secs
..if not button
...say "Cheater!"
..else
...set led1 to on
...reset timer
...wait until not button
...say [timer]

Hopefully everyone had a fun time with this, and I've got some ideas for a Xmas/Winter project next month...