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

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!

Wednesday, 12 October 2016

MQTT Basics

MQTT isn't particularly well know, but if you're building things with Arduino's then it probably does something very useful to you, in a quick and some fashion - it moves data around between embedded controllers and servers with the minimum of fuss. It's an industrial standard which means its battle tested, but it also means it can be a bit intimidating reading some of the documentation. Its actually really simple.

There are two parts to any MQTT system: The server or "broker" that manages everything, and the clients that do the work. Clients will usually be attached to hardware to either measure or control something, but there might also be some larger clients, doing logging, analysis and providing overall control. We'll be mainly looking at the small side of the system (though you can write the "big" side in Sniff too). For pushing data out of the MQTT system you can use something like Node-Red, which has full MQTT support, making it easy to link IoT/Embedded tech to the bigger world.

Setup

The first thing you'll need to do is set up a broker. These can potentially be massive servers, as the whole thing is designed to scale to thousands or even millions of clients, but assuming you just want to drop a few smartThings around your house we can use a Raspberry Pi - in fact this is the best use I've ever found for a Pi! To set it up as a server just run

sudo apt-get install mosquitto 

That's it! It should install the server and start it running. You should probably tweak the config a little (at some point you should turn on the security features!), but basically it works perfectly out of the box.

Next we need a client. We're going to write our own clients in a few seconds, but its handy to have a debug tool. I grabbed a copy of MQTT.fx, but there are plenty to choose from (including for phones which is kind of handy of you're wandering round the building testing an install). Node-Red will work too, though its a bit fancy for just basic debugging. Connect your test client to the server, and nothing much should happen! No errors, and its all good.

Connecting

Now lets write some Sniff. We'll start with something running on the computer first to illustrate a the basic concepts of MQTT.

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

make networkConnected boolean
make networkPeer string

First we need to make an MQTT device, and some variables to control it. There are a few more parameters we can use but the essential ones are here - the rest can be left as default a lot of the time. Then we need to connect to our server:

when start
.set clientid to "sniffListenClient"

.set networkPeer to "raspberrypi.local."
.tell mqtt to "connect"
.
.if not networkConnected

..say "connect failed"
..stop script
.say "connected"

The only tricky bit here is the clientid. It needs to be something completely unique. If you run two versions of the same program with the same id then it won't work. For test purposes we can put anything in here, but for real world uses it should be a name which is specific to that client, on that particular piece of hardware.

Subscribing

Finally we can get to some real stuff - messages. Every message has a topic, and clients tell the server that they're interested in a particular topic. Then they get sent all the messages on that topic.

.
.set topic to "text"
.tell mqtt to "subscribe"
.

Here I've said I'm interested in all messages with the topic "text". Then all we have to do is wait for messages to arrive:

.
.forever
..tell mqtt to "loop"
..if not message = ""
...say message

Messages get received when we call "loop", but its important to call loop regularly even if you're just sending messages, as it handles lots of behind the scenes networking too. If "loop" finds a message it sets the values of topic and message, otherwise they'll both be empty strings. In principle you can send any kind of data over MQTT, but Sniff doesn't handle raw data very well, so it can only send and receive ASCII data. Having things in a readable format is probably better for small scale projects anyway.

Now we've reached the point where something like MQTT.fx comes in handy - select the publish tab, enter the topic "text" in the top box, and add some longer text into the main area. When you hit publish the Sniff program should receive the message and print it out!

It's that simple! You can have any number of clients (with unique ids) connected at the same time, all connected to the same server, and they'll all receive the message, so you can control them all by just publishing a message to the server.

Publishing

To Publish messages to the server, we first need to establish the connection, and start calling "loop":

#Call loop to receive messages forever
when listen
.forever
..tell mqtt to "loop"

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"
.
.broadcast listen

The easiest way to do this is to create a new script called listen, and once we're connected we start running that, using broadcast to kick it off. It will now do all the housekeeping in the background, and the mains script can continue on and do its thing.

.
.forever
..ask "Message?" and wait
..set topic to "text"
..set message to answer
..tell mqtt to "publish"
..say message

To publish a message first we ask what the message should be (which is returned in answer). Then we set the topic to "text" because that's what our other clients are subscribed to, and the message to whatever we just typed in. To push those out to all the other clients, just tell mqtt to "publish", and its done!

One gotcha to be careful of is that "listen" is running at the same time as the main script, and it can change the values of topic and message when we're not expecting it (this is called a race condition, and its a real thing that proper computer scientists worry about a lot). The way Scratch and Sniff are designed this is minimised as different scripts are only allowed to run at specific times. Here we need to watch out that listen can run during an ask, or a say (or a wait, but that's pretty obvious!). That's why we print the message out after its been sent. If we say the message before sending, listen will sneak in while we're printing it out. It will print OK, but the publish probably won't work. Just remember to set topic and message immediately before you publish and you're guaranteed to be OK.


And we're done...

for now! That is all you need to know to use MQTT in Sniff, but there are few more fancy features that will come in handy. You probably also want to know about using this on Arduino (same code, different setup), but I'll write them up next time!

Friday, 16 September 2016

Sniff Live For Arduino

Our initial implementations of Sniff Live were aimed at getting regular programs compiling and running in the browser, so it would be easier for new users to write a few simple Sniff programs without having to install the system. On Windows in particular installation is a bit of work (it's MUCH easier if you're running Linux or Mac).

However the most fun projects we do with Sniff involve external hardware - usually the Arduino Uno, so getting that working with Sniff Live was something we always had as part of the plan, and now its ready for you to try out.

If you head over to live.sniff.org.uk  you'll see that on the front page there's a suggestion to download the Loader app.  Download and unzip it somewhere. The Loader app is the easiest way to upload intel hex files to an Uno, though if you have any other method you prefer that will work too.

Then log in as usual, and take a copy of the blink example using the "copy examples" pop up in the top left. Once you've got that, press the Arduino button in the editor to compile your code for arduino. If you were running Sniff on your own computer, this would also do the flashing for  you, but unfortunately it can't because now the code is being compiled on our server, while the arduino is connected to your PC!

Once the code is compiled, press the "run" button, either in the editor, or in the sidebar (the run link might not appear straight away). This should download the hex file to your computer. The exact details of what this looks like will depend on your browser and its settings, but you should save the file, and open it.

Your PC probably doesn't know what to do with a "hex" file, so set up the file association so that it opens in the "UnoLoader.exe" that you downloaded earlier. From then on when you double click a hex file, or tell your browser to "Open" it, then it should upload the code straight to your arduino without any further intervention from you.

If UnoLoader doesn't work, then check that you have an Uno connected, and that it is appearing as a COM port. If UnoLoader does fail, then its window will stay open and it will tell you whats going wrong. Check out what it says the problem is, and if you can't figure it out, let us know.


Wednesday, 1 June 2016

Talk to me Sniff!

In all the excitement over the microbit, we forgot a fun little feature we added in release 27. On "hosted" systems (Mac, Windows and Linux) you can now make Sniff speak. On Mac and Windows it uses the built in voices which sound pretty good. On Linux you may need to install the speech software (using apt-get, or yum).

To make the computer talk to you, all you need to do is create a speech device:

make voice speech device
make message string

Then just set the message and tell it to "speak"

when start
.set message to "hello world"
.tell voice to "speak"
.wait 3 secs


It's that easy! It'll read out any text you like, so to make it count:

make counter number
when start
.set counter to 1
.repeat 10
..set message to [counter]
..tell voice to "speak"
..change counter by 1
..wait 1 secs

That's pretty much all there is to it!

Sunday, 3 April 2016

Installing on Windows

Installing Sniff on Windows is a bit more tricky than on other platforms. This is mainly because both Mac and Linux provide a standard, more or less built in set of developer tools. While these might not be installed by default, they're pre-configured so they pretty much install automatically. On Windows we need to install a few things manually, and installing things on Windows is always a pain. There's also been some problems with these tools changing (and different version of windows), so here's an up to-date install guide:

Step One: Install MinGW

MinGW is MINimal Gcc for Windows. It provides the heavy lifting backend, to turn programs into executables. Go to their website, and click the button on the top right that say "download installer". Once its downloaded, run it and you'll get the option to configure your system. If you want you can install everything, but you simply need to select "base", and "msys". Click the Install button, and off it goes.

Once its done, you'll find you've got a folder C:\mingw (it's possible to install elsewhere, but lets not!). Inside that is C:\mingw\msys\msys.bat. This is the program we want, so drag a shortcut to the desktop/startmenu or wherever you want it. If you double click in it you'll get a bash shell - type "ls", and/or "pwd" to check its working (it's a basic Unix/Bash shell so you can pretend you're using a proper computer).

There's one more file to edit before you've got a working MinGW. Open the folder C:\mingw\msys\1.0\etc. In there you'll find a file called "ftab.sample". Copy this (or rename it) to fstab - no extension, in the same folder.

Now if you start another shell (running the msys shortcut) you'll find that the command "gcc" will say something like "no input files". That means its installed and ready to go.

Step Two: Install Sniff

Download the latest SniffW release. There's actually no difference between the Unix and Windows packages, but we just delete the un-needed bits from each version to keep the file size small. You can place the unzipped folder anywhere you like (please avoid path names with spaces in!), but its easiest to just put it in the users msys folder. To do that open an msys window and type "start .". Then drag the Sniff folder into the folder that has just opened.

Step Three: Lets Go!



In the Sniff folder there's  file called "clickMe.bat". If you do click it then you should get a working version of SniffPad, which you can use to get started writing Sniff programs. You can also create a shortcut to clickMe.bat and put it on your desktop (and rename it to something more memorable), to make things really easy.

When you run Sniffpad like this it runs in the Documents/SniffProjects folder, so you can copy any examples files from the release to that folder to run them.

At some point you'll want to do more than you can just using Sniffpad, so start an msys shell, then cd to the Sniff folder - it you've placed it in the Sniff folder as above, then "cd Sniff26WRelease" or whatever version you're using. Then type "source setup" or ". ./setup" (they both do the same thing) to initialise everything.

Now in the shell, you can run "sniffpad".  cd into the examples folders and off you go. For a more advanced user you can use any text editor you like, and compile programs from the shell, by typing "sniff myprog.sniff".

Step Four: Install Arduino

This step is optional - if you don't want arduino, then just don't install it. You can also install it any time afterwards. Download the installer from the arduino site, and do a regular/standard install. The next time you run the sniff setup, it will say that its found an arduino installation and now in sniffpad you can click the arduino button to compile and download to an Uno, or use uno-sniff to compile from the command line.

You should be able to use more or less any version of the Arduino IDE, but recent versions have a rather nasty bug where the compiler (actually the linker) crashes! This is a problem that the Arduino Forums have been struggling with for a while, as its difficult to reproduce - it only happens for certain programs on certain hardware. However it turns out that Sniff blows it up nearly every time! If you get an error message about "ld.exe", then  try downloading the Arduino 1.0 IDE, and using that. It's a very old version, but seems (at least for me) seems to work OK.

COM Port Discovery
When you initialise Sniff (either by clicking on clickme.bat, or "source setup' from the command line) it detects the first com port and tries to use that whenever it tries to talk to an Arduino, Flotilla, Picoboard, or Lego Control Centre. If you find that any of these aren't working, then try removing other serial devices, and make sure that Sniff is talking to the right piece of hardware.



And that's it... It's a bit more fiddly that Mac/Linux, but not really so hard.

Release 26: TLC for Windows

Release 26 is aimed squarely at Windows users.

Since we last did a windows focus release, a lot of things have changed in terms of the packages we rely on - MinGW, Arduino and Window's itself, so this time aroundwe've dedicated some time to getting everything to work as smoothly as possible for Windows users. We'll have a more detailed blog post soon, re-ittereating the Windows install requirements, but basically install MinGW/Msys, then just unzip the Sniff folder!

Now everything should run a bit smoother on windows, and we've even added a file called "ClickMe.bat"... which fires up sniffpad, and you can just start coding!

Unfortunatly the Arduino backend for Windows is crashing (its actually a bug in gcc!), so if you get a message that ld.exe is hanging when compiling for Arduino, try installing the old 1.0.6 version of Arduino.

If you're running Windows, then get over to the downloads page immediately. There's also few other minor fixes and features for everyone else too...

Wednesday, 18 November 2015

Playing Sounds

One of the features in the recent release is support for playing sound files. We've actually had a "sound" device (on Hosted platforms) for some time, but so far it only actually worked with js-sniff, where the compiled code runs in a browser. This meant you could write games in Sniff and have sound when you embed them in a web page, but the sounds wouldn't actually play when you ran the code as a real program on your own machine!

We've fixed that, and now you can play sounds on Mac, Windows and Linux.

make player sound device
make fileData string

when start
.set fileData to "chainsaw.wav"
.tell player to "load sound"
.forever
..tell player to "play"
..wait 10 secs

The code is about as simple as it gets: we make a sound device (called player in this case), and tell the player the name of the sound file we want it to play. With that out of the way, we can just tell it to play whenever we want to hear that sound.


If you want to play multiple sounds then you can either create multiple devices, or create one device, and keep telling it to load different sounds. Use whichever approach makes most sense for your app.

Because each system handles sound file slightly differently there are minor variations in behaviour across platforms. On Mac and JS you should be able to use most types of sound files, while on windows and Linux it must be a WAV. On Windows if the file can't be loaded a default "ping" sound is played instead. The Linux version uses the external program "aplay" to actually play the sound. This is standard in most distributions. Some Linux variants give a warning that a return value is ignored when you compile the Sniff program - this is harmless, and we've fixed it in new versions so if you do see it, just ignore it.

That's about it - get out there, play some music, and add some sound effects to your Sniff games!

Saturday, 8 August 2015

The Unfortunately Named SniffPaint


One of the researchers we work with is interested in getting kids to design video games to explore social issues (a little like the Serious Play approach). Having worked with groups of children to develop their ideas he approached us to help help them actually turn those ideas into playable games.

This led us to develop the Sprite system we've posted about recently, which makes it easy to write simple Sprite based games in Sniff. In our initial classroom tests this worked really well, and a group of KS3 kids who had never used Sniff before were able to work through a series of exercises and get a working game in an afternoon.

We learnt a lot from that trial, and made a few changes to the Sprite system to make things easier. For example you can now tell a sprite to "move to", or turn by" rather than the more "programmer" style "moveTo" or "moveBy". Well D'uh! Of course that was obvious if you're a kid, but its hard for grown up programmers to remember what it was like to not be programmers. Programming like a kid, rather than like a middle aged guy is why we built Sniff, so we fixed it! The kids' way is better!


One of the things that was surprisingly hard for kids to do as actually make artwork for their games. You would think that this wouldn't be a problem - its 2015! We have a million tools for making images... well no actually we don't! If this was the 80's or 90's we'd have MacPaint or Windows Paint(aka Paintbrush). Now we don't. On Windows 10 "FreshPaint" is an optional "Freemium" install, and costs "from Free to £77.29". I've only spent a few minutes with the free version during which I've been offered several in-app purchases, but I've yet to see anything that actually involves drawing an image!

Of course at the other end of the scale you've got photoshop. While that may be a great tool (not convinced - looks clunky and badly designed!), its certainly expensive and complex. We wanted something that could

  • make basic, low res icons
  • ran on all platforms
  • be learnt in 2 minutes
  • load/save BMP
  • doesn't cost anything
There are some web tools out there that provide some of those features, but getting images out of the web and into the games in the right format was a clunky process.

So we build SniffPaint. It's written if Sniff of course! "Dogfooding" is a great development tool. By writing it in Sniff we get a tool that runs on all Sniff systems, develop and debug the language, and get a really cool demo of how flexible Sniff is. Sniff Paint is included in Sniff R20, both as an executable (just type sniffpaint) and full source (examples/Hosted/sniffpaint.sniff) so you can add your own features if you like. We built the tools using the tools we give you!


SniffPaint isn't going to scare adobe anytime soon! It's very basic, but based on the experiences of the "in house testing team" a 7 year old can produce exactly the sort of artwork we need after only a few minutes. It's not particularly tied to the Sniff code, so you can just use it on its own to generate images for any of your projects. You can even try an online demo version online - just remember the online demo can't save anything, as its running in a sandbox and can't access your real files!

Using SniffPaint

To get started, just click on a colour in the palette and then click a pixel in the canvas. When you're done hit save (hint - up down cursor keys work great in the load/save panel).

Default resolution is 46x46 pixels. This just happens to fit well with the screen size. Hitting HiRes will double the current resolution so you can add more detail. LoRes halves the resolution. 46x46 is a good size for small game objects, 96x96 is about right for larger characters. If you want a nice 8-bit look then design at low-res, and then size up when you're done!


At this resolution drawing a single pixel at a time produces the best results, but you can draw circles or lines by clicking the button, then clicking and dragging on the canvas. Selecting a tool like this is a "one shot" option, and you're dumped back into single pixel mode immediately. If you want to draw lots, use the keyboard shortcuts - just press the letter for the tool you want to select. Dropper allows you to select a colour from the canvas, and fill (sort of) fills areas.

New Internal Bitmap Support

Sniff paint is written in Sniff! In order to make Sniff Paint possible we improved support for bitmap images. Previously you could draw bitmaps on a display using tell display to "draw image" but pushing that file access functionality into the display code was always something that needed looked at, so we fixed it. "draw image" will be available for the next few releases, but gradually we're going to remove it from display devices.

We've now got a new "bitmap" device. It currently only works with the Window device but we hope integrate it so you can use it on any display in the future.

In SniffPaint the default canvas is 46x46 and black, which we can create with:

make canvas bitmap device

when start
.set displayColor to 000
.set displayX to 46
.set displayY to 46
.tell canvas to "new"

Then we can draw it by simply setting the position, and calling draw:

.set displayX to 10
.set displayY to 10
.tell canvas to "draw"

When you draw them bitmaps are automatically "keyed" to remove the background, so you don't need to worry about Alpha (we may add a way to control this inf the future, but the default 

To edit it, we can use 
.tell canvas to "set pixel"
.tell canvas to "set pixel"

Load and save work with BMP files - not the most up to date format, but simple and easy:
.set fileData to "myDrawing.bmp"
.tell canvas to "load"
.tell canvas to "save"

If you've loaded an image in it can be useful to know how big it is: 
.tell canvas to "get size"
.say join "width:"[displayX]
.say join "height:"[displayY]


The next plan is to use what we've built as part of a 2 day games jam that will take place in September, where groups of kids will build a game over a weekend. We'll announce more details later, but if you interested in taking part let us know.



Monday, 6 July 2015

Sprites in Sniff (Pt 1)

Everyone loves games, and Scratch makes it really easy to build simple games. However when we built Sniff we wanted it to be more like a "real" programming language, that could be used to solve all sorts of problems - not just sprite based games.

But of course that doesn't mean that writing sprite based games isn't fun! As we're rounding out the support for Sniff, we've now added support for Sprites, so you can write Scratch like games. However Sprites aren't part of the language as they are in Scratch - they're devices. While this means the two systems aren't exactly alike, it makes the implementation a lot cleaner, and makes it possible to do things that would be very difficult in Scratch.

We'll be developing the documentation for this over the next few weeks, but here's a brief introduction (and you can check the examples in SNIFF/examples/Hosted/sprite).

make nativeFile device
make fileData string

make display window device

make spriteManager device
make spriteX number
make spriteY number
make spriteHit boolean
make spriteID boolean
make spriteValue boolean

when start
..tell spriteManager to "drawAll"
..wait 0.1 secs

We start by making a whole bunch of devices and variables. The SpriteManager is going to look after all of our sprites. We'll use it more in part 2 of this tutorial, but its most important user facing task at this stage is to handle the drawing. You can ask individual sprites to draw, but for simple games, we're just going to task the spriteManager to "drawAll". We can put this in a separate script and just let it run.


The sprite manager uses the display device (which currently has to be a window, but we hope to support other display types in the future), which in turn uses a nativeFile device.

Now lets make some sprites:

make background sprite device
make donut sprite device
make player sprite device

when start
.set fileData to "sea.bmp"
.tell background to "loadCostume"
.
.set fileData to "turtle.bmp"
.tell player to "loadCostume"
.
.set fileData to "donut.bmp"
.tell donut to "loadCostume"

We make three sprite devices, starting with the background - by default they get drawn in the order they're created, so background has to go first. We then load up some images to set the appearance of each sprite. Here we've only loaded up one costume per sprite, but you can add up to 8, just by calling loadCostume again. "setCostume" and "nextCostume" let you cycle through the different appearances.

.set spriteValue to 2
.tell player to "setCostume"

Sniff windows are fixed at 640x480, so we move the background image to the centre of the screen:

.set spriteX to 320
.set spriteY to 240
.tell background to "moveTo"

To move things around, we can use XY coordinates to set (moveTo) or adjust (moveBy) the players position). We can also find the players position using "getPosition".

Alternativly we can turtle style graphics, with "turnBy", "turnTo", "getRotation": and moveForward:

.set spriteValue to 90
.tell player to "turnBy"
.set spriteValue to 100
.tell player to "moveForward"


We can combine that with code to read from the keyboard to move our character around:

when start
.forever
..set specialKeypress to ""
..tell display to "getEvent"
..if specialKeypress = "left"
...set spriteValue to -10
...tell player to "turnBy
..if specialKeypress = "right"
...set spriteValue to 10
...tell player to "turnBy
..if specialKeypress = "up"
...set spriteValue to 10
...tell player to "moveForward
..wait 0.1 secs

Now we need to configure the collision detection:

.tell background to "setUnhittable"
.tell player to "setUnhittable"
.tell donut to "setHittable"

The background takes no real part in the game, so we set it to be unhittable. Similarly we're interested in the player hitting the donut, rather than the donut hitting the player, so we set the donut as hittable, and the player as unhittable.

.forever
..tell player to "checkTouching"
..if spriteHit
...change score by 1
...set spriteX to pick random 30 to 600
...set spriteY to pick random 30 to 440
...tell donut to "moveTo"
..wait 0.1 secs

We call "checkTouching" on a sprite to discover it its hit something hittable. If it has (in this case) it must be the donut, in which case we increase the score by 1, and move the donut to a new random location.

Using these basic sprite functions its possible to write the standard chase the target, Scratch type games in Sniff. In addition Sniff has a much more powerful way of dynamically creating sprites through the SpriteManager, so you can spawn new sprites as your game is running, rather than having to code them all at compile time. The two approaches play nice together, so you can migrate to the advanced API when you're ready, but we'll leave that till next time.

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.