LXF147 Supplement

16
NEW! 16 PAGES OF PROGRAMMING TUTORIALS The # 1 source for Linux presents... All tutorial files on this issue’s disc! Android Code a simple spirit level and get your app moving p4 Arduino Build a temperature datalogger using I 2 C p12 Python Discover how to make a front-end for useful tools p8 Projects this month... Learn new skills and start programming today

Transcript of LXF147 Supplement

Page 1: LXF147 Supplement

NEW! 16 PAGES OF PROGRAMMING TUTORIALS

The #1 source for Linux

presents...All tutorial fi leson this issue’s

disc!

AndroidCode a simple spirit level

and get your app moving p4

ArduinoBuild a temperature

datalogger using I2C p12

PythonDiscover how to make a

front-end for useful tools p8

Projects this month...

Learn new skills and start programming today

LXF147.sup_fcover 1 6/3/11 4:16:28 PM

Page 2: LXF147 Supplement

Get online…… and get more from Linux Format!

www.linuxformat.com

Download complete issues of the magazine Grab the latest LXFDVDs via BitTorrent Get new issues as soon as they go to press!

Get help from other readers Discuss the latest Linux news Read articles from previous issues Follow the Team LXF blog Or just pop in for a chat!

Don’t miss our podcast: www.tuxradar.com

Subscribe to Linux Format and get exclusive extras:

Hear the Linux Format team chew over the

hottest topics in free software!

TuxRadar

LXF147.sup_webad 2 5/31/11 4:49:22 PM

Page 3: LXF147 Supplement

Welcome

www.tuxradar.com August 2011 Coding Academy 3

Graham Morrison Editor [email protected]

More to comeThis is the last of our 16-page programming supplements. But fear

not! Thanks to your great feedback, we’re planning to add those extra 16 pages to the main magazine from next issue. That means the Coding Academy will live on as its own section within Linux Format, giving us the opportunity to explore all kinds of languages and techniques. We’ve had requests for a primer on C, for example, as well as more exotic languages like Clojure and Scala. We’ve even had a query about Modula-2! I’ve not used that for years, but it was the language of choice for our university back in the early nineties. It must still make a great beginner’s language, and similarly, we hope the Coding Academy goes on to nurture your coding abilities, whether you’re a freshman or seasoned senior.

The #1 source for Linux

Future Publishing Ltd, 30 Monmouth Street, Bath BA1 2BW Tel 01225 442244 Email [email protected]

24,487 January–December 2010 A member of the Audited Bureau of Circulations.

EDITORIALEditor Graham Morrison [email protected] editor Andrew [email protected] and communities editor Mike Saunders [email protected] writer Jonathan [email protected] editor Efrain Hernandez-Mendoza [email protected] editor Andrea BallEditorial contributors Juliet Kemp, Nick Veitch Art assistance Sally HendersonIllustrations iStockphoto

MANAGERIAL & MARKETINGBrand manager Daniel BruceCampaign manager Sharon CopelandPromotions executive Rosie DedmanPublishing director Stuart Anderton UK chief operating officer Mark Wood

SUBSCRIPTIONS & BACK ISSUESUK readers’ order line 0844 848 2852 General UK enquiries 0844 848 1602 Overseas readers +44 (0)1604 251045Online enquiries www.myfavouritemagazines.co.uk Email [email protected]

LINUX is a trademark of Linus Torvalds, GNU/Linux is abbreviated to Linux throughout for brevity. All other trademarks are the property of their respective owners. All the code printed in this magazine is licensed under the GNU GPL v2 or later. See www.gnu.org/copyleft/gpl.html.

Copyright No part of this publication may be reproduced without written permission from our publisher. We assume all letters sent – by email, fax or post – are for publication unless otherwise stated, and reserve the right to edit contributions. All contributions to Linux Format are submitted and accepted on the basis of non-exclusive worldwide licence to publish or license others to do so unless otherwise agreed in advance in writing. Linux Format recognises all copyrights in this issue. Where possible, we have acknowledged the copyright holder. Contact us if we haven’t credited your copyright and we will always correct any oversight. We cannot be held responsible for mistakes or misprints.All DVD demos and reader submissions are supplied to us on the assumption they can be incorporated into a future covermounted DVD, unless stated to the contrary.Disclaimer All tips in this magazine are used at your own risk. We accept no liability for any loss of data or damage to your computer, peripherals or software through the use of any tips or advice.Printed in the UK by William Gibbons© Future Publishing Ltd 2011

Future Publishing Ltd is part of Future plc. Future produces carefully targeted magazines, websites and events for people with a passion. We publish more than 180 magazines, websites and events and we export or license our publications to 90 countries across the world. Future plc is a public company quoted on the London Stock Exchange (symbol: FUTR), www.futureplc.com

Non-executive chairman Roger Parry Chief executive Stevie Spring Group finance director John Bowman Tel +44 (0)1225 442244 www.futureplc.com

ATLANTA • BATH • LONDON • NEW YORK • PARIS • SAN DIEGO • SAN FRANCISCO

AndroidGet your smartphone app

moving with Juliet Kemp p4

ArduinoNick Veitch builds a

temperature datalogger p12

PythonGenerate your own QR

codes with Nick Veitch p8

Welcome

Contents

LXF147.sup_contents 3 6/2/11 12:17:59 PM

Page 4: LXF147 Supplement

Android

4 Coding Academy August 2011 www.linuxformat.com

Code and all the previous tutorials

different directions – and, when you move the phone around in these ways, it means that you can get your code to work differently.

The Android API includes a bunch of different sensors which gather this information, and which you can use to inform your code, and this tutorial is going to look at the gravity sensor – and its relation to the accelerometer. But first, a closer look at Android graphics.

Let’s get moving

Juliet Kemp shows you how to make the most of your phone’s ‘mobile’ nature by coding a simple spirit level app.

In the previous two parts of this tutorial, we looked at setting up a fairly basic Android program, displaying information, and getting some user input. We also

covered a few parts of the fairly extensive Android API. This time, we’re going to start looking at some of the

really neat aspects of writing code for a portable device. Unlike a desktop, or even a laptop PC, phones are very

mobile. You can shake them around and turn them in

Juliet Kemp found moving a bubble around a screen to be a surprisingly satisfying experience.

GraphicsIn previous tutorials we used View objects to display and arrange information on the screen. Views are the basic building blocks of the user interface, handling drawing and events within a rectangular part of the screen. You can set up trees and hierarchies of Views to create more complicated layouts, and you can show drawings and animations by placing them into a View object, but Views are only really suitable for static content.

If you’re writing an app with graphics which need to respond regularly to user interaction – for example if your screen will be regularly redrawing – a better option is to use a Canvas. A Canvas provides an interface between you and the bitmap which will actually be drawn onto the area controlled by it. In this tutorial we’ll create a spirit level app, which redraws itself when the user tilts the device, so a Canvas is the best option.

Two basic ways to use a Canvas:1 Create your own custom View component,

and handle the redrawing via this. This option is fine if the app doesn’t require a lot of processor power, and doesn’t need to redraw itself very quickly.

2 Use a separate thread and a SurfaceView. This can redraw the screen as fast as the thread will go, so this is good for apps which need a fast response.

The spirit level app doesn’t need to redraw itself at high speed, so in this case, we’ll use the first option, and create a custom View component.

To set up the new project, type:android create project --target android-10 --name spiritlevel \\--path ~/android/spiritlevel --activity SpiritLevelActivity \\--package com.example.spiritlevel

(See previous tutorials, or the Android online docs, for more explanation.) Note that this time, we’re developing against Android 2.3.3 (API level 10), as the gravity sensor was only introduced in API level 9.

The first step is to set up our custom View, which will draw the basic spirit level outline. Not all the code will be included in the text so see the coverdisc for the full listing. The code for this first pass of the graphics setup, which we’ll improve later in the tutorial, is saved as SpiritLevelDrawableViewOne.java, so you’ll need to rename it SpiritLevelDrawableView.java for it to compile properly.

The code for the outer box of the spirit level looks like this:public class SpiritLevelDrawableView extends View { private LayerDrawable layer; public SpiritLevelDrawableView(Context context) { super(context); int outerX = 80; int outerY = 50; int outerWidth = 150; int outerHeight = 300; ShapeDrawable outer = new ShapeDrawable(); outer.getPaint().setColor(0xFF9933FF);

outer.setBounds(outerX, outerY, outerX + outerWidth, outerY + outerHeight); layer = new LayerDrawable(new Drawable[] {outer}); } protected void onDraw(Canvas canvas) { layer.draw(canvas); }}

A Drawable object is a catch-all class for ‘something that can be drawn’; it’s extended to classes like ShapeDrawable and LayerDrawable to define more specific things. You can also extend it yourself to create your own custom drawable objects.

The new ShapeDrawable variable, outer (the outside of the spirit level) will be constructed using the default of a RectShape, since the constructor has no other argument.

Next, we set the colour, using an RGB value, then the shape’s boundaries. See the boxout for more on colour – this one is a pleasingly garish purple, but you might have something more soothing in mind. ShapeDrawable will draw the Shape it has constructed (here, the default RectShape) to the boundaries provided by the setBounds method.

This means giving the x and y coordinates of the shape’s top left corner, and then the x and y coordinates of its bottom right corner. Basically, you specify a rectangle on the screen, and the ShapeDrawable is drawn within that.

Let’s get movingOur expert

PART 3

LXF147.sup_android 4 6/1/11 11:42:26 AM

Page 5: LXF147 Supplement

Android

www.tuxradar.com August 2011 Coding Academy 5

On the Android screen, 2D coordinates start from the top left corner of the screen and are measured in pixels. This is the case whether the display is in portrait or landscape, but which physical corner of the screen the ‘top left’ one is, will of course change depending on which way you hold your phone.

For this app, we’ll want to lock the orientation so that turning the phone around doesn’t move the spirit level. This also means that we don’t need to worry about coding our layout to behave well when the screen orientation changes.

To do this, edit AndroidManifest.xml to add a screen orientation attribute to the activity element:<activity android:name=”SpiritLevelActivity” android:label=”@string/app_name” android:screenOrientation=”portrait”>

The setBounds(Rect bounds) method is a method on ShapeDrawable, inherited from Drawable. Once outer is fully defined, the next step is to create a LayerDrawable. If we only needed to draw the single rectangle of outer, this wouldn’t be necessary, but as we’ll also want an inner box (for the spirit level bubble to bounce around in) and the bubble itself, the best bet is to use a LayerDrawable, constructed from an array of Drawable objects. It’s declared as a class variable because we’ll revisit it later in the code. The contents of the LayerDrawable are drawn on top of each other in order, the first element of the array first.

Bubble buildingFinally, a View needs an onDraw method to define what happens when it is drawn. Here, that’s straightforward: just use the draw method of the LayerDrawable:protected void onDraw(Canvas canvas) { layer.draw(canvas);}

Check the code on the disk for the inner box (the ‘liquid’ of the spirit level, that the bubble floats within). For the bubble, we’ll use an OvalShape, and define its bounds as a square, to produce a circle:bubble = new ShapeDrawable(new OvalShape());bubble.getPaint().setColor(0xFF000000);bubble.setBounds(bubbleX, bubbleY, bubbleX + bubbleDiam, bubbleY + bubbleDiam);layer = new LayerDrawable(new Drawable[] {outer, liquid, bubble});

Now the custom View is set up, the main application method, SpiritLevelActivity, needs to use it:public class SpiritLevelActivity extends Activity{ SpiritLevelDrawableView spiritlevelView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); spiritlevelView = new SpiritLevelDrawableView(this); setContentView(spiritlevelView); }}

Compile your activity with ant debug, fire up android and start a test device, install the new code with ant install -rbin/spiritlevel-debug.apk, and give it a go. Behold! A very basic graphical spirit level. Which so far, of course, does absolutely nothing of any interest. On to the next bit of the code...

Android devices come in many different sizes and screen densities; so in general it’s not a good idea to hard-code pixels

like this in your graphics code. There are a couple of ways around this, including the use of dp (density independent pixels) units, using XML layouts, and providing alternative bitmaps for different screen densities. We’ll look at some of these in later tutorials. One quick way to make things scale about right on different screens is to set android:anyDensity in your AndroidManifest.xml file:<manifest ... > <supports-screens android:anyDensity=”false” /></manifest>

This means that the system scales any absolute pixel coordinates at runtime, rather than pre-scaling them, according to the reported screen density. For now, it’s a quick way to minimise problems; but you shouldn’t release code into the wild with hard-coded pixels.

Using colo(u)rColours in Android are defined as packed ints, with four bytes, one each for alpha (transparency), red, green and blue, each in hex. An alpha value of FF is opaque (so 00 would be entirely transparent). Opaque red would be 0xFFFF0000; opaque black 0xFF000000.

The Color class gives you a few constants, such as Color.BLACK and Color.YELLOW, but that’s a pretty limited colour palette, so you’ll probably want to make at least some use of the packed integers.

However, having bare ints like this in code isn’t a great idea – it’s not very maintainable. Instead, you can store your color definitions in a resources file, such as res/values/colors.xml (the filename doesn’t matter, although it does need to be in

the res/values/directory):<?xml version=”1.0”

encoding=”utf-8”?><resources> <color

name=”brightpurple”>#ff9933ff</color> <color

name=”brightyellow”>#ffffff00</color></resources>

You can then refer to these resources in your code like this:Resources res = getResources();outer.getPaint().setColor(res.getColor(R.

color.brightpurple));If you’re using one of the colours

that does have a constant from Color assigned to it, it’s good practice (and saves code lines) to use that instead of self-defining your colour:bubble.getPaint().setColor(Color.

BLACK);

Calculating your graphics values.

If adb says ‘device offline’ the first time you try to install your test code, just run it again.

Quicktip

LXF147.sup_android 5 6/1/11 11:42:28 AM

9000

Page 6: LXF147 Supplement

Android

6 Coding Academy August 2011 www.linuxformat.com

When the device is at rest, the output of the accelerometer and the gravity sensor should be identical. So if you want to test this code on a device running Android 2.2 or earlier, which doesn’t have a gravity sensor, just swap TYPE_ACCELEROMETER for TYPE_GRAVITY in the code, and make sure you hold it still when testing.

Quicktip

Android has a bunch of different hardware sensors: accelerometer, gravity, gyroscope, light, linear acceleration, magnetic field, pressure, proximity, rotation vector and temperature. Each can be represented by the Sensor class, and accessed by the SensorManager class. Sensor events are represented by the SensorEvent class, which holds data (including timestamp and accuracy) about each sensor event.

The SensorEvent class uses a different set of coordinates from the 2D graphics engine. The origin is in the middle of the screen; the x-axis is horizontal (across the screen) and points right; the y-axis is vertical (up and down the screen) and points up; and the z-axis runs through the middle of the phone and points outwards from the front of the screen.

To see how the Sensor, Management, and Event classes work together, let’s set up the gravity sensor:public class SpiritLevelActivity extends Activity implements SensorEventListener { private SpiritLevelDrawableView spiritlevelView; private SensorManager manager; private Sensor gravity; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); spiritlevelView = new SpiritLevelDrawableView(this); setContentView(spiritlevelView); manager = (SensorManager)getSystemService(SENSOR_SERVICE); gravity = manager.getDefaultSensor(Sensor.TYPE_GRAVITY); }

The first section of the code sets up the custom View. Then the getSystemService method (inherited from the Activity class) gets the sensor manager service, which we can then use to get a gravity sensor.

Talking sensorsYou could leave the sensors on all the time, but that uses up the battery alarmingly fast. It’s good practice to turn them off when the activity is paused, and pick them back up on resume: protected void onResume() { super.onResume(); manager.registerListener(this, gravity, SensorManager.SENSOR_DELAY_GAME); } protected void onPause() { super.onPause(); manager.unregisterListener(this); }

Alternatives for the sensor delay rate are SENSOR_DELAY_NORMAL and SENSOR_DELAY_FASTEST, but this one should work OK for our purposes. Finally, we need to do something when the sensor data changes: public void onSensorChanged(SensorEvent event) {spiritlevelView.invalidate(); }

All this does is redraw the spirit level View. Which is great, but what we want is for that View to reflect the data coming in from the sensor, which right now it doesn’t. So the next step is to grab that data and to do something useful with it.

The sensor returns an array of three values, representing a 3D vector (x, y, z) showing the direction and magnitude of gravity. First, let’s ignore the x-value and look only at tilt in the y direction (tilt up and down; the x-direction is side-to-side).

If the device is lying absolutely flat, then gravity acts straight down the z-axis, meaning that the z-value is 9.81 (9.81m/s^2, the magnitude of gravity on the Earth), and the y value is zero. If, on the other hand, the device is balancing on its top edge, the gravity runs straight along the y-axis, so the y-value is 9.81 and the z-value is zero. If the device is anywhere between those two states, then y and z will be somewhere between those two sets of values.

The easiest option is to map the y-value directly onto the location of the bubble, and redraw it accordingly as the device’s gravity alignment changes.

Let’s put a method into SpiritlevelDrawableView to do that. First, we’ll have to move all the bubble’s dimensions outside the main method, so that we can change them from the moveBubble() method. This defines the bubble’s default position, in the centre of the inner box. Now, create a new moveBubble() method:protected void moveBubble(float gravY) { bubbleY = bubbleOrigY - (int) (gravY * 5);}

and redraw the bubble in the onDraw() method (this is called when invalidate() is called on the View): protected void onDraw(Canvas canvas) { bubble.setBounds(bubbleX, bubbleY, bubbleX + bubbleDiam, bubbleY + bubbleDiam); layer.draw(canvas); }

In moveBubble, the sensor value is passed in as a float, but must be cast to an int before saving it in bubbleY, as the setBounds method called in onDraw requires ints.

Also, because the y-axis for the graphics goes up in value as you move down the screen, to get the bubble moving in the correct direction, the gravity value must be subtracted from its origin. (Feel free to experiment with this to find out for yourself.) Finally, multiplying by five gives us more

Testing the spirit level.

LXF147.sup_android 6 6/1/11 11:42:28 AM

Page 7: LXF147 Supplement

Android

www.tuxradar.com August 2011 Coding Academy 7

Next timeOver the next few tutorials, we’ll look at the Android UI, and using the touch-screen interface; graphics, screen layout, and games; handling threading in Android; and the accelerometer and other hardware sensors.

movement in the bubble – as the sensor value has a min of 0 and a max of 9.81, moving the bubble by only those bare values would make for a very small movement (only 10 pixels in either direction).

Values and loggingTo find out what values the sensors are returning, and to experiment with pixel values for drawing, you might want to try some logging and debugging. To add a log line in your code, use this:/* this line at top of class */private static final String TAG = “SpiritLevelActivity”; /* this line where you want your debugging */ Log.i(TAG, “onSensorChanged() “ + event.values[0] + “ “ + event.values[1] + “ “ + event.values[2] + “ “ + gravY);

Fire up ddms from the command line, and run your app. In the left-hand pane of ddms, you’ll see process info for any devices and emulators you have hooked up. Click on the device you want to debug, run your activity, and debugging info will appear in the pane at the bottom. This is obviously particularly handy if you’re experiencing run-time crashes, as it’ll give you a stack trace.

Now we’ll get back to the SpiritLevelActivity class. Grab the sensor information when the sensor changes, call this method, then redraw the View:public void onSensorChanged(SensorEvent event) { spiritlevelView.moveBubble(event.values[1]); spiritlevelView.invalidate();}

Unfortunately, you can’t use the device emulator to test this stuff, as it doesn’t provide fake sensor data. You’ll need to hook your device up to your laptop and test that way. See the last tutorial, or http://developer.android.com/guide/developing/device.html for details.

Once the y-values work, you can put in very similar code for the x-values; although in this case, you should add rather than subtract the gravity value in moveBubble() to get movement in the correct direction:bubbleX = bubbleOrigX + (int) (gravX * 5);

Calculating graphicsAt this point, there’s still quite a lot of hard-coded graphics values, and they’re mostly calculated by trial and error. While we won’t take all of the pixel values out, we can make it all fit together a little bit better.

The liquid box needs to have enough room for the bubble to move around in, but no more than that – when the phone is tipped all the way in one direction, the bubble should be at the very end of the box (exactly as you would see with a real spirit level).

Happily, we know exactly how much the bubble will move in each direction: the maximum possible of the raw gravity value (gravX or gravY), multiplied by five as our magnifying factor. It’s bad practice to have the magnifying factor in the code, as well, so:private static final double WIDTH_MAGNIFY = 1.5;private static final double HEIGHT_MAGNIFY = 3;[ ... ]int liquidWidth = (int) ((SensorManager.STANDARD_GRAVITY * WIDTH_MAGNIFY * 2) + bubbleDiam + 0.5);int liquidHeight = (int) ((SensorManager.STANDARD_GRAVITY * HEIGHT_MAGNIFY * 2) + bubbleDiam + 0.5);

I think these values for the WIDTH_MAGNIFY and HEIGHT_MAGNIFY constants look best onscreen, but you can of course change them as you prefer. SensorManager.STANDARD_GRAVITY does exactly what you’d expect: provides the standard gravity value, which is the maximum you’ll get for gravity in either direction. As well as allowing for the movement, there also needs to be room for the bubble itself; and the 0.5 ensures that any decimal value will be rounded up rather than down.

Similarly, we can use the hard-coded pixel values for the outer case of the spirit level to define the top-left x and y coordinates of the liquid and the bubble:int liquidX = outerX + (outerWidth-liquidWidth)/2;int liquidY = outerY + (outerHeight-liquidHeight)/2;bubbleX = bubbleOrigX = outerX + (outerWidth/2) - (bubbleDiam/2);bubbleY = bubbleOrigY = outerY + (outerHeight/2) - (bubbleDiam/2);

This will centre the liquid and the bubble in the middle of the outer case. The moveBubble and onDraw methods will then move the bubble around from there.

Recompile, install, and try it out, and you should see the little bubble moving around the screen as you tilt the phone. Add some graphics to draw a cross centred in the liquid box, and see if you can put a shelf up straight with it! LXF

The axes of the Android sensors.You can easily drop a pre-created image file into your application by saving it (as a PNG or JPG) in res/drawable and then referring to it with the resource ID R.drawable.filename (no extension).

Quicktip

LXF147.sup_android 7 6/1/11 11:42:29 AM

Page 8: LXF147 Supplement

Python Python

8     Coding Academy August 2011 www.linuxformat.com

Python Python

Tutorial code qrencode 3.1.1

within your Linux scripts. If you are a veteren of Python tutorials, you may be used to using os.popen(), os.spawn() or other methods for the same job. These still work, depending on your version, but the subprocess module was made to simplify the mess of all the different methods of running subprocesses that existed before. It still uses a similar syntax to the old popen()subprocess.Popen([‘ls’,’-al’])

Given a list of arguments, the first one will be assumed to be the executable, and the remaining ones will be supplied as arguments to it when run. Running this code will give you a directory listing, but you will need to press Ctrl+C to get back to Python. Processes are spawned in a new thread, and the main program will continue to execute as normal. To make the Python script wait until the subprocess has terminated, you can call the objects wait() method:>>> subprocess.Popen([‘sleep’,’50’])<subprocess.Popen object at 0xaec710>>>> x=subprocess.Popen([‘sleep’,’50’])>>> x<subprocess.Popen object at 0xaec726>>>> x.wait()[... some time goes by...]

QR code generator Nick Veitch harnesses Python to the power of the command line and shows you how to make a front-end for useful tools.

Python is fantastic, but it can’t do everything, at least not by itself. Most of the code written for Linux is in the C programming language (the language of the kernel)

for very good reason – it is fast. That means that there are a lot of bits of code for doing things that we would like to be able to access from Python.

In many cases, Python modules are created that provide a link between the Python interpreter and the code (usually a library file) in question. In fact, quite a few Python modules are more or less wrappers for this type of system library, which means that you can call them and their functions from within Python as though they were native applications.

Sometimes though, you want to be able to use code that doesn’t have a convenient Python wrapper. Of course, if you had the time, you could generate your own Python module to encapsulate this functionality, but it is a time-consuming task that requires a degree of knowledge of both C and Python, so that may not be the best option for a quick script.

In which case, you need the alternative – subprocess

Popen sourcePart of the main Python install is a module called subprocess, which is dedicated to the task of running other software from

Your mission is to generate ‘QR’ codes from within Python. It’s easy though, because as usual, we’ll be cheating.

that may not be the best option for a quick script.

Part of the main Python install is a module called which is dedicated to the task of running other software from

You will need Python and the PyGTK packages which are usually installed by default on most distros anyway. You will also need the qrencode software and library, provided as source on the DVD, but also widely available from repositories.

You will need...

PART 3

When launching LXF, only a set of Nick Veitch’s Bash scripts kept the magazine going. Then they were replaced with ‘people’, a retrograde step in his opinion…

Our expert

LXF147.sup_python 8 6/1/11 4:48:39 PM

Page 9: LXF147 Supplement

Python PythonPython Python

www.tuxradar.com August 2011 Coding Academy     9

0>>>

This is good, and there are various other methods to control the spawned process, such as sending signal or collecting output, or supplying more input.

Subprocess also includes some convenience functions to make it easier to do what you want. For our purposes, using check_call() will be best. This accepts commands in the same format, but by default it waits for the process to finish before continuing, and throws an exception (error message) if something goes wrong. >>> cmd=[“qrencode”, “-o”]>>> cmd.append(‘output.png’)>>> cmd.append(‘”’+text+’”’)>>> subprocess.check_call(cmd)0

That’s all there is to calling this command from within Python, but so far, we are no better off than running it directly from the commandline. We need to build a graphical interface.

Windows and widgetsWe have talked about writing object-oriented code and using classes before in this Python series (see LXF145). A lot of the time we can import ready-made classes to make our job easier, and that is definitely the case with PyGTK. This module wraps up the whole GTK toolkit libraries, making it fairly trivial for us to create our own GUI. Don’t believe me? try this:>>> import pygtk, gtk>>> window = gtk.Window(gtk.WINDOW_TOPLEVEL)>>> window.set_size_request(200, 160)>>> window.show()

Pretty simple isn’t it? Of course, this window doesn’t actually do anything. In fact, it won’t even close, because we neglected to tell it how to do that. But all we have done here is created a Window object and told it to show itself, the magic classes of PyGTK have taken care of actually drawing it and presenting it to the window manager to deal with.

A window is only half the story though, because we actually need to display things within the window for it to be of any use to us.

The things we will add are known as widgets, and they comprise the half dozen regular controls you use every day when running software – and quite a few more that get used less often. We aren’t going to delve too deeply into the bestiary of drawable widgets, because we only really need two – something that will let us input text, and a button to press when we are done. Oh, and a place to display our generated code.

It should come as no surprise that these widgets have classes to define them too. In fact, there is a whole treeful of classes, because widgets inherit properties from more primitive classes required to open up all of the functionality of GTK. Some of the widgets are sort of invisible – they act as organisers and containers for the other widgets. If we create a vertical box, we can stick widgets into it, and the box will take care of sizing them within the available space:>>> vbox = gtk.VBox(False, 0)>>> window.add(vbox)>>> vbox.show()

The VBox constructor takes two arguments – a boolean value to determine if it is homogenous (all the contents share the space equally) and a padding value for the space between widgets. Of course, even though we have added the box, we still can’t see anything. >>> entry = gtk.Entry()>>> vbox.pack_start(entry, True, True, 0)>>> entry.show()>>> button = gtk.Button()>>> vbox.pack_start(button, True, True, 0)>>> button.show()

This should finally display our less than awe-inspiring widgets. The vbox.pack() method simply adds our widgets into the vbox container – the extra three arguments are two booleans for ‘expand’ and ‘fill’ and a padding value. These

control the behaviour of the widgets when the parent gets resized (ie, do they expand to fill the space and if so, do they resize or just pad the space). When we call the show()

method on the widgets, they are finally drawn to the screen.Those two widgets set out what we planned to do. The

image? Aha! well, to save space and be a little cooler, we can add the image to the button:buttonpic=gtk.Image()buttonpic.set_from_file(“/home/evilnick/myimage.png”)button.add(buttonpic)buttonpic.show()

So, now we have a means of entering text, a means of generating a 2d code graphic, and a way to display it on screen. All that is needed is some way to link all this together.

Handling eventsEach part of our GUI, from the main window to the individual widgets, has events attributed to it. The type of available events depends on the type of object, but usually they are things that react to the user.

For example, a button widget has a ‘clicked’ event. When the user clicks on the button, the GTK library code interprets that action and sends out an event signal to let you know what has happened. What you do with that event is up to you – although the user might expect clicking on a button marked ‘Close’ to close the current window, you can make it do anything you like.

Playing around with GTK from Python’s interactive shell can be a great way to experiment and learn.

“Widgets comprise the half-dozen regular controls you use daily.” If you aren’t sure

what a Python object (function, variable, module) is or can do, use the built-in functions print(), type() and dir() to find out some useful info.

Quicktip

LXF147.sup_python 9 6/1/11 4:48:39 PM

Page 10: LXF147 Supplement

Python Python

10     Coding Academy August 2011 www.linuxformat.com

Python Python

Event codes are ‘trapped’, or in normal speak, ‘noticed’, by explicitly telling the widget to perform some action. Effectively, we just write some code and then tell the widget to execute it when it notices a particular event.

For example, when we click on our button, we want the program to save our image file somewhere. We actually need to open up a file requestor to do that, but that is easy, because that is a really common thing to do, and PyGTK already has classes that can generate such things with the minimum of fuss:>>> def save_something(button):... x=gtk.FileChooserDialog(title=”Save code image as...”, \... action=gtk.FILE_CHOOSER_ACTION_SAVE, \... buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,\... gtk.STOCK_OK, gtk.RESPONSE_OK))... rc = x.run()... print x.get_filename()... x.destroy()... >>> save_something(button)/home/evilnick/test.png>>>

The FileChooserDialog is a stock class for GTK which brings up a standard file requestor dialog window. The action defines what it is supposed to do (in this case save a file) and the buttons are also stock objects for common functions (OK, Cancel, etc). We just pass these keyword parameters in and then call the run() method to generate the file requestor. In this case it just prints out the filename we chose, but you can see that the buttons also have response conditions, so we could check these to perform (or not) any task.

We get to run this code by attaching this method to the event handler for our button like this:button.connect(“clicked”, save_something)

As even functions in Python behave like objects, we just use the function name as an argument (you don’t need brackets), which links it to the button in our Windows via the ‘clicked’ event. Now when you click on the button, the file dialog should pop up.

Events are the key for making our 2D code generator more awesome than any similar software. The text entry widget that we are using has various events, and one very useful one (inherited from gtk.Editable) called “changed”. Normally, you wouldn’t bother reading a text entry until someone had pressed a button somewhere to submit the value, but because the object generates a signal every time it changes (ie, someone presses a key and enters more text) we can trap that signal and cause it to update our 2D code. All we need to do for this is to write some code which:

reads the current text shoves this out to our commandline code generator reads the image back in again updates the image object in our user interface

It might sound like quite a bit of work, but it is pretty straightforward considering we have already written the code to call the external executable.

So if we wrap up all our code into a class and build an application out of it, it should look something like this:#!/usr/bin/env pythonimport pygtkpygtk.require(‘2.0’)import gtkimport subprocess, osclass CodeGenerator():

Documentation is a good thing. There is so much to cover in PyGTK, that you should really bookmark the site.

LXF147.sup_python 10 6/1/11 4:48:40 PM

Page 11: LXF147 Supplement

Python PythonPython Python

www.tuxradar.com August 2011 Coding Academy     11

def enter_update(self, widget, entry): self.generate_code(entry.get_text()) self.image.set_from_file(self.tfilename) os.remove(self.tfilename) self.image.show() def save_image(self,button): x=gtk.FileChooserDialog(title=”Save code image as...”, \ action=gtk.FILE_CHOOSER_ACTION_SAVE, \buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,\ gtk.STOCK_OK, gtk.RESPONSE_OK)) response=x.run() if response == gtk.RESPONSE_OK: p=self.image.get_pixbuf() p.save(x.get_filename(), “png”) x.destroy() def generate_code(self, text): calltxt=[“qrencode”, “-o”] calltxt.append(self.tfilename) calltxt.append(‘”’+text+’”’) subprocess.check_call(calltxt) def __init__(self): # generate a temporary file to hold the image self.tfilename=”/tmp/qrtemp.png” self.image=gtk.Image() self.generate_code(“x”) self.image.set_from_file(self.tfilename) self.image.show() # create a new window window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_size_request(200, 160) window.set_title(“2D code generator”) window.connect(“delete_event”, lambda w,e: gtk.main_quit()) vbox = gtk.VBox(False, 0) window.add(vbox) vbox.show() entry = gtk.Entry() entry.set_max_length(60) entry.connect(“changed”, self.enter_update, entry) vbox.pack_start(entry, True, True, 0) entry.show() button = gtk.Button() button.add(self.image)

button.connect(“clicked”, self.save_image) vbox.pack_start(button, True, True, 0) button.show() window.show()

def main(): gtk.main() return 0

if __name__ == “__main__”: CodeGenerator() main()

The gtk.main() call simply hands over control of our app to the GTK main loop, which processes all the events and manages the objects without us having to worry about them. When this loop receives a quit signal (we added a signal to the main window in this version), it tidies up and exits.

As we have built a class, Code.generator, all the different bits of code we have been working on now fit into that structure. It does make it a little easier for us to reference other properties of the object, like filenames and images.

Going furtherNow you have seen how easy it is to add a GUI front-end to your commands, you can try it yourself on other things that would be nicer to run from the desktop. Start out by thinking what information you will need to enter for the command, and how the output is going to be dispayed (is it an image, a number or just text?). These will determine what widgets you need to add – then it’s just a matter of plumbing! LXF

If you can’t find a module you want packaged for your distro, you can always use easy_install from the Python setup tools to fetch it and install it safely.

Quicktip

Temporary filesAs Linux is very much a file-based operating system (even your devices are just files as far as the OS is concerned), temporary files are a useful and often necessary part of many programs. A temporary file provides short-term storage for data or resources that may be required for the lifetime of an application, but not permanently.

Python has an excellent module which can generate temporary files on the fly, named or with a random extension, and automatically delete them when you no longer need them. 

This is good because temporary files are often a security risk. If you know the name and location of a file, you may be 

able to alter it for your own evil ends. Using random files and removing them is good practice. Find out more at http://docs.python.org/library/tempfile.html.

So, if it’s so great, why didn’t we use it? It is possible, but it would require a bit of a workaround, because in our case writing to the file involves an external application. Generating a tempfile within Python and getting an external tool to write to it is a bit trickier. 

As it is, we could generate our own random name, and we do already delete the file as soon as we read it, so it is probably as secure as a 2D barcode generator needs to be...

You don’t need a full IDE to develop good Python code – most basic text editors have things like syntax highlighting and code folding.

LXF147.sup_python 11 6/1/11 4:48:40 PM

Page 12: LXF147 Supplement

Arduino

12 Coding Academy August 2011 www.linuxformat.com

talking about can’t actually operate as a bus master anyway.For most devices, the setup couldn’t be simpler. Consider

an I2C EEPROM device like the 24C64. This is a 64k EEPROM arranged as 8192x8bit bytes and comes in various shapes of 8-pin packages. There are the power supply lines, the SCL and SDA lines for communication, a Write Protect input, and three address lines. These are not tied to some address bus you need to manipulate, but to the +ve supply or Ground, and give the chip a hardwired 7-bit address it can use to identify itself. This is useful if you want more than one chip of the same type.

The first four bits of the device address are pre-encoded into the chip (which begins 0101 or 0x50 for EEPROM chips), and the last bit is used to signify a read or write operation. The address lines float low though, so if you want to attach this chip to an Arduino, you only need to give it the power supply and the two I2C control connections.

Down to the wireOn the Arduino side of things, it isn’t too difficult to set up either. There is a library called Wire that is part of the base Arduino distribution, which is for handling I2C communication. As this is a general communications library we still have to read the specific datasheet for our device (http://www.atmel.com/dyn/resources/prod_documents/doc0336.pdf) to see what it expects.

This is an area which can cause some confusion, because Wire is pretty much a ‘black box’ unless you take the time to delve into the C code.

As a general rule, all the actual protocol parts are handled by Wire, you just need to attend to the specific modes of operation for your device. For example, our

EEPROM has different read and write modes. For reading a byte, we need to first do a dummy “write” operation by sending the device and memory address and closing or acknowledging it. Then we listen for the byte being transmitted. In this case, Wire takes care of the handshakes and stuff, but we have to send the actual bytes for the address, close the connection and then listen out for the byte. It is easier to follow in code! This sketch outlines a couple of the read and write modes as functions which depend on Wire.

Long ago in an office far away, some people created the communication protocol we now know as I2C. The idea was to build a simple data interface that could easily be

used by embedded devices with the minimum of resources, and yet provide fast and reliable transfer of data. There are a number of derivations and different implementations of I2C, but they all work more or less the same way.

Technically, this is a two-wire interface – it only needs two connections to transmit and receive data. These are called SDA and SCL. The data line is SDA and goes high and low and transmits the data bi-directionally across this line. SCL is the ‘clock’ and is used to signal when data is being transmitted. The clock is not a regular pulse, but needs to be manipulated to ensure correct transmission of the data.

Because of its relative simplicity, this technology is in widespread use: most of the sensors on your motherboard probably use I2C to signal temperature, fan speed and other data. There is a huge range of I2C devices, including real-time clocks (RTCs), sensors (magnetic, temperature, accelerometers) and memory devices (NVRAM and EEPROMs), and you can interface them all to your Arduino.

An I2C interface is a master-slave affair, because something needs to control the actual bus. In our case, that will normally be the Arduino, because it is easier to control what goes on that way, and many of the devices we will be

“Its relative simplicity means the technology is widely used.”

The code and latest software sources.

Build a dataloggerNick Veitch delves into the magic of the I2C interface and builds a temperature datalogger with only four components.

and yet provide fast and reliable transfer of data. There are a number of derivations and different implementations of Ibut they all work more or less the same way.

connections to transmit and receive data. These are called SDA and SCL. The data line is SDA and goes high and low and transmits the data bi-directionally across this line. SCL is the ‘clock’ and is used to signal when data is being transmitted. The clock is not a regular pulse, but needs to be manipulated to ensure correct transmission of the data.

simplicity, this technology is in widespread use: most of the sensors on your motherboard probably use Idata. There is a huge range of Iclocks (RTCs), sensors (magnetic, temperature, accelerometers) and memory devices (NVRAM and

An Arduino (any will be sufficient) and for this project:

an I2C EEPROM chip (24C64 preferred) an I2C real-time clock (DS1307 preferred) 32.768kHz crystal (watch crystal) linear sensor (MCP9701 / LM35)

You will need...

Last month We looked at simple sensors and how to attach them.

There are a huge range of I2C devices and you can interface them all to your Arduino.

When launching LXF, only a set of Nick Veitch’s Bash scripts kept the magazine going. Then they were replaced with ‘people’, a retrograde step in his opinion…

Our expert

PART 2

LXF147.tut_arduino 12 6/1/11 11:51:26 AM

Page 13: LXF147 Supplement

Arduino

www.tuxradar.com August 2011 Coding Academy 13

#include <Wire.h> //I2C libraryint DEVICETYPE = 0x50; //24c64 eeprom default address valueboolean eeprom_write_rand( int device, unsigned int location, byte data ) { Wire.beginTransmission(device); int MSB = location >> 8; int LSB = location & 0xFF; Wire.send(MSB); Wire.send(LSB); Wire.send(data); Wire.endTransmission(); delay(10);}boolean eeprom_write_page( int device, unsigned int location, byte* buffer, int length) { //NB - wire library is currently hardcoded to a buffer length of 30 bytes. I recompiled mine) Wire.beginTransmission(device); int MSB = location >> 8; int LSB = location & 0xFF; /* For 24c64 EEPROM xxx1111111100000 = page address = 0x0f 0xe0 xxx0000000011111 = byte address = 0x1f */ Wire.send(MSB); Wire.send(LSB); int byteaddress = LSB & 0x1F; if (byteaddress + length > 32) { return false; // no writing over page boundary } int i; for ( i = 0; i < length; i++ ){ Wire.send(buffer[i]); // send bytes } Wire.endTransmission(); return true;}byte eeprom_read_rand( int device, unsigned int location ) { byte data = 0xFF; Wire.beginTransmission(device); int MSB = location >> 8; int LSB = location & 0xFF; Wire.send(MSB); Wire.send(LSB); Wire.endTransmission(); Wire.requestFrom(device,1); if (Wire.available()) data = Wire.receive(); return data;}boolean eeprom_read_seq( int device, unsigned int location, int length, byte *readdata) { // check for valid input if (length > 32) return false; //longest sequence possible Wire.beginTransmission(device); int MSB = location >> 8; int LSB = location & 0xFF; Wire.send(MSB); // fake write to set the address Wire.send(LSB);

If you missed last issue Call 0870 837 4773 or +44 1858 438795.

Wire.endTransmission(); Wire.requestFrom(device,length); // now read back int i; for ( i = 0; i < length; i++ ){ if (Wire.available()) readdata[i] = Wire.receive(); } return true;}

Writing these as functions means we can call them from our code easily enough. If you collect together enough of them, it may be worth hiving them out to a library. An intelligent blanking routine might be useful, for example one which reads the data a page at a time and sees whether it needs to be written to be blanked. Each write operation decreases the lifespan of all the bytes in the page remember, so you don’t want to write a byte at a time if you can help it. On the other hand, most modern EEPROMs have long lifetimes, so maybe you don’t care!

Now we need to write some code to use these functions. The all but mandatory setup() function is a good place to write some data to the device. In this case we will just fill an array full of bytes and then dump it to the first page address.void setup() { int eprom1= DEVICETYPE; byte data[]={ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 }; Wire.begin(); // initialise the connection Serial.begin(9600); int rc = eeprom_write_page(eprom1,0,(byte *)data, 32); Serial.println(“Memory written”);}

For our next trick, we will read back the memory and dump it out through the serial connection. Open up the serial monitor in your Arduino software and make sure the baud rate is set to the same value as you used in the setup above.

Courtesy of Fritzing, here’s how our datalogger should look on a breadboard. Flying leads to the sensor mean it can be placed anywhere you want to measure.

Not sure about your supply voltage? With some processors, you can measure it with the Arduino itself! See https://code.google.com/p/tinkerit/wiki/SecretVoltmeter

Quicktip

LXF147.tut_arduino 13 6/1/11 11:51:26 AM

Page 14: LXF147 Supplement

Arduino

14 Coding Academy August 2011 www.linuxformat.com

The Serial library can display bytes of data in a number of different ways. In this case we can use it to provide a hex dump of the memory and also turn it into character format. void loop() { int eprom1 = DEVICETYPE; //I2C address of EEPROM boolean rc =true; //used for return codes int location =0; //first address byte buffer[32]; // a buffer full of bytes byte b1 = 0xFF; while (location!=80) { b1 = eeprom_read_rand(eprom1, location); //read a byte Serial.print((char)b1); //print to serial port location++; //increase address } Serial.println(“ “); rc = eeprom_read_seq(eprom1,0,32,(byte *)buffer); if (!rc) Serial.println(“Read Failed!”); else Serial.println((char *)buffer); Serial.println(“dumping byte data as hex”); int i; for ( i = 0; i < sizeof(buffer); i++ ){ Serial.print(buffer[i], HEX); Serial.print(“ “); if (buffer[i] < 16) Serial.print(“ “); } Serial.println(“ “); for ( i = 0; i < sizeof(buffer); i++ ){ Serial.print(buffer[i], BYTE); Serial.print(“ “); } Serial.println(“ “); delay(2000);}

If your code does not spit out the correct data – well, there is a reason. When the Wire library was written, to save space it was given a 32-byte buffer. That is fine, but the address has to go in there too, so really you only have 30 bytes. Any more than that just gets lost! This is unfortunate, because we can use all 32 bytes of a page. The solution is to hack the Wire library – the buffer size is determined by two DEFINE statements at the beginning of Wire.h and Twi.h in the relevant library folder of your Arduino code. Since version 19, the library code gets recompiled when you compile or upload your sketch, so you

don’t have to do anything extra to make it work, although you should beware that if you do change it, your application may not be so portable to other users, and we make no guarantees it will work at all if you mess around too much.Logged data isn’t much use unless we know when it was logged – that calls for a real-time clock (RTC). The DS1307 is probably the most common, and there is plenty of info on driving these from an Arduino. It’s an I2C device, so we can just tag it on to the same bus we use for the memory.

The I2C bus does have limits, but if you keep it short, there isn’t even any need to add the suggested extra capacitance or pull-up resistors, so the only extra component required is a crystal used for the clock’s oscillator. This is a 32.768kHz device, which is pretty common for timekeeping applications, so you will find an abundance of them available at low cost.

The clock behaves almost like the memory device: date and time are stored in byte registers that you can address in a very similar way to the EEPROM. Read the bytes and do a bit of maths to convert them into something sensible if you like.void getTime() { // Reset the register pointer Wire.beginTransmission(DS1307); Wire.send(0x00); Wire.endTransmission(); Wire.requestFrom(DS1307, 7); // mask out control bits from some of the registers second = bcdToDec(Wire.receive() & 0x7f); minute = bcdToDec(Wire.receive()); hour = bcdToDec(Wire.receive() & 0x3f); // Need to change this for am/pm dayOfWeek = bcdToDec(Wire.receive()); day = bcdToDec(Wire.receive()); month = bcdToDec(Wire.receive()); year = bcdToDec(Wire.receive());}

You will obviously need to set the time as well. The DS1307 supports a battery backup supply, so if you properly attach a button cell, you can get away with doing this just once. For details of this, check out the fully commented code on the DVD or visit http://combustory.com/wiki/index.php/RTC1307_-_Real_Time_Clock

In hot pursuitWith a means of timestamping and recording data, now we need something to actually measure. We looked at LDRs in the last tutorial. This time we will have a bash at temperature (you may obviously want to record something else entirely).

I used to believe that getting a packaged-up temperature IC was a waste of time, money, bus capacity and breadboard space when a thermistor could be just as good.

Thermistors do require a bit of extra cunning though, and can be harder to calibrate. A good compromise is a linear temperature module like the MCP9701. This tiny package has three legs and works as an all-in-one temperature divider, giving a linear output of 19.5 mV per degree C, starting at 400mV for freezing point. This means we can easily use the analogue input of the Arduino to generate a reading with some simple maths. The LM35 range is also a good choice, though pricier – if you want to use that instead, you’ll have to adjust the calculations according to the datasheet.

A quick aside on accuracy – the MCP9701 is only quoted at 2C. However, it is usually a lot more accurate between 10 and about 40C, which is good for room temperature measurements. Assuming a 5V supply, the Arduino can measure down to 5mV, so we are looking at an accuracy of

Connect the logger to the USB port and press the button for a data dump, which you can copy and paste into a file or spreadsheet.

LXF147.tut_arduino 14 6/1/11 11:51:27 AM

Page 15: LXF147 Supplement

Arduino

www.tuxradar.com August 2011 Coding Academy 15

around 0.3C in the reading. Another advantage is that as it is not a bus device, we can mount it a significant distance away from the Arduino, so it isn’t measuring the temperature of your board, but whatever you stick it to.

Talking of accuracy, you should also know that the Arduino uses it’s own 5v line as the comparison when doing the analogue to digital conversion. That’s fine if it is actually 5v. If it is 4.8v, as many USB 5v lines really are, then you can see that you may need to adjust some calculations.

a. reading = 300: ((5/1024) x 300)= 1.44 (1.44-0.4)/0.0195 = 53.3 Cb. reading = 300: ((4.8/1024) x300 = 1.40 (1.40-0.4)/0.0195 = 51.6 CAs you can see, that’s a difference of nearly 2 degrees

(about the same as the stated accuracy, which means your results could be -4C out!) from a tiny variation in the sensor voltage. After we have read the analogue pin, we can easily convert to centigrade with a function like this:int convertAnalogueToTemp(int reading) { float mvperunit = 4.687; return(((int)((reading*mvperunit)-400)/.195));}

The number we end up with is very large because we have effectively multiplied it by 100, so 2345 is really 23.45C. This is because it is easier to store integers than floating point numbers. Our adjusted value will easily fit in two bytes of space and it will take a very expensive sensor to require much more accuracy. The size matters – read on!

What to record?It would be nice if our chunks of data would fit into pages of our EEPROM. But the memory only has 256 pages of 32 bytes, so we need to be more economical. A nice target would be to get all the info in 8 bytes, which would give us four recordings per page, and 1024 altogether; for temperature data, we actually only need two bytes to store it with more accuracy than we need. That leaves us six bytes for the datestamp. A byte each for day, month, year, hour, minute and second will be enough.

You could compress it into a unix-style datestamp (number of seconds since 1 Jan 1970) which would yield a ‘long’, a four-byte integer. However, using a slightly expanded format means data will be pretty much human readable – if space matters that much, you can find an alternative method. As it is, with 8 bytes per reading, we can conveniently fold four data points in each page of the memory. To preserve our EEPROM a little, we can store the results up and write a page at a time, so we should construct a loop something like this:void loop() { int offset =0; int i; int val; blockcount =0; while (blockcount<4){ ///read temperature data sensorData = analogRead(sensorPin); temp =convertAnalogueToTemp(sensorData); ///pause for next reading for ( i = 0; i < delta; i++ ){ delay(650); digitalWrite(LED, HIGH);

delay(200); digitalWrite(LED, LOW); // we should check if the ‘dump’ key is pressed val = digitalRead(BUTTON) if (val) dumpdata(); } getTime(); // read RTC ///package up bytes offset = 8 * blockcount; data[offset] = (byte) day; data[offset+1]= (byte) month; data[offset+2]= (byte) year; data[offset+3]= (byte) hour; data[offset+4]= (byte) minute; data[offset+5]= (byte) second; data[offset+6]= (byte) (temp >> 8); //msb of temp data[offset+7]= (byte) (temp & 0xFF); blockcount++; } rc = eeprom_write_page( eprom1, addr*32, (byte *)data, 32); addr++; if (addr > 255) addr =0;}

The code here is really straightforward. Using the offset as a counter to our data array, we fill up 32 bytes with data and write it all in one go to a page of the EEPROM. It just keeps recording, so when it gets to the last page it will start again at the beginning and write over whatever was there. The delta value holds a number of seconds to wait between recording. We have built this into a loop, rather than just use a delay – it isn’t a good idea to pause the chip for huge amounts of time with a call to delay() so we can flash an LED to let everyone know it is still working.

Adjusting this delta value roughly suggests how many seconds to wait between samples, so you can configure the system to make your 1,024 samples stretch over a few hours or a few days. So, go on, measure something! LXF

The diagrams and layouts for this feature were done with Fritzing, a former HotPick! It’s great for simple schematics (www.fritzing.org/).

Quicktip

Next month How to manage outputs in a sound and vision extravaganza!

This simple circuit will manage all your data capturing needs – well, 1k of them.

LXF147.tut_arduino 15 6/1/11 11:51:27 AM

Page 16: LXF147 Supplement

GET A LINUX FORMAT SUBSCRIPTION

(may contain penguins)

See page 66 of the main issue or visitwww.linuxformat.com/subscribe

The last word on Linux

LXF147.sup_bcover 132 6/1/11 4:38:32 PM