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.

Sunday, 6 April 2014

Pibot

@code_ED and I had a bit of a catch up last week, and he leant me a few toys to play with -  an Arduino Yun and one of his PiBots (http://raspberryfool.net). I'll save the Yun to talk about another day, but the PiBot works great with Sniff.

The PiBot consist of two tracked sets of wheels, driven from a board which handles the electrical side of things, including providing battery power to the Pi. Driving it from Sniff is pretty easy (once I'd figured out that the sample Python code used Pi header pin numbers rather than broadcom gpio pin numbers).

make pibotPower digital output 17

make leftForward digital output 18
make leftBackward digital output 27 #21 on rev 1 Pi board
make rightForward digital output 23
make rightBackward digital output 24

Firstly we set up some digital outputs. The pibotPower is a global on/off switch which also resets the driver board - the driver board includes a kill switch, which turns out to be pretty handy when your robot gets out of control (note when it gets out of control, rather than if). The first thing the code needs to do is turn off the power to reset the kill switch, then turn on the power:

when start
.set pibotPower to no
.wait 0.2 secs
.set pibotPower to yes


and then we can start moving around. To go forwards:

.set rightBackward to no
.set rightForward to yes
.set leftBackward to no
.set leftForward to yes

and of course we can go backwards similarly:

.set rightBackward to yes
.set rightForward to no
.set leftBackward to yes
.set leftForward to no

To stop, we can either kill the master power, or set all of the values to "no". To turn we just send one track forwards and the other backwards:

.set rightBackward to yes
.set rightForward to no
.set leftBackward to no
.set leftForward to yes

slightly counterintuitivly, sending left forwards, means you're turning right!

It's pretty easy to put each of these in a script, and then move the PiBot around using something like:

.repeat 4
..broadcast forward
..wait 1 secs
..broadcast left
..wait 1 secs
.broadcast stop

However while this works fine, and is a really good way to get started I wanted to do something more fun. To get the PiBot to move more smoothly we need to be able to set the speed of the motors. We would do this by changing the voltage supplied to them. Unfortunately the PiBot can't do that, but what if we turned the motors on and off really fast - like 1000 times a second. If they were on for half the time, and off for half the time, then it would look like they were running at half speed (in fact they probably are because the motors probably take longer than that to stop or start). This is exactly the trick the Arduino uses when we use an analog output as the Arduino's AVR chip has hardware specifically to do that, but the Pi's GPIO can't do that either.

But as I've worked with Sniff I've found that its surprisingly good at doing stuff like that. It's not really designed for high speed low level IO, but it seems work.

make leftSpeed number

when start
.forever
..if leftSpeed>0
...set leftBackward to no
...set leftForward to yes
...wait leftSpeed*1000 microsecs
...set leftForward to no
...wait (1-leftSpeed)*1000 microsecs
..else
...set leftForward to no
...set leftBackward to yes
...wait leftSpeed*-1000 microsecs
...set leftBackward to no
...wait (1+leftSpeed)*1000 microsecs

We have a number "leftSpeed" which is going to go from -1 to +1. -1 is full speed backwards, while +1 is full speed forwards. I'm sure there's a more elegant way to do this, but we handle forwards and backwards separately. If we're going forwards then we turn the left motor on for a fraction of a millisecond, then off for the rest of the millisecond. The longer that fraction then the more power goes to the motor, and the faster it goes. Same for backwards, and the same for the right track.

.set leftSpeed to 1
.set rightSpeed to 0.5
.wait 10 secs

makes the piBot turn in a nice circle, while to get the sidewinder dance in the video, you can try:

.forever
..set leftSpeed to  sin of (timer * 100)
..set rightSpeed to  cos of (timer * 100)

Just set leftSpeed and rightSpeed to the values you want, and off it goes!

here's the complete code:

make pibotPower digital output 17

make leftForward digital output 18
make leftBackward digital output 27 #21 on rev 1 board
make rightForward digital output 23
make rightBackward digital output 24

make leftSpeed number
make rightSpeed number

when start
.forever
..if leftSpeed>0
...set leftBackward to no
...set leftForward to yes
...wait leftSpeed*1000 microsecs
...set leftForward to no
...wait (1-leftSpeed)*1000 microsecs
..else
...set leftForward to no
...set leftBackward to yes
...wait leftSpeed*-1000 microsecs
...set leftBackward to no
...wait (1+leftSpeed)*1000 microsecs

when start
.forever
..if rightSpeed>0
...set rightBackward to no
...set rightForward to yes
...wait rightSpeed*1000 microsecs
...set rightForward to no
...wait (1-rightSpeed)*1000 microsecs
..else
...set rightForward to no
...set rightBackward to yes
...wait rightSpeed*-1000 microsecs
...set rightBackward to no
...wait (1+rightSpeed)*1000 microsecs

when start
.set pibotPower to no
.wait 0.2 secs
.broadcast startPiBot
.
.set leftSpeed to 1
.set rightSpeed to 0.5
.wait 10 secs
.
.reset timer
.repeat until timer > 10
..set leftSpeed to  sin of (timer * 100)
..set rightSpeed to  cos of (timer * 100)
.
.set pibotPower to yes

Of course the real irony is that the PiBot board includes an AVRTiny processor to handle the kill switch. Sniff works great on AVRTiny processors - with a bit of tweaking to allow reprogramming, the PiBot board could run all this stuff itself, and it wouldn't even need the Pi!

No comments:

Post a Comment