2011-01-20

(Desktop) Safari & (W3C) Geolocation API

I don't know why this thing fails in desktop Safari. I'm using Modernizr (which is bundled with the highly recommended HTML5 Boilerplate) which reports that the Geolocation API is supported but I'm unable to even get any dialog asking the user for permission to geolocate (which is, if I read the draft correctly, REQUIRED to be implemented).

Also, it seems that others (quite a few people actually) have had this same problem but I have been unable to find a solution yet. Below is a snippet from the code; in desktop Safari the PositionErrorCallback (the latter cb function) gets called after timeout but no luck with PositionCallback, no matter how long the timeout value is. Other tested browsers work as expected.

Referenced in other places:

(Note to self: check if Google Gears, which is still installed, is causing this?)

var position = null;

$(document).ready(function() {

    if(!Modernizr.geolocation) {
        return;
    }

    navigator.geolocation.watchPosition(
        function(pos) {
            position = {};
            position.lat = pos.coords.latitude;
            position.lng = pos.coords.longitude;
            position.allowed = true;
            init();
        },
        function(error) {
            position = {};
            position.allowed = false;
        },
        {
            enableHighAccuracy: false,
            timeout: 10000,
            maximumAge: 86400000
        }
    );

    checkposition();
});

function checkposition()
{
    log("checkposition()");
    if(!position) {
        setTimeout(function() {
            checkposition();
        }, 1000);
        return;
    } else {
        if(position.allowed) {
            log("checkposition(): got position: " + position.lat + "," + position.lng);
            fetchephemeris();
        } else {
            log("checkposition(): could not get a position, giving up");
            $("#geolocate").hide();
        }
    }    
}

2011-01-10

QUERY_STRING parsing in plain C

As far as I can tell (which, I'll be the first one to admit, doesn't count for that much) this code is so simple that there are no holes that could be exploited.

  char * query = getenv("QUERY_STRING");
  char * pair;
  char * key;
  double value;
 
  if(query && strlen(query) > 0) {
    pair = strtok(query, "&");
    while(pair) {
      key = (char *)malloc(strlen(pair)+1);
      sscanf(pair, "%[^=]=%lf", key, &value);
      if(!strcmp(key, "lat")) {
        lat = value;
      } else if(!strcmp(key, "lng")) {
        lng = value;
      }
      free(key);
      pair = strtok((char *)0, "&");
    }
  }

2010-11-21

jQuery Boids (Plugin)

From the README:

"My first attempt making a jQuery plugin, following the guidelines at: http://docs.jquery.com/Plugins/Authoring

Boids code adapted from Javascript Boids by Ben Dowling, see: http://www.coderholic.com/javascript-boids/

If this is bound to the window resize event, then the jQuery resize event plugin by "Cowboy" Ben Alman should be used as it throttles the window resize events. See: http://benalman.com/projects/jquery-resize-plugin/"

The plugin uses HTML Canvas to render the Boids, so a modern browser with Canvas support is required for this to work. I tested with Chrome, Safari and Firefox. IE with Excanvas was painfully slow…

Code is hosted at GitHub: https://github.com/kahara/jQuery-Boids

Demo is at: http://jonikahara.com/lab/jQuery-Boids/test.html

2010-07-01

Solar Impulse

«If an aircraft is able to fly day and night without fuel, propelled only by solar energy, let no one claim that it is impossible to do the same thing for motor vehicles, heating and air conditioning systems and computers. This project voices our conviction that a pioneering spirit with political vision can together change society and bring about an end to fossil fuel dependency.»

Bertrand Piccard

2010-04-03

Arduino, DS18B20, Ethernet Shield, Pachube.Com

Kotkansaari Sensorium

Update 2: The sensor is now outside, and running on parasitic power.

Update: The data is now available in a more mobile-friendly web page here.

Arduino code, based on (i.e. copypasted & modified a little) stuff from http://www.dial911anddie.com/weblog/2009/12/arduino-ethershield-1wire-temperature-sensor-pachube/ is below. Requires the 1-wire library and the Dallas Temperature Control library, both of which can be downloaded from here. Original code utilized DHCP, but I found this to be somewhat unstable and went with a static IP address instead.

DS18B20 gets it's power from Arduino, and the data line (that's the center pin) is connected to Arduino pin 8. Data line is pulled up to +5V through a 4k7 resistor, as suggested in Maxim literature. Parasitic power supply was not used as proper voltage was readily available from Arduino. Please note that even though parasitic power is not used, the pull-up resistor is still necessary (see the data sheet).

#include <Ethernet.h>
#include <OneWire.h>
#include <DallasTemperature.h>

char PACHUBE_API_STRING[] = "";  // Your API key
int PACHUBE_FEED_ID = 0; // Your feed ID 

// Digital IO port used for one wire interface
int ONE_WIRE_BUS = 8 ;

// Ethernet mac address - this needs to be unique
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// IP addres of www.pachube.com
byte server[] = { 209,40,205,190 };

// Arduino address
byte ip[] = { 10, 0, 0, 223 };
byte gateway[] = { 10, 0, 0, 2 };

char version[] = "PachubeClient Ver 0.01c";

#define CRLF "\r\n"

// simple web client to connect to Pachube.com 
Client client(server, 80);

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// 1wire device address
DeviceAddress thermometer;

void setup()
{
   // Note: Ethernet shield uses digitial IO pins 10,11,12, and 13   
   Serial.begin(9600);
  
   Serial.println(version);
   Serial.println();
  
   // locate devices on the 1Wire bus
   Serial.print("Locating devices on 1Wire bus...");
   sensors.begin();
   int count = sensors.getDeviceCount();
   Serial.print("Found ");
   Serial.print( count );
   Serial.println(" devices on 1wire bus");

   // select the first sensor   
   for ( int i=0; i<count; i++ )
   {
      if ( sensors.getAddress(thermometer, i) ) 
      {
         Serial.print("1wire device ");
         Serial.print(i);
         Serial.print(" has address: ");
         printAddress(thermometer);
         Serial.println();
      }
      else
      {
         Serial.print("Unable to find address for 1wire device "); 
         Serial.println( i );
      }  
   }
  
   // show the addresses we found on the bus
   Serial.print("Using 1wire device: ");
   printAddress(thermometer);
   Serial.println();

   // set the resolution to 9 bit 
   sensors.setResolution(thermometer, 9);

   Serial.print("Initializing ethernet...");  
   delay(5000);
   Ethernet.begin(mac, ip, gateway);
   delay(5000);
   Serial.println(" done.");
}

void sendData()
{     
   float temp = sensors.getTempC(thermometer);
   //float temp = sensors.getTempF(thermometer);
   Serial.print("Temp=");
   Serial.println(temp);
  
   Serial.println("connecting...");

   if (client.connect()) 
   {
      Serial.println("connected");
      
      client.print(
         "PUT /api/feeds/" );
      client.print(PACHUBE_FEED_ID);
      client.print(".csv HTTP/1.1" CRLF
                   "User-Agent: Fluffy Arduino Ver 0.01" CRLF
                   "Host: www.pachube.com" CRLF 
                   "Accept: */" "*" CRLF  // need to fix this 
                   "X-PachubeApiKey: " );
      client.print(PACHUBE_API_STRING);
      client.print( CRLF 
                    "Content-Length: 5" CRLF
                    "Content-Type: application/x-www-form-urlencoded" CRLF
                    CRLF );
      client.println(temp);
      unsigned long reqTime = millis();
      
      // wait for a response and disconnect 
      while ( millis() < reqTime + 10000) // wait 10 seconds for response  
      {
         if (client.available()) 
         {
            char c = client.read();
            Serial.print(c);
         }

         if (!client.connected()) 
         {
            Serial.println();
            Serial.println("server disconnected");
            break;
         }
      }
      
      Serial.println("client disconnecting");
      Serial.println("");
      client.stop();
   } 
   else 
   {
      Serial.println("connection failed");
   }
}

void printAddress(DeviceAddress deviceAddress)
{
   for (uint8_t i = 0; i < 8; i++)
   {
      if (deviceAddress[i] < 16) Serial.print("0");
      Serial.print(deviceAddress[i], HEX);
   }
}

void loop()
{
   sensors.requestTemperatures(); // Send the command to get temperatures
   sendData();
   delay( ( 5l * 60l * 1000l) - 11000l  ); // wait 5 minutes
}

2009-10-11

Feeding lm_sensors Data to a Remote Database

A more correct way to do this would be to use libsensors(3) but I couldn't find Python bindings for it, so I just read and parse the output of the sensors(1) program. The temperature and fanspeed readings come from a spare Asus Eee PC and the raw data can be seen here (may be offline). I wish I had a more varying and interesting data source but this will do for now for the purpose of playing with Google Chart API.


#!/usr/bin/env python

import os, scanf

temperature = 0
fanspeed = 0

for line in os.popen('/usr/bin/sensors'):
    if len(line) < 2:
        continue

    fields = line.split()

# Python scanf from http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

    key = fields[0]
    if key == 'temp1:':
        temperature = scanf.sscanf(fields[1], '+%d.0')
    if key == 'fan1:':
        fanspeed = scanf.sscanf(fields[1], '%d')

import urllib2

# Authentication code ripped from python.org docs

password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
top_level_url = "http://jonikahara.com/lab/thermometer/update"
password_mgr.add_password(None, top_level_url, 'USERNAME', 'PASSWORD')
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
opener = urllib2.build_opener(handler)
opener.open('http://jonikahara.com/lab/thermometer/update?' + 'temperature=' + str(temperature[0]) + '&fanspeed=' + str(fanspeed[0]))