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.

Saturday, 25 October 2014

Arduino phone home

Every month or so someone gets 5 minutes of internet fame claiming they just invented their own smart phone using a raspberry pi and a few hundred pounds in parts... Trouble is that's a lot of money for something that's actually not a very good smart phone. I'm far more interested in building a dumb phone. For about £30 you can build a phone based on an Arduino. If you wanted to you could add a touch screen, but its far more fun to think about what you could do with a phone which has no screen at all, but is attached to one of your projects: Hook it into a temperature sensor, and it will phone you when your greenhouse gets to cold for your tomatoes. Or use a humidity/water depth gauge and get it to send you a text to let you know your garage is flooding...

To make a phone you basically need a GSM shield. These used to be quite expensive, but the price has fallen a lot, and you can get one for about £20 now. There are some variations, but most of them are based around the SIM900 chipset. The main difference between them is that they may use different pins: The Sniff device is set up to use pin8 to control power on, 9 reset, 2 receive and 3 transmit. These are easily changed in lib/Xphone.c if you have a slightly different model.

I also hooked up an i2c lcd display, so I could see what's going on. We can set up the drivers for the phone and the display in Sniff as:

make i2c device
make display lcdi2c device 4
make message string
make displayFlush boolean
make phone device

make phoneNumber string

I was using a 4 line LCD - hence the 4 config parameter to the lcdi2c device.

Then you start up the first thing that happens is that the system tries to automatically turn then the phone on, and connect to the network. This can take a few seconds, so we start by printing a message and then waiting for the phone to tell us its ready:

when start
.set message to "Sniff Phone"
.tell display to "show"
.
.repeat until message="ready"
..wait 0.5 secs
..tell phone to "checkStatus"
.tell display to "show"
.

Once "checkStatus" returns the "ready" message we're good do go. From here on in there are basically 4 things we might want to do: answer a phone call, make a phone call, send an SMS or receive an SMS. The GSM board also supports mobile data, but that at least in the UK that can get expensive if you're not on the right contract. By contrast for £5/month, I can send and receive unlimited texts, receive calls, and make unlimited calls to phones on the same network. If I only receive calls and texts, and may send a few texts, than I can probably make that £5 last all year.

The easiest thing to do is send and SMS:
.set phoneNumber to "+441234111222"
.set message to "Sent from my Arduino"
.tell phone to "sendSMS"
.tell display to "show"

We just set the number and the message, and the tell the phone to "sendSMS". If all goes well then the variable message should be set to "Sent", or if there's a problem it will be set to "Failed".

Calling a number is almost as easy:
.set phoneNumber to "+441234111222"
.set message to join "Calling:" phoneNumber
.tell display to "show"
.tell phone to "startCall"

You can plug a headset into the GSM shield, so you can chat to someone! One project we've discussed is for a someone we know who is disabled, and unable to dial a phone: It would be trivial to  make a custom phone which dials their emergency contact, by pressing a single large button.

During a call you can check its status by calling:
.tell phone to "checkStatus"
.tell display to "show"
.set message to join "busy:" phoneNumber
.tell display to "show"
This also works for incoming calls too.

When you're done just hang up:
.tell phone to "endCall"

Receiving calls and texts is a bit harder, as they can arrive at any time, so you need to check for them:
.forever
..broadcast checkSMS and wait
..wait 0.5 secs

To actually check the message:
when checkSMS
.tell phone to "checkSMS"
.if not message = ""
..tell display to "show"
..set message to join "from:" phoneNumber
..tell display to "show"
..tell phone to "deleteSMS"

We try to receive the message by telling the phone to "checkSMS". which will fill in the message, and phoneNumber (though note that on AVR's Sniff strings are limited to 128 characters which is shorted than the longest possible message - there's simply not enough memory on an Arduino to hold long strings). Here we print out the message, and number then delete the SMS. If you don't delete it, it will be there next time you check.

You could use the contents of the message to do pretty much anything - for example you could send a text to the arduino, and use its contents to change the colour of your neoPixel christmas tree lights (When I show this code in CPD groups with teachers I also point out it could be used to do unpleasant stuff that the chemistry teacher could help them with, but its probably not in my best interests to make that joke online...).

The final think we can do as answer a phone call. These have to be handled slightly differently to texts, as while texts can always be stored for later calls pretty much need to be handled straight away.

.repeat until not message = "ready"
..wait 0.5 secs
..tell phone to "checkStatus"
..if message="ringing"
...set message to "Incoming Call"
...tell display to "show"
...set message to join "From:"phoneNumber
...tell display to "show"
...
...tell phone to "answerCall"
...

When we call "checkStatus" it will return "ringing" if there's an incoming call. We can also get the caller ID. If we want to answer the just tell the phone to "answerCall".

We can wait for the phone to either stop ringing, or for the call to end (busy status) by waiting for the status to go back to "ready":

...repeat until message="ready"
....tell phone to "checkStatus"
....wait 0.5 secs
...
...set message to "call ended"
...tell display to "show"

And that's all the bits you need to integrate phone calls and texts into you app. All of these are demonstrated in the sPhone.sniff example included in the current Sniff release (along with the necessary device files). sPhone isn't a complete phone, but rather all the code here broken into parts you can use for yourself, and a general demo of the phone functions.

Lets put them together some simple code that waits for a call, and then sends  a text back to that number back. You could use this to remotely check the status of an experiment, but it only sends you data when you (or anyone else) asks for it:

make lastPhoneNumber string

.forever
..tell phone to "checkStatus"
..repeat until message = "ringing"
...wait 0.5 secs
...tell phone to "checkStatus"
..
..set lastPhoneNumber to phoneNumber
..
..repeat until message = "ready"
...wait 0.5 secs
...tell phone to "checkStatus"
..
..set phoneNumber to lastPhoneNumber
..set message to "hello from phone"
..tell phone to "sendSMS"

Lets assume you've set everything up as before. Now we wait for the status becomes "ringing" to indicate that there's call. This also sets the phoneNumber variable, which we copy to lastPhoneNumber (strictly we don't need to do this, but its safer and clearer). Now just wait for the call to go away, and then send out a text containing whatever data you think the user wanted.

One final obligatory comment: Don't make an auto-dialler which calls random people. Only use it with your own phones, or you could make a lot of people unhappy.

No comments:

Post a Comment