With Jupiter in Mind

I’d hoped to invite a couple of friends round for some stargazing, but it clouded up shortly after I’d set up the telescope. I managed to get this shot of Jupiter with my smartphone held up to a new eyepiece.

Jupiter is about 11 Earths in diameter.

I’ve been reading up on ‘image stacking’, where you combine tens or hundreds of frames (either still or from video) to improve the detail visible in images – so fingers crossed for some clear nights ahead.

Further XPDisplay improvements

XPDisplay now has a ‘Data’ tab, which (currently) displays charts with approach glideslope and landing gear forces, for post-landing analysis:

Glideslope

Glideslope - showing a late approach and landing!

Landing Gear Forces

Landing Gear Forces

Charts are updated in real time and can be zoomed, printed or exported as PNG images (thanks to the excellent JFreeChart library).

Caveat: these features are not in the current released version of XPDisplay yet.

XPDisplay improvements in the pipeline

After a couple of months with hardly any time to work on my personal projects, it feels great to be developing again! This is a screenshot of XPDisplay, showing the new airport information display on the map tab:

The airport information is read from the same apt.dat file that XPlane uses, so everything is kept nice and consistent.

Supernova SN 2014J

No photo unfortunately, but last night I successfully observed supernova SN 2014J – a new supernova in the galaxy M82 that was only discovered on January 21st – using my 8-inch Meade LX200 telescope and a 15mm Super Plossl Eyepiece.

I’m only just getting the hang of using the telescope, so finding M82 (the “Cigar Galaxy”) was tricky. The galaxy appeared as an elongated smudge, only just visible, but with a few stars around. I made a rough sketch of what I saw and compared it with a finder chart and Stellarium so that I could be sure I’d found the right place.

It’s pretty cool to see light from a dying star that’s travelled 11.5 million light years. :)

By Jove!

I took this using a normal point-and-click camera held up to the eyepiece of my telescope last night. No red spot on Jupiter, that would have appeared around midnight (Jupiter rotates completely in 10 hours – crazily fast) but you can see bands of cloud quite clearly. It might have been a clearer picture but for the thin cloud (on our planet) and condensation that formed and reformed every few minutes on the telescope.

Panshanger scenery for X-Plane

This is my first attempt at a scenery package for the X-Plane flight simulator, using only Sketchup, GIMP and WED (plus the library objects that come with X-Plane and OpenSceneryX). It covers my local airfield (Panshanger Aerodrome), which is home to the excellent North London Flying School.


The scenery covers most of the main objects on the airfield, although there is always room for improvement (in particular the main hangar needs remodelling from scratch). Bengeo water tower is included, as it is a prominent local feature that pilots sometimes use for navigation.

To download the scenery, or for more information, please click here.

I’ve shared the code on GitHub under a public domain license – improvements welcome.

Edit: I’ve also uploaded this to the official X-Plane.org website.

More Aircraft Traces

Here are two more traces, generated from a whole day’s worth of data (29th June 2013). I’ve added some more ground locations as well as the places which the ‘stacks’ are named after (BOVINGDON, LAMBOURNE, BIGGIN and OCKHAM). Happily they coincide nicely with the loops which indicate where aircraft are circling. Colours here are assigned randomly to different aircraft.

The second trace is coloured according to altitude (purple corresponds roughly to 35,000ft – 40,000ft, down through green, yellow, orange and finally red, which corresponds to 5,000ft – 2,000ft).

You can clearly see high altitude routes (purple) as well as the stacks and approach routes into Heathrow. I expect that the routes into and out of Gatwick would also be visible if the range of my receiver was better. I’ve tried building a custom aerial, but haven’t had any luck with it so far.

For completeness, here is a trace coloured according to speed. Purple indicates 500-600kts, down through green, yellow and red at about 100-150kts.

(Related post: June 26th 2013)

Aircraft Traces

One of my trusty Raspberry Pis has a SDR (Software Defined Radio) USB stick, which is currently tuned into aircraft ADS-B transponders (which basically means that the Pi receives the position, altitude, speed and heading of every commercial aircraft within 50-100km, sometimes up to 200km, if the conditions are right).

The Pi now logs this data every 10 seconds to a file, which I copied from the Pi this evening and with a bit of Groovy scripting was able to generate the traces of aircraft seen over the last 8 hours or so:

You can clearly see the Lambourne, Biggin and Ockham holding stacks, where the planes circle around for a while, descending until given a landing slot.

The aircraft disappear from my screen below 1500ft or so, which is why you can see lots of planes approaching Heathrow (from the east/right) but they all seem to disappear before they get there. Similarly with Gatwick, many aircraft disappear from the right (east) as they arrive and many appear to the left (west) as they leave.

The data and processing isn’t perfect, but it’s a pretty picture for now. :)

Raspberry Pi – AnswerBox

I’ve been playing with a Raspberry Pi, Logitech USB mic, USB-powered speakers and a USB SDR TV tuner stick, combined with Stephen Hickson’s fantastic voicecommand system. (The Pi is the rainbox-striped box at the back, the flat black thing in the middle is a powered USB hub, into which is plugged the black TV tuner stick, which has an aerial (positioned on the small jar). The Rubix Cube is just there for scale.)

First, speech is sent as raw audio to a Google API which returns text. It would be better to do this on the Pi, but my experiments (with Julius) have shown that Google (with their gargantuan computing grid) is much better in terms of both speed and accuracy. (Since the microphone has an on/off button, audio is only sent to Google when I so choose.)

Second, the text is compared with a list of known commands (see the voicecommand website for more details). If a match is found, the corresponding script is run. (This is how the ‘weather forecast’ and ‘have I got mail’ commands work.) If no match is found, the text is sent off to Wolfram Alpha, which returns a text answer.

Finally, the results from Wolfram Alpha, or the appropriate script, are sent off to another Google API to turn them into an audio file, which is then played out over the speakers. I have tried using espeak, but again, Google’s API currently does a better job.

The whole thing is reasonably fast, given everything that is involved. Occasional internet latency spikes delay responses from the script for 10 seconds or so, but in my experience they are rare.

The live aircraft information is received using the Software Defined Radio (SDR) technique, using a RTL2838 TV tuner USB stick with rtl-sdr and dump1090 software, which provides a nice json interface over http. A python script queries this interface on demand and computes the nearest couple of aircraft to my location, then gathers some supplementary information from the internet before reading the response.

The scripts that make all this work are available on github.

Future plans include: adding commands to play music, add items to a shopping list, read news headlines and much more. My four year old daughter’s most recent request was for the AnswerBox to gain wings and fly around the room on request. There’s probably a python library for that. Hmmm….

MudPi

About 10 years ago, I wrote Alternate Universe MUD, a free text-based online adventure game, with a space-theme and an emphasis on exploration and discovery.

Note: for younger/less geeky readers, a MUD is a Multi User Dungeon, i.e. a game that you could play (with other people, no less!), which often revolved around dungeons, monsters and combat. Massively Multiplayer Online Role-Playing Games (MMORPGs) like World of Warcraft can trace their history back to these kinds of games.

The game features spaceships, several worlds, an astronomical observatory, mudmail and mud-wide-web terminals, a stock market, various robots and even an auditorium where Shakespeare’s plays are performed by bots every few hours.

Over 2000 players discovered Alternate Universe over the first few years (not bad for something that started out just as an experiment). Most players just played, but others contributed code, some helped build the world from the inside and one even ran an in-game monthly newspaper. I ended up meeting some of them in real life too, as a result of all this.

The game server itself was written from scratch in Java and has had many homes, but thanks to Oracle’s release of Java 8 for ARM in December, it now runs on a Raspberry Pi (and it runs happily too, using less than a third of the 512MB of memory – AU was mostly written on a 400MHz HP laptop with half the CPU speed that the Pi has now, so I’m not that surprised).


(the black thing poking out by the yellow S-Video socket is an external temperature sensor – I hope to wire this up to the MUD, so that the external temperature drives some behaviour or description inside the game)

Play!
To play Alternate Universe, go to the command-line and type
telnet alternateuniverse.dyndns.org 1063
If you are playing from another Pi, you may first need to install the ‘telnet’ program:
sudo apt-get install telnet

I hope you have fun playing. Drop me a MUD-mail if you enjoy the game!

New website

I’ve updated my website. Hopefully it looks cleaner and makes each project page a bit more ‘discoverable’.

Laptop Repair Man

Thank goodness for YouTube and eBay. My wife’s laptop (an Acer Travelmate 3270) started to switch off randomly and unexpectedly last week. An investigation quickly revealed the culprit: as I was wiggling the pin in the middle of the laptop’s circular power socket – it fell out! I did my best to defeat the laptop’s physical security mechanisms (screws) over the next 40 minutes or so, and I’m ashamed to admit that I didn’t use the Queen’s best English all of the time either. Just when I reached the point of giving up, I googled for ‘Travelmate laptop disassembly’ and struck gold thanks to YouTube. A mere ten minutes later (and wishing I’d googled first), I was looking at a kitchen table with a daunting pile of bits that were formerly known as a nearly-working laptop.

Fortunately, the power socket is on a smallish removable daughter board (circled in green) and replacements can be purchased on eBay for around £15. A few days later, I was googling for ‘Travelmate laptop reassembly’ with my fingers crossed – fortunately YouTube complied and an hour later I was sitting at the kitchen table with a fully working laptop once more (and with only five screws mysteriously left over, too).

Phew!

Low-cost XPlane Control Panel

This is a little project that I’ve been working on over the last month or so. (I completed it several weeks back, but I’ve been having too much fun with it to write up this blog post) It is a physical control panel for the XPlane flight simulator – i.e. when I move a slider (in the real world), the flaps begin to deploy (in the simulator). I flick another switch, some lights start to flash (in the real world) and in the simulator the landing gear start to rumble their way down into place. What the switches/sliders do is arbitrary – they can be reprogrammed whenever I feel like it.

There are three components: 1) the x-plane simulator 2) the physical box itself, with switches and LEDs wired to inputs/outputs on an Arduino Uno 3) some software running on the PC, which talks to the Arduino (over serial / USB) and to the simulator (sending and receiving UDP packets).

Building the box

The box is made from offcuts of MDF (about 3mm thick), laboriously measured, cut with a saw and drilled with a Dremel. (To cut the slots, I drilled lots of holes in a row and used the Dremel’s circular saw attachment).

The sides of the box were epoxied together (with a little help from some scrap blocks of wood in the corners) so that the back section could be removed, and the gaps filled with ordinary DIY filler. I sanded the whole thing and painted it with a clear primer (to stop the MDF from soaking up the paint and getting a bit soggy) then painted with two coats of a satin black paint (in hindsight, I wish I’d used matt rather than satin as it shows up the imperfections more than I’d like). The labels were printed on my ordinary inkjet printer, white text on a black background & attached with a little spot of glue so that I can rip them off and attach new labels if I reprogram the switches to do something different.

Electronics

The panel has six switches, two linear potentiometers (I accidentally ordered audio ones, which are logarithmic, and had to work around this in the Arduino sketch code to convert to near-linear values), three 5mm red LEDs and three 5mm green LEDs (plus a resistor for each set of LEDs). Inside the box there is an Arduino Uno, a breadboard and quite a few jumper wires.

The USB port of the Arduino sticks out through a square hold in the side of the box. The Arduino is screwed to some chipboard (with some bubblewrap between the PCB and the wood, just to cushion the circuit a little.

The inside is rather messy. With hindsight I would’ve spent longer finding a way to make this a lot tidier and probably not needed the breadboard. In most cases, I ended up snipping a jumper wire in half, soldering one end to a component and pressing the pointy end into the breadboard.

I didn’t use all the ports on the Arduino Uno – if I made a version 2, I could probably have 6 more lights or switches and 4 more sliders, or possibly include a buzzer to serve as stall / warning indicator.

Arduino sketch code

This is the Arduino sketch that is currently running inside the control panel. In a nutshell, in a loop, it checks the state of each switch and writes a string to the serial port (really it’s serial over USB), which looks something like this:

A^ Bv Cv Dv Ev F^ P1022 Q1019

This indicates that switch A is up, B is down, etc. P and Q are the values of the sliders (from 0 to 1023).

The code also reads from the serial port to receive one of the letters U, D, R, F or N to indicate the current state of the landing gear in the simulator (Up, Down, Rising, Falling or No idea!). If the gear is up, the red LEDs go on, if the gear is down, the green LEDs go on. If it’s rising, the red LEDs are flashed, falling = green LEDs are flashed.

const int led_red_pin = 3;
const int led_green_pin = 2;

const int switch_1_pin = 6;
const int switch_2_pin = 5;
const int switch_3_pin = 4;
const int switch_4_pin = 9;
const int switch_5_pin = 8;
const int switch_6_pin = 7;

const int slider_1_pin = A0;
const int slider_2_pin = A1;

int switch_1_state = 0;
int switch_2_state = 0;
int switch_3_state = 0;
int switch_4_state = 0;
int switch_5_state = 0;
int switch_6_state = 0;

int slider_1_state = 0;
int slider_2_state = 0;

int inByte = 0;

const int GEAR_NO_CONNECTION = 0;  // alternate flashing both sets of LEDs
const int GEAR_DOWN = 1; // solid green 'D'
const int GEAR_UP = 2;  // solid red 'U'
const int GEAR_TRANSITIONING_DOWN = 3;  // flash green 'F'
const int GEAR_TRANSITIONING_UP = 4;  // flash red 'R'

int gearState = GEAR_NO_CONNECTION;

unsigned long time;
unsigned long lastReceivedTime;

void setup() {
    pinMode(switch_1_pin, INPUT_PULLUP);
    pinMode(switch_2_pin, INPUT_PULLUP);
    pinMode(switch_3_pin, INPUT_PULLUP);
    pinMode(switch_4_pin, INPUT_PULLUP);
    pinMode(switch_5_pin, INPUT_PULLUP);
    pinMode(switch_6_pin, INPUT_PULLUP);

    pinMode(led_red_pin, OUTPUT);
    pinMode(led_green_pin, OUTPUT);    

    // no pinMode required for analog inputs

    Serial.begin(9600);

    time = 0;
}

void loop() {
    if (Serial.available() > 0) {
        inByte = Serial.read();
        delay(20);    

        if( inByte == 'U' ) {
            gearState = GEAR_UP;
        } else
        if( inByte == 'D' ) {
            gearState = GEAR_DOWN;
        } else
        if( inByte == 'R' ) {
            gearState = GEAR_TRANSITIONING_UP;
        } else
        if( inByte == 'F' ) {
            gearState = GEAR_TRANSITIONING_DOWN;
        } else
        if( inByte == 'N' ) {
            gearState = GEAR_NO_CONNECTION;
        }

        lastReceivedTime = millis();
    }

    if( (millis() - lastReceivedTime) > 2000 ) {
        gearState = GEAR_NO_CONNECTION;
    }

    // note: read delays required due to issue with Arduino ADC timing.
    delay(20);
    slider_1_state = logToLin(analogRead(slider_1_pin));
    delay(50);
    slider_2_state = logToLin(analogRead(slider_2_pin));

    switch_1_state = digitalRead(switch_1_pin);
    switch_2_state = digitalRead(switch_2_pin);
    switch_3_state = digitalRead(switch_3_pin);
    switch_4_state = digitalRead(switch_4_pin);
    switch_5_state = digitalRead(switch_5_pin);
    switch_6_state = digitalRead(switch_6_pin);

    write('A', switch_1_state);
    write('B', switch_2_state);
    write('C', switch_3_state);
    write('D', switch_4_state);
    write('E', switch_5_state);
    write('F', switch_6_state);

    Serial.write('P');
    //Serial.println(slider_1_state, DEC);
    Serial.print(slider_1_state, DEC);
    Serial.write(' ');
    Serial.write('Q');
    Serial.print(slider_2_state, DEC);

    // switch_1 is avionics on/off and so is also an on/off switch for all the LEDs
    if( switch_1_state == LOW ) {
        if( gearState == GEAR_UP ) {
            digitalWrite(led_red_pin, HIGH);
            digitalWrite(led_green_pin, LOW);
        } else
        if( gearState == GEAR_DOWN ) {
            digitalWrite(led_red_pin, LOW);
            digitalWrite(led_green_pin, HIGH);
        } else
        if( gearState == GEAR_TRANSITIONING_UP ) {
            // time since last change..
            unsigned long since = millis() - time;
            if( since > 1000 ) {
                digitalWrite(led_red_pin, HIGH);
                digitalWrite(led_green_pin, LOW);
                time = millis();
            } else
            if( since > 500 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, LOW);
            }
        } else
        if( gearState == GEAR_TRANSITIONING_DOWN ) {
            // time since last change..
            unsigned long since = millis() - time;
            if( since > 1000 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, HIGH);
                time = millis();
            } else
            if( since > 500 ) {
                digitalWrite(led_red_pin, LOW);
                digitalWrite(led_green_pin, LOW);
            }
        } else
        if( gearState == GEAR_NO_CONNECTION ) {
            digitalWrite(led_red_pin, LOW);
            digitalWrite(led_green_pin, LOW);
        }

    } else {
        digitalWrite(led_red_pin, LOW);
        digitalWrite(led_green_pin, LOW);
    }

    Serial.write('\n');  

    delay(150);
}

void write(char c, int state) {
    Serial.write(c);
    if( state == HIGH ) {
        Serial.write('v');
    } else
    if( state == LOW ) {
        Serial.write('^');
    } else {
        Serial.write('x');
    }
    Serial.write(' ');
}

int logToLin(int a) {
    double x = (double) a / 1024.0;
    double y = pow(20.0, x);
    double z = y - 1.0;
    int b = (int)(z * 54.0);
    return b;
}

Note the logToLin function which I needed because I’d accidentally ordered logarithmic scale potentiometers rather than linear ones. The magic numbers you see in that function came from experimentation.

I found the Arduino serial monitor very helpful to watch what the box was sending over the serial port.

PC software

The code to send/receive UDP packets to/from XPlane is written in Java and based on XPDisplay but using the RXTX library for serial port communication. If I’m honest it’s a bit of a shambles at the moment and I’m hoping to rewrite it, so samples are available on request. :-)

Conclusions

All in all, this was an easy project to pick up for an hour, do a little bit, then put down again (which is important when you’ve got young kids). Although it’s definitely amateurish, the end result is better than I hoped for at the beginning, and as this was my first arduino project it was very much a case of discovering the right way to do things as I went along.

The total cost of materials & electronics was about £30 (from RS, Farnell, Oomlout):

  • MDF wood – scraps I had lying around, probably worth no more than £3
  • slide potentiometers – £1 each, so £2 – get linear ones, I got logarithmic (audio) ones
  • knobs for slide potentiometers – £1
  • switches – £1 each, so £6
  • LEDs – about 40p in total
  • Arduino Uno – £15
  • Epoxy glue – £2
  • Labels – nothing, printed on my inkjet printer
  • Paint – £3 for the primer & for the paint, probably used 50p’s worth.

Raspberry Pi serving HTML5 to the Kindle Touch

The web browser on Amazon’s Kindle Touch is capable of rendering HTML5 content and executing Javascript, which has given me the idea of using it as an external display for the flight simulator that I often play with. Tonight I installed apache on my Raspberry Pi and created a file in /var/www/test.html containing some basic html5 canvas code:

<html>
<head>
<script type="text/javascript">
function drawShape(){
  // get the canvas element using the DOM
  var canvas = document.getElementById('mycanvas');
  // Make sure we don't execute when canvas isn't supported
  if (canvas.getContext){
    // use getContext to use the canvas for drawing
    var ctx = canvas.getContext('2d');

    // Filled triangle
    ctx.beginPath();
    ctx.moveTo(25,25);
    ctx.lineTo(105,25);
    ctx.lineTo(25,105);
    ctx.fill();

    // Stroked triangle
    ctx.beginPath();
    ctx.moveTo(125,125);
    ctx.lineTo(125,45);
    ctx.lineTo(45,125);
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.arc(150,120,42,0,2 * Math.PI);
    ctx.closePath();
    ctx.stroke();

    // border canvas area
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(0,canvas.height);
    ctx.lineTo(canvas.width,canvas.height);
    ctx.lineTo(canvas.width,0);
    ctx.closePath();
    ctx.stroke();

    ctx.font = "3em Arial";
    ctx.lineWidth=2;
    ctx.strokeStyle="gray";
    ctx.strokeText("Hello Kindle", 100,300);
    ctx.strokeText("from the", 120,400);
    ctx.strokeText("Raspberry Pi", 100,500);

    var imageObj = new Image();
    imageObj.onload = function() {
        ctx.drawImage(imageObj, 350, 10);
    };
    imageObj.src = "http://techaxcess.com/wp-content/uploads/2012/10/Raspberry-Pi.png

  } else {
    alert('You need Safari or Firefox 1.5+ to see this demo.');
  }
}
</script>
</head>
<body onload="drawShape();">
   <canvas id="mycanvas" width="560" height="580"></canvas>
</body>
</html>

I pointed the Kindle’s web browser at the IP address of the RPi & the page loaded nicely in the browser. Here is a screenshot:

Font sizes on the Kindle seem to differ slightly from those rendered by Firefox, but I can probably work around that.

RPi CPU load and temperature logging to Cosm

Here is a 15-minute recipe to get your Raspberry Pi logging data to Cosm.com, who provide a RESTful API to query the data and produce customized charts.

I use two scripts that cron runs every 5 minutes, one to log data to a CSV file, and another to upload the data to cosm, then delete the CSV file. That way, if my internet connection goes down for a while, the logged data is not lost. I have a copy of these scripts in a separate directory for each variable (‘datastream’) that I monitor, which makes it easier to manage.

CPU load

First, create an account on Cosm.com (it’s free & quick).

Now set up a new feed (a feed is a collection of datastreams; each datastream is a series of timestamped data points, aka a ‘time series’)

  • “+ Device/Feed”
  • What type of device/feed do you want to add?: Choose ‘Something Else’
  • Step 1 – Data: Choose ‘No, I will push data to Cosm’
  • Step 2 – Title: think of a relevant name for your feed, e.g. “MyDesktopPi”
  • Step 3 – Tags: give it any relevant tags that might help you find it in future (this is only really useful for public feeds)
  • Step 4 – Create. Make a note of the feed ID, we’ll use this later.

Once you’ve created a feed, you can add datastreams to it. A datastream represents a value that your Pi will monitor over time, like temperature or CPU load (or internet connectivity, washing machine activity, presence of your mobile phone on your LAN, etc..)

  • “+ Datastream”
  • ID: This doesn’t need to be numeric – you can enter something like ‘Pi_CPU_Load’. Make a note of this datastream ID too.
  • Tags: e.g. ‘pi cpu load’
  • Units: ‘Capacity’ – this is free text
  • Symbol: leave blank

At this point, your feed is public, i.e. anyone can view the current data. This may be fine, but if you want to change it, scroll down to the ‘Feed Status’ section at the bottom of the page.

Now, at the bottom of the screen click the green ‘Save Changes’ button. (this is an area of the cosm UI that needs work, IMHO, as you expect to find this button near the data that you’re editing..)

You can get back to the edit feed / add datastream page at any time by clicking the little cog icon on the right hand side of the feed name and choosing ‘edit’.

Now that we’ve defined our feed and datastream, we need to give our script permission to upload data to our Cosm datastream. This is done by generating an API key.

  • In the top-right of the cosm web page, click the ‘Keys’ icon.
  • Click the ‘+ Key’ icon, give the key a label (ID), e.g. ‘MyDesktopPi_UploadScriptKey’, and choose feed restrictions:’Use any feed (including my private feeds)’ and access privileges:’all’, then click ‘create’.
  • Make a note of the long alphanumeric API key string, as we’ll use that in a moment.

The last thing to do is to create the scripts on the Pi that will upload data to cosm.

Log in as ‘pi’.

cd ~
mkdir cosm
cd cosm
mkdir load
cd load

Install CURL
sudo apt-get install curl

Using your favourite text editor, create the file ‘log.sh’:
#!/bin/bash
####################################################
# Please customize these values appropriately:
LOCATION=/home/pi/cosm/load
#VALUE=$( uptime | awk -v FS="[, ]" '{print $18}' )
# alternatively:
VALUE=$( cat /proc/loadavg | awk {'print $2'} )
####################################################
TIME=`/bin/date -u +%FT%XZ`
if [[ "$VALUE" == "" ]]
then
VALUE=0
fi
echo "$TIME,$VALUE" >> $LOCATION/cosm.csv
exit 0

.. and save it. The line cat /proc/loadavg | awk {'print $2'} simply takes the second number from the /proc/loadavg file, which represents the 5-minute-average of the CPU load.

Create a file called ‘upload-cosm.sh’:
#!/bin/bash
####################################################
# Please customize these values appropriately:
LOCATION=/home/pi/cosm/load
API_KEY='your-long-alphanumeric-api-key-here'
FEED_ID='your-feed-id-here'
DATASTREAM_ID='your-datastream-id-here'
####################################################
COSM_URL=http://api.cosm.com/v2/feeds/$FEED_ID/datastreams/$DATASTREAM_ID/datapoints.csv
sleep 2 # gives any data logging scheduled at the same time a chance to run
echo $COSM_URL
curl -v --request POST --header "X-ApiKey: $API_KEY" --data-binary @$LOCATION/cosm.csv $COSM_URL
if [ $? -eq 0 ]
then
rm $LOCATION/cosm.csv
#echo "Would delete file now."
fi
#echo "Done"

.. and save that too. Exit the text editor, and make all the .sh scripts executeable with:
chmod u+x *.sh

(I’ll assume that you’ve changed LOCATION, API_KEY, FEED_ID and DATASTREAM_ID appropriately for your system)

The log.sh script will append to a file called cosm.csv every time it is run. You can try it now if you like:
./log.sh

To schedule these scripts to run, we edit ‘crontab’, a file that tells cron which scripts to run, and when. My favourite editor is called ‘joe’, yours might be ‘vi’, ‘emacs’ or another. The first line makes sure that crontab will use your editor to edit the crontab file:

EDITOR=joe
crontab -e

Append these lines to the end of your crontab file (leaving a blank line at the end):

*/5 * * * * /home/pi/cosm/load/log.sh
*/5 * * * * /home/pi/cosm/load/upload-cosm.sh

*/5 means ‘run every 5 minutes’. See this reference for more details.

Save the crontab file and exit your editor. Now you can either wait for five minutes, or simply run the upload script with:
./upload-cosm.sh

With luck, you should now be able to reload your Cosm.com page and see the data uploaded to your datastream as a chart.

CPU temperature

Simply copy the ~/cosm/load directory:
cp -R ~/cosm/load ~/cosm/temperature
cd ~/cosm/temperature

Edit the log.sh script, and replace:

LOCATION=/home/pi/cosm/temperature
# VALUE is in degrees celcius
VALUE=$( cat /sys/class/thermal/thermal_zone0/temp | awk -v FS=" " '{print $1/1000""}' )

This takes the 1st value from /sys/class/thermal/thermal_zone0/temp (in fact there is only one number), and divides it by 1000 to get degrees C (the raw value is in thousandths of a degree).

Edit the upload-cosm.sh script with your temperature datastream ID (update the LOCATION too), and add to crontab:

* * * * * /home/pi/cosm/temperature/log.sh
*/5 * * * * /home/pi/cosm/temperature/upload-cosm.sh

(I’ve chosen to record the temperature every minute, but upload the values every 5 minutes)

Note the daily variation, even though this is monitoring CPU temperature! I guess the Pi is in a south-facing room which warms up during the day, but I didn’t expect to see this so clearly. The dropouts/spikes that you see in the data are caused by occasional erroneous values returned by the temperature sensor.

If you find this useful, please post a comment indicating the type of data that you’re monitoring (and maybe the line of script that captures the data?).

Thanks for reading!

Dansette