Monday, November 7, 2011

Apt-get in the iphone

Highly annoying to find this. With a jail broken iPhone you need to install the package "AptBackup" to get command line apt-get.

chomping with boost

Always convenient
    
#include <boost/algorithm/string.hpp>
...
boost::algorithm::trim_if(str,boost::algorithm::is_any_of(" \t\n"));

Friday, November 4, 2011

Recursive Templates -- Iterable poly dimensional associative arrays

And here is the template based array with a iterator added to it that is capable of iterating all the final cells in the array. Its not the most efficient but it works. Also note the operator<< is a bit busted at the moment.. Will fix it when im not about to fall asleep.
//built with mimggw g++ (gcc version 4.5.0 (GCC))
#include <iostream>
#include <map>
#include <string>
#include <ostream>

//ploy dimensional iterators
// what we are dealing with here is a N dimensional deep map

template <int n, typename Key, typename Value > 
class MultiDimMap
{
public:
//problem here for types u cant reach inside of the yet to be defined struct and get its type. You need to have it pre-definable.  
  typedef MultiDimMap<n-1, Key, Value>  InnerMapClass;
  typedef std::map<Key, InnerMapClass>  MapClass;

  //instance depend types.
  //"typename" is need here so the compiler knows we are pointing 
  //to a type that is not yet a solid implementation.
  typedef typename InnerMapClass::iterator InnerMapClassItr;
  typedef typename MapClass::iterator      MapClassItr;

  class iterator 
  {
  public:
    Value&    operator*()  { return *inner; }
    iterator& operator++()
    {
      ++inner; 
      valid_or_next(); 
      return *this;
    }
    bool      operator!=(const iterator& oit) const
    { 
      return (oit.outer != outer          && 
              (map->end() == outer         ||
        oit.map->end() == oit.outer ||
        oit.inner != inner)); 
    }
    bool      operator==(const iterator& oit) const { return !(oit != *this);  }

    std::ostream& print(std::ostream& out) { out << outer->first << ":"; inner.print(out); return out; } 

    iterator() :
      map(NULL),
      outer(),
      inner()
    {}
    iterator(MapClass& map_, const MapClassItr& outer_) :
      map(&map_),
      outer(outer_),
      inner()
    {
      inner = outer->second.begin();
      valid_or_next();
    }
        
  private:
    void valid_or_next()
    {
      while(
           (map->end() != outer) &&
           (outer->second.end() == inner) 
           )
       {
         ++outer;
         inner = outer->second.begin();
      }
    }

    MapClass* map;  
    MapClassItr outer;
    InnerMapClassItr inner;
  };
  
  iterator begin() { return iterator(map, map.begin()); }
  iterator end()   { return iterator(map, map.end()  ); }
  
  InnerMapClass& operator[](Key key) { return map[key]; }

private:
  MapClass map;  
}; 

//The recursive termination.. 
template <typename Key, typename Value > 
class MultiDimMap<1, Key, Value > 
{
public:
  typedef std::map<Key, Value> MapClass;

  //instance depend types.
  //"typename" is need here so the compiler knows we are pointing 
  //to a type that is not yet a solid implementation.
  typedef typename MapClass::iterator MapClassItr;

  class iterator 
  {
  public:
    Value&    operator*()     { return it->second; }
    iterator& operator++() { it++; return *this; }
    bool      operator!=(const iterator& oit) const { return oit.it != it;  }
    bool      operator==(const iterator& oit) const { return !(oit != *this);  }

    std::ostream& print(std::ostream& out) const { out << it->first << ":" << it->second; return out; }

    iterator() : it() {}
    iterator(const MapClassItr& it_) : it(it_)  {}
  private:
    MapClassItr it;
  };
  
  iterator begin() { return iterator(map.begin()); }
  iterator end()   { return iterator(map.end()  ); }

  Value& operator[](Key key) { return map[key]; }
private:
  MapClass map;  
}; 

//this hates me fix it later
//template <int n, typename Key, typename Value > 
//std::ostream& operator<<( std::ostream& out, const typename MultiDimMap<n, Key, Value>::iterator& it )
//{
//  return it.print(out);
//}

int main()
{
  MultiDimMap<1, std::string, int> stringInt1DMap;
  stringInt1DMap["a"] = 2; 
  stringInt1DMap["b"] = 5;
  
  MultiDimMap<2, std::string, int> stringInt2DMap;
  stringInt2DMap["d"]["b"] = 5;
  stringInt2DMap["a"]["c"] = 2;
  stringInt2DMap["d"]["c"] = 3;

  MultiDimMap<3, int, std::string> intString3DMap;
  intString3DMap[2][1][1] = "first";
  intString3DMap[1][1][2] = "second";
  intString3DMap[2][2][1] = "third";

  std::cout << "1d Iteration:" << std::endl;
  MultiDimMap<1, std::string, int>::iterator it1d;
  for(it1d  = stringInt1DMap.begin();
      it1d != stringInt1DMap.end();
      ++it1d)
    {
      std::cout << " ";
      it1d.print(std::cout); 
      std::cout << " Value:" << *it1d << std::endl;
    }

  std::cout << "2d Iteration:" << std::endl;
  MultiDimMap<2, std::string, int>::iterator it2d;
  for(it2d  = stringInt2DMap.begin();
      it2d != stringInt2DMap.end();
      ++it2d)
    {
      std::cout << " ";
      it2d.print(std::cout); 
      std::cout << " Value:" << *it2d << std::endl;
    }

  std::cout << "3d Iteration:" << std::endl;
  MultiDimMap<3, int, std::string>::iterator it3d;
  for(it3d  = intString3DMap.begin();
      it3d != intString3DMap.end();
      ++it3d)
    {
      std::cout << " ";
      it3d.print(std::cout); 
      std::cout << " Value:" << *it3d << std::endl;
    }
}

Thursday, November 3, 2011

Recursive Templates -- poly dimensional associative arrays

Another step to the poly dimensional iterators are the generic ploy dimensional arrays. These come in handy. The Trick to realize is that the types need to be predefined first so 2 typedefs are needed.

//built with mimggw g++ (gcc version 4.5.0 (GCC))
#include <iostream>
#include <map>
#include <string>
//ploy dimensional associative arrays 

template <int n, typename Key, typename Value > 
struct MultiDimMap
{
//problem here for types u cant reach inside of the yet to be defined struct and get its type. You need to have it predefinable.  
  typedef MultiDimMap<n-1, Key, Value>  InnerMapClass;
  typedef std::map<Key, InnerMapClass>  MapClass;
  
  InnerMapClass& operator[](Key key) { return map[key]; }

private:
  MapClass map;  
}; 

template <typename Key, typename Value > 
struct MultiDimMap<1, Key, Value > 
{
  typedef std::map<Key, Value> MapClass;

  Value& operator[](Key key) { return map[key]; }
private:
  MapClass map;  
}; 

int main()
{
  MultiDimMap<1, std::string, int> stringInt1DMap;
  
  stringInt1DMap["a"] = 2;
  
  MultiDimMap<2, std::string, int> stringInt2DMap;
  stringInt2DMap["a"]["b"] = 2;
  stringInt2DMap["a"]["c"] = 3;

  MultiDimMap<3, int, std::string> intString3DMap;
  intString3DMap[1][1][1] = "first";
  intString3DMap[1][1][2] = "second";
  intString3DMap[2][2][1] = "third";
  
  std::cout << intString3DMap[2][2][1] << std::endl;
  std::cout << stringInt2DMap["a"]["c"]  << std::endl;
}

Wednesday, November 2, 2011

Recursive Templates

I have been thinking about doing a generic N dimension container and iterator template recently and started putting together some of the code for it. In order to make it work a good understanding of Template recursion is needed. Here is an trivial example

//built with mimggw g++ (gcc version 4.5.0 (GCC))
#include <iostream>

template <int n> 
struct Factorial
{
  enum { RET = Factorial<n-1>::RET * n };
}; 

template <>
struct Factorial<0>
{
  enum { RET = 1 };
}; 

int main()
{
  std::cout << Factorial<1>::RET << std::endl;
  std::cout << Factorial<2>::RET << std::endl;
  std::cout << Factorial<3>::RET << std::endl;
  std::cout << Factorial<4>::RET << std::endl;
}

Thursday, October 27, 2011

Homemade Geiger Counter - Part 3 -- The Arduino software

WARNING >>>>>>LONG>>>>>> POST

The core processor in the Geiger counter is a Arduino ATmega328. It has 2kBytes of sdram 32kB of Flash and 2kb of EEPROM. And its running at 16Khz. Consider this carefully.

It means that memory will be completely filled with the following;
* 2000 character string
* 400 word sentence (at a 5 char average per word )
* an array of 500 32 bit integers

It means that excessive overhead and code niceties are often sacrificed for the sake of code speed.

Since it is a waste of more memory to keep a table of malloced and freed areas of memory embedded software is generally designed to using compile time data structures or stack allocated variables rather than heap data.

The Geigers counters code is built in this manor, Its not petty but its functional and efficient. The code is divided into 2 files the header and main code file

The header contains the constants such as which parts of that hardware and wired to what pins.

If you look closely you might not that I made a mistake when i selected the pins for the LCD since A3(Analogue 3) is already in use by the WIZNET(wireless radio) chip.

Another point to note is that is the "state" and "LcdDisplayMode" enums, this clearly implies that the code makes use of at least 1 state machine. In actual fact there are several but more on that later. I often consider stateful code to be the poor-mans threads. Coding this way allows the low spec hardware to quickly juggle a rather large set of real-time tasks without taking too much time for each. All you have to do a a code is segment the code into manageable chunks and allocate a state for each subsection or task.

enum states
  {
    RESET     = 0,
    SOFT_RESTART,
    INIT_DHCP,
    OBTAIN_IP,
    INIT_CLIENT,
    NORMAL,
    CONNECT,
    SEND_DATA,
    RESP_DATA
  };

typedef enum {
  LCD_AVE_READING,
  LCD_INST_READING,
  LCD_STATE,
  LCD_UPTIME,
  LCD_MAX_MODE
} LcdDisplayMode;

// DEVICE SETTINGS -- REPLACE WITH A PROPER MAC ADDRESS
byte macAddress[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xBE };

// DEVICE SETTINGS NetRAD - this is specific to the NetRAD board
#define  GEIGER_INTERRUPT_PIN  1 // pin of the gieger counter 
#define  PIN_SPKR              6 // pin number of piezo speaker
#define  PIN_LED               7 // pin number of event LED
#define  WIZNET_RESET_PIN     A1
#define  RADIO_SELECT         A3

//DEVICE SETTINGS LCD Shield
#define BUTTON            A0
#define LCD_PINS          9,8,A2,A5,A3,A4

#define URI_MAX          20
#define LAN_ON_MASK      0x80
#define DHCP_ON_MASK     0x40
#define FAIL_COUNT_MASK  0x3f

You will note the "tubes" variable uses the array of structs trick so that I can statically allocate a mix of strings and integers into its initialization. This array provides the factory standard calibration constants for Caesium and the most commonly found Geiger tubes that are compatible with the NetRad shield, as you can see the board was built to be very flexible.

Also note that Arduinos PROGMEM macro keeps this data firmly out of SDRAM and EEPROM memory and in the flash. This makes it necessary to later load the data from the flash when needed but saves the SDRAM from instant overflow and frees up more working memory. I also encountered a problem with putting floats into the flash so dividing the mantissa and exponent become necessary.

#define MAX_TUBE 8
#define CUSTOM_TUBE 1
typedef struct 
{
  const char* label;
  uint32_t    convFactorMag;  //something breaks if u put floats into PROGMEM..
  int8_t      convFactorExp;
} TubeEntry;

// this is ugly but.. dont blame me http://arduino.cc/en/Reference/PROGMEM
prog_char tubeLabel1[] PROGMEM = "UNKNOWN";
prog_char tubeLabel2[] PROGMEM = "CUSTOM_TUBE";
prog_char tubeLabel3[] PROGMEM = "LND_712";
prog_char tubeLabel4[] PROGMEM = "SBM_20";
prog_char tubeLabel5[] PROGMEM = "J408GAMMA";
prog_char tubeLabel6[] PROGMEM = "J306BETA";
prog_char tubeLabel7[] PROGMEM = "INSPECTOR";
prog_char tubeLabel8[] PROGMEM = "CRM100";

//floats dont seem to be healthy in PROGMEM either!
// 1CPM = ?? uS/hr
PROGMEM TubeEntry tubes[MAX_TUBE] =
  {
    // label,    uSv/hr factor
    { tubeLabel1, 0,     0 },   // ???      - use nothing!
    { tubeLabel2, 0,     0 },   // ???      - use conversion factor without caring
    { tubeLabel3, 2333, -6 },   // 0.002333 - http://www.lndinc.com/products/348/
    { tubeLabel4, 57,   -4 },   // 0.0057   - http://www.libelium.com/wireless_sensor_networks_to_control_radiation_levels_geiger_counters
    { tubeLabel5, 0,     0 },   // ???      - North Optic
    { tubeLabel6, 0,     0 },   // ???      - North Optic
    { tubeLabel7, 29,   -4 },   // 0.0029   - 
    { tubeLabel8, 0,     0 },   // ???      - 
  }; 

And the definitions of the main memory stores in the system state_t and settings_t.
* state_t is the non-permanent set of operating conditions, it will exist in SDRAM
* settings_t is the permanent data that is loaded and save to the EEPROM. In theory I can make this consume up to 2k however it needs to be loaded into SDRAM when the device is operating, as a result you still need to use it sparingly, or break it into pages.

//static flashed settings
typedef struct
{
  //tube/counter settings
  float    conversionCoefficient; // The conversion coefficient from cpm to µSv/h
  uint32_t readingIntervalMillis;
  uint8_t  tubeModel;
  uint8_t  emaHalfLife;    // the number of samples before 50% redution for Expondential average comptation

  //ether setings
  uint8_t  lanDhcpFails;  //bit 1 lanOn, bit 2dhcpOn, bit 3-8 FailedConsCount
  uint8_t  ipAddr[4];
  uint8_t  gateway[4];
  uint8_t  subnet[4];
  
  //upload server settings
  uint8_t  uploadIPAddr[4];  
  char     uploadURI[URI_MAX];
} settings_t;

// Interrupt mode:
// * For most geiger counter modules:     FALLING
// * Geiger Counter Twig by Seeed Studio: RISING

//dynamic runtime settings
typedef struct
{
  //execution state
  uint8_t state;
  uint8_t lcd_mode;
  bool    lcd_refresh;

  //lan status
  uint8_t  connFailCnt;
  uint32_t dhcpMaintainceTime; // The last connection time to disconnect from the server after uploaded feeds
  uint32_t lastConnectionTime; // The last connection time to disconnect from the server after uploaded feeds  

  //time status
  uint32_t now;  // millisec accumulative counter -- prone to overflow!

  // uptime status..
  uint16_t upTimeMillis; 
  uint8_t  upTimeSec;
  uint8_t  upTimeMin;
  uint8_t  upTimeHours;
  uint8_t  upTimeDays;

  // reading data last 10.. 
  volatile uint8_t eventFlag;  // Event flag signals when a geiger event has occurred
  uint32_t         count;      // Value to store counts per minute
 
  //current event
  uint32_t lastReading;  // millisec accumative counter -- prone to overflow!
  uint32_t totalCount;              
  float    countsPerMinute;
  float    microsievertPerHour;
 
  //Exponential moving average 
  float    emaCountsPerMinute;
  float    emaMicrosievertPerHour;
} state_t;

The Arduino comes with a nice set of headers, the internal unit support is provided by the avr libs. While the LiquidCrystal, Ethernet support the most common of peripheral devices used with by hobbyists.

As you can see the take the static compile time object approach common in embedded software. One note here new and delete don't work in the Arduino compilers so don't even bother trying to use them.

#include <stdint.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <limits.h>
#include <LiquidCrystal.h>
#include <SPI.h>
#include <Ethernet.h>
#include "PrivateSettings.h"

// this holds the info for the device
static settings_t settings;

// holds the control info for the device
static state_t state;

//Web Client      
Client client;

//LCD
LiquidCrystal lcd(LCD_PINS);

One large draw back with the Ardiunos is that printf is missing and the output interfaces tend to be inhomogeneous enough to be frustrating. In the end it annoyed me sufficiently well enough that I ended up writing a facade for the whole mess. It of course adds a slight overhead but in terms debug; the ability to redirect any output to network, serial port or lcd was invaluable.

//Print wrapper system.. cause it sucks without it
class PrintCore
{
public:
  virtual void endl() const = 0;
  virtual void clr() const = 0;
  virtual void p(const unsigned long val) const = 0;
  virtual void p(const unsigned int  val) const = 0;
  virtual void p(const int           val) const = 0;
  virtual void p(const char*         val) const = 0;
  virtual void p(const char          val) const = 0;
};

class PrintLCD : public PrintCore
{
private:
  mutable int line;
public:
  PrintLCD() : line(0) {}
  virtual void endl() const                     { line = (line+1)%2; lcd.setCursor(0,line); }
  virtual void clr()  const                     { lcd.clear(); line = 0; lcd.setCursor(0,0); }
  virtual void p(const unsigned long val) const { lcd.print(val,DEC); }
  virtual void p(const unsigned int  val) const { lcd.print(val); }
  virtual void p(const int           val) const { lcd.print(val); }
  virtual void p(const char*         val) const { lcd.print(val); }
  virtual void p(const char          val) const { lcd.print(val); }
};

class PrintSerial : public PrintCore
{
  virtual void endl() const                     { Serial.println(); }
  virtual void clr()  const                     { }
  virtual void p(const unsigned long val) const { Serial.print(val,DEC); }
  virtual void p(const unsigned int  val) const { Serial.print(val); }
  virtual void p(const int           val) const { Serial.print(val); }
  virtual void p(const char*         val) const { Serial.print(val); }
  virtual void p(const char          val) const { Serial.print(val); }
};

class PrintWebClient : public PrintCore
{
  virtual void endl() const                     { client.println(); }
  virtual void clr()  const                     { }
  virtual void p(const unsigned long val) const { client.print(val,DEC); }
  virtual void p(const unsigned int  val) const { client.print(val); }
  virtual void p(const int           val) const { client.print(val); }
  virtual void p(const char*         val) const { client.print(val); }
  virtual void p(const char          val) const { client.print(val); }
};

//main print wrapper..
class PrintClass : public PrintCore
{
  PrintCore*     core;
  PrintLCD       lcd_mode;
  PrintSerial    serial_mode;
  PrintWebClient client_mode;

public:
  PrintClass() { serial();  }

  //mode
  void serial() { core = &serial_mode; } 
  void lcd()    { core = &lcd_mode;    } 
  void client() { core = &client_mode;    } 
  
  //standard
  virtual void endl() const                     { core->endl(); }
  virtual void clr()  const                     { core->clr();}
  virtual void p(const unsigned long val) const { core->p(val); }
  virtual void p(const unsigned int  val) const { core->p(val); }
  virtual void p(const int           val) const { core->p(val); }
  virtual void p(const char*         val) const { core->p(val); }
  virtual void p(const char          val) const { core->p(val); }  
  
  //commons
  void pln(const char*          val) const { core->p(val); core->endl(); }
  void ulng(const unsigned long val) const { core->p(val); }
  
  //specials
  void prog(const prog_char* data) const
  {
    //IN FLASH(code) print
    //WARNING ardindo have to use special functions conserve sram use
    // this wraps up the print string to use the PSTR flash offload string
    char c=pgm_read_byte(data++);
    while(c != 0)
      {
   p(c);
   c=pgm_read_byte(data++);
      }
  }
  
  void progln(const prog_char* data) const
  {
    prog(data);
    endl();    
  }

  void pad(const int val, int padding=-1) const
  { 
    int mult = 1;
    if (padding > 0)
      {
 //not designed for -ves
 while(--padding > 0)
   mult *= 10;
 
 while((val < mult) &&
       (mult != 1))
   {
     p('0');  
     mult /= 10;
   }
      }
    p((int)val); 
  }
  
  void flt(double val, byte precision) const
  {
    if( precision <= 0) 
      return;
  
    // prints val with number of decimal places determine by precision
    // precision is a number from 0 to 6 indicating the desired decimial places
    // example: printDouble( 3.1415, 2); // prints 3.14 (two decimal places)
    //rounding
  
    unsigned long mult = 1;  //start at 10 for rounding
    byte padding = precision-1;
    while(precision--) mult *=10;
  
    val += 0.5/mult; //round
    
    p(int(val));  //prints the int part
    p('.'); // print the decimal point
    
    unsigned long frac;
    
    //get fraction and remove negative
    if(val >= 0) frac = (val - int(val)) * mult;
    else         frac = (int(val)- val ) * mult;
    
    //padding compute
    unsigned long frac1 = frac;
    while(frac1 /= 10) padding--;
    
    //print padding
    while(padding--) p('0');
    
    //print faction 
    //p(frac,DEC) ;
    p(frac) ;
  }

  //OK getting highly geiger specific now...

  // Just a utility function to nicely format an IP address.
  void ip(const uint8_t* ipAddr)
  {
    for(int i = 0;i < 4; i++)
      {
 if (i!=0) p('.');
 p((int)ipAddr[i]);
      }
  }

  void upTime(bool lng=true)
  {
    const char* comma = PSTR(":");
    pad(state.upTimeDays);      prog(PSTR(" days "));
    if(!lng) endl();
    pad(state.upTimeHours , 2); prog(comma);
    pad(state.upTimeMin   , 2); prog(comma);
    pad(state.upTimeSec   , 2); prog(comma);
    if(lng) pad(state.upTimeMillis, 3); 
  }
  
  void conversionFactor()
  {
    prog(PSTR("ConversionFactor : 1CPM is "));
    flt(settings.conversionCoefficient,4);
    prog(PSTR(" uS/Hr"));
    
    if (settings.conversionCoefficient != 0)
      {
 prog(PSTR("Hence 1 uS/h is : "));
 flt((1.0/settings.conversionCoefficient),4);
 prog(PSTR(" CPM "));
      }
  }
  
  void tubeLabel()
  {
    if (settings.tubeModel < MAX_TUBE)
      {
 prog(PSTR(" - ")); 
 const char* labelProgmemPtr = (const char*)pgm_read_word(&tubes[settings.tubeModel].label);
 prog(labelProgmemPtr); 
      }
  }
};

PrintClass print;

The main user interface to the device is the USB based serial port, this interface is how the device is initially configured and calibrated(and its software is uploaded via this also). The next part of the code will handle user input via this method.

The core routine in this was the "cmdPoll" this would simply gather input coming in from the serial port until a enter is struck. The gathered line is then dispatched for processing in the "cmdParse" function, which compares it to another array of strings and function pointers. This then dispatch's it out to the found command function or dumps the help if needed.

Note that the majority of the commands are build as getter/setters. If ran parameter-less they will dump the current setting. But if a parameter is supplied it will first update it and then dump the new setting. It reduces coding effort and cuts down the mess of commands.

/**************************************************************************/
// Set address
/**************************************************************************/
void cmdSetLanOn(char *args)
{
  if (args)
    {
      if (strtol(args, NULL, 10) != 0)
 settings.lanDhcpFails |= LAN_ON_MASK;
      else
 settings.lanDhcpFails &= ~LAN_ON_MASK;
      state.state = SOFT_RESTART;
    }
  print.prog(PSTR("Lan is set to ")); print.pad((settings.lanDhcpFails & LAN_ON_MASK) != 0); print.progln(PSTR(" Off=0"));
}

void cmdSetDhcpOn(char *args)
{
  if (args)
    {
      if (strtol(args, NULL, 10) != 0)
 settings.lanDhcpFails |= DHCP_ON_MASK;
      else
 settings.lanDhcpFails &= ~DHCP_ON_MASK;
      state.state = SOFT_RESTART;
    }
  print.prog(PSTR("DHCP set to ")); print.pad((settings.lanDhcpFails & DHCP_ON_MASK) != 0); print.progln(PSTR(" Off=0"));
}

void cmdSetFailsUntilReset(char *args)
{
  if (args)
    {
      uint8_t fails = strtol(args, NULL, 10);
      fails = fails & FAIL_COUNT_MASK;
      settings.lanDhcpFails = (settings.lanDhcpFails & ~FAIL_COUNT_MASK) | fails;
      state.state = SOFT_RESTART;
    }
  print.prog(PSTR("Max Fails til reset set to ")); print.pad(settings.lanDhcpFails & FAIL_COUNT_MASK);
}

void readIP(char *args, uint8_t* ipAddr)
{
  if (args)
    {
      char* part = strtok(args, ".");
      for(int i = 0;(i < 4) && (part != NULL); i++)
 {
   ipAddr[i] = strtol(part, NULL, 10);
   part = strtok(NULL, ".");
 }
      state.state = SOFT_RESTART;
    }
}

void cmdSetIpAddr(char *args)
{
  readIP(args, settings.ipAddr);
  print.prog(PSTR("IP set to: ")); print.ip(settings.ipAddr);
}

void cmdSetGateway(char *args)
{
  readIP(args, settings.gateway);
  print.prog(PSTR("Gateway set to: ")); print.ip(settings.gateway);
}

void cmdSetSubnet(char *args)
{
  readIP(args, settings.subnet);
  print.prog(PSTR("Subnet set to: ")); print.ip(settings.subnet);
}

void cmdSetUploadIpAddr(char *args)
{
  readIP(args, settings.uploadIPAddr);
  print.prog(PSTR("Upload IP address set to: ")); print.ip(settings.uploadIPAddr);
}

void cmdSetUploadURI(char *args)
{
  if (args)
    {
      uint32_t len = strlen(args);
      if (len > (URI_MAX-1))
 len = (URI_MAX-1);

      state.state = SOFT_RESTART;
      memset(settings.uploadURI, 0   , URI_MAX);
      memcpy(settings.uploadURI, args, strlen(args));
    }
  print.prog(PSTR("Upload URI set to: ")); Serial.println(settings.uploadURI);
}

/**************************************************************************/
// TUBE Settings
/**************************************************************************/
void cmdSetTube(char *args)
{
  if (args) 
    {
      settings.tubeModel = (uint32_t)strtol(args, NULL, 10);  
      if (settings.tubeModel > MAX_TUBE)
 settings.tubeModel = 0;
      if (settings.tubeModel != CUSTOM_TUBE)
 {
   //ugly -- but something goes wrong with floats in PROGMEM...
   uint32_t convFactMag = (pgm_read_word(&tubes[settings.tubeModel].convFactorMag));
   int8_t   convFactExp = (pgm_read_byte(&tubes[settings.tubeModel].convFactorExp));
   float convFactor = convFactMag;
   
   if (convFactExp < 0)
     while (convFactExp != 0)
       {
  convFactor /= 10.0;
  convFactExp++;
       }
   else if (convFactExp > 0)
     while (convFactExp != 0)
       {
  convFactor *= 10.0;
  convFactExp--;
       }
   settings.conversionCoefficient = convFactor;
 }
    }
  print.prog(PSTR("Tube model: ")); print.pad(settings.tubeModel); print.tubeLabel();
  print.endl();
  print.conversionFactor();  
}


void cmdSetConversionFactor(char *args)
{
  if (args) 
    settings.conversionCoefficient = strtol(args, NULL, 10);  
  print.conversionFactor();
}

void cmdSetReadingInterval(char *args)
{
  if (args) 
    settings.readingIntervalMillis = strtol(args, NULL, 10);  
  print.prog(PSTR("Reading Interval set to ")); print.ulng(settings.readingIntervalMillis); print.endl();
}

void cmdSetEmaHalfLife(char *args)
{
  if (args) 
    settings.emaHalfLife = strtol(args, NULL, 10);  
  print.prog(PSTR("EMA half life set to ")); print.pad(settings.emaHalfLife); print.endl();
}

/**************************************************************************/
// Print out the current device ID
/**************************************************************************/
void cmdReading(char *args)
{
  unsigned long deltaMillis = elapsedTime(state.lastReading);
  float guessCPM  = state.count * 60000 / deltaMillis;
  float guessDose = guessCPM * settings.conversionCoefficient;
  print.prog  (PSTR("UpTime:"));       print.upTime(); print.endl();
  print.progln(PSTR("Current:"));
  print.prog  (PSTR(" - time    : ")); print.ulng(deltaMillis); print.progln(PSTR("msec")); 
  print.prog  (PSTR(" - counts  : ")); print.pad(state.count);  print.progln(PSTR("counts")); 
  print.prog  (PSTR(" - guessed : ")); print.flt(guessCPM,4);   print.progln(PSTR("CPM"));  
  print.prog  (PSTR(" - guessed : ")); print.flt(guessDose,4);  print.progln(PSTR("uS/hr"));     
  print.progln(PSTR("Prior:"));
  print.prog  (PSTR(" - reading : ")); print.flt(state.countsPerMinute,4);     print.progln(PSTR("CPM"));
  print.prog  (PSTR(" - reading : ")); print.flt(state.microsievertPerHour,4); print.progln(PSTR("uS/hr"));
  print.progln(PSTR("Average:"));
  print.prog  (PSTR(" - reading : ")); print.flt(state.emaCountsPerMinute,4);     print.progln(PSTR("CPM"));
  print.prog  (PSTR(" - reading : ")); print.flt(state.emaMicrosievertPerHour,4); print.progln(PSTR("uS/hr"));
}

void cmdSettings(char *args)
{
  print.progln(PSTR(" GIGEIR TUBE:"));
  print.prog  (PSTR("   -  tube    : ")); print.pad(settings.tubeModel); print.tubeLabel(); print.endl();
  print.prog  (PSTR("   -  conv    : ")); print.flt(settings.conversionCoefficient,4); print.progln(PSTR(" uS/Hr = 1CPM"));  
  print.prog  (PSTR("   -  interval: ")); print.ulng(settings.readingIntervalMillis); print.endl();
  print.prog  (PSTR("   -  EMAHalf : ")); print.pad(settings.emaHalfLife); print.endl();
  print.progln(PSTR(" Lan:"));
  print.prog  (PSTR("   -  lan     : ")); print.pad((settings.lanDhcpFails & LAN_ON_MASK ) != 0); print.progln(PSTR(" (0=off)"));
  print.prog  (PSTR("   -  dhcp    : ")); print.pad((settings.lanDhcpFails & DHCP_ON_MASK) != 0); print.progln(PSTR(" (0=off)"));
  print.prog  (PSTR("   -  fail lim: ")); print.pad(settings.lanDhcpFails & FAIL_COUNT_MASK); print.endl();
  print.prog  (PSTR("   -  ip      : ")); print.ip(settings.ipAddr);       print.endl();
  print.prog  (PSTR("   -  gateway : ")); print.ip(settings.gateway);      print.endl();
  print.prog  (PSTR("   -  subnet  : ")); print.ip(settings.subnet);       print.endl();
  print.progln(PSTR(" UPLOAD:"));  
  print.prog  (PSTR("   -  upip    : ")); print.ip(settings.uploadIPAddr); print.endl();
  print.prog  (PSTR("   -  upuri   : ")); Serial.println(settings.uploadURI);

}

void cmdReset(char *args)
{  
  state.state = RESET;
  print.progln(PSTR("RESETING...\n"));
}

void cmdSave(char *args)
{  
  eeprom_write_block((byte *)&settings, 0, sizeof(settings_t));
  print.progln(PSTR("Settings saved\n"));
}

void cmdHelp(char *args)
{  
  print.progln(PSTR(" GIGEIR TUBE:"));
  print.progln(PSTR("   -  tube : set the tube type(autoset others)")); 
  print.progln(PSTR("   -  conv : set the 1CPM -> uS/Hr conversion"));  
  print.progln(PSTR("   -  inter: set the rate of reading compution/upload"));  
  print.progln(PSTR("   -  half : set the EMA half life"));  
  print.progln(PSTR(" Lan:"));
  print.progln(PSTR("   -  lan  : turn lan on/off"));
  print.progln(PSTR("   -  dhcp : turn dhcp on/off"));
  print.progln(PSTR("   -  ip   : set static ip"));
  print.progln(PSTR("   -  fail : number of upload fails before reset"));
  print.progln(PSTR("   -  gate : set static gateway"));
  print.progln(PSTR("   -  snet : set static subnet"));
  print.progln(PSTR(" UPLOAD:"));  
  print.progln(PSTR("   -  upip : set a custom server ip"));
  print.progln(PSTR("   -  upuri: set a custom server URI"));
  print.progln(PSTR(" ACTIONS:"));
  print.progln(PSTR("   -  read : display a detailed reading"));
  print.progln(PSTR("   -  sett : display the settings"));
  print.progln(PSTR("   -  reset: reset the device")); 
  print.progln(PSTR("   -  save : save any changes to settings")); 
  print.progln(PSTR(""));
}

//MAX cmds are 5 + 20 in args .. Ardiunos dont have masses of space
#define CMD_MAX_LENGTH 25
typedef struct 
{
  const char* cmd;
  void (*func)(char*);
} CmdEntry;

void cmdParse(char* msg)
{
  static CmdEntry cmdTable[] =
    {
      { "read",   cmdReading          },
      { "sett",   cmdSettings          },
      { "lan",    cmdSetLanOn            }, 
      { "dhcp",   cmdSetDhcpOn          },
      { "fail",   cmdSetFailsUntilReset  },
      { "ip",     cmdSetIpAddr          },
      { "gate",   cmdSetGateway          },
      { "snet",   cmdSetSubnet          },
      { "upip",   cmdSetUploadIpAddr     },
      { "upuri",  cmdSetUploadURI        },
      { "tube",   cmdSetTube   },
      { "conv",   cmdSetConversionFactor },
      { "inter",  cmdSetReadingInterval  },
      { "half",   cmdSetEmaHalfLife      },
      { "reset",  cmdReset   },
      { "save",   cmdSave   },
      { NULL,     NULL                   }
    };

  char* cmd = strtok(msg, " ");
  char* arg = strtok(NULL, " ");
  
  if (cmd == NULL) 
    {
      cmdHelp(NULL);
      return;
    }

  for (uint32_t i=0; cmdTable[i].cmd != NULL; i++)
    {
      if (!strcmp(cmd, cmdTable[i].cmd))
        {
   (*cmdTable[i].func)(arg);
   return;
        }
    }
  cmdHelp(NULL);
}

void cmdPoll()
{
  static char     msg[CMD_MAX_LENGTH+1];
  static uint8_t  msg_idx = 0;

  bool process = false;
  while (Serial.available())
    {
      if (msg_idx == 0)
 {
   print.endl();
   print.prog(PSTR("CMD> "));
 }
      
      char c = Serial.read();
      if (c == '\r')
 {
   process = true;
 }
      else if ( c == '\b')
 {
   Serial.print(c);
   if (msg_idx > 0)
            msg_idx--;
 }
      else if(!process)
 {
   if (msg_idx < CMD_MAX_LENGTH)
     {
       // normal character entered. add it to the buffer
       Serial.print(c);
       msg[msg_idx++] = c;
       msg[msg_idx]   = 0;
     }
 }
      else
 {
   //empty serial port... of junk after enter..
 }
    }

  if (process)
    {
      print.endl();
      Serial.println(msg);
      cmdParse(msg);
      
      msg_idx = 0;
      msg[msg_idx] = '\0';
    }
}

The Geiger is not always connected to a serial port. User input can also come in from the pull up switch added on the LCD shield and wired to the A1 pin. The button at the moment is setup to just toggle the LCD display modes. As you can see the lcd is refreshed in proxy from the button push via lcd_refresh this is so that other areas of the code can also trigger the lcd to change.

void handleButton()
{
  static bool buttonPushState = false;
  static bool prevButtonPushState = false;

  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  buttonPushState = (digitalRead(BUTTON) == HIGH);
  
  if (buttonPushState != prevButtonPushState)
    {
      prevButtonPushState = buttonPushState;
      if (buttonPushState == true)
 {
   state.lcd_mode = (state.lcd_mode+1)%LCD_MAX_MODE;
   state.lcd_refresh = true;
 }
    }

  if (state.lcd_refresh)
    {
      state.lcd_refresh = false;
      print.lcd(); 
      print.clr();
     
      switch (state.lcd_mode)
 {
 case LCD_AVE_READING:
   // LCD output
   print.prog(PSTR("Av:"));
   print.flt(state.emaCountsPerMinute,1);  
   print.endl();
   print.flt(state.emaMicrosievertPerHour,4);
   break;
 case LCD_INST_READING:
   // LCD output
   print.prog(PSTR("Rw:"));
   print.flt(state.countsPerMinute,1);  
   print.endl();
   print.flt(state.microsievertPerHour,4);
   break;
 case LCD_STATE:
   print.prog(PSTR("St:"));
   switch (state.state)
     {
     case RESET:        print.prog(PSTR("RST"));  break;
     case SOFT_RESTART: print.prog(PSTR("SRST")); break;
     case INIT_DHCP:    print.prog(PSTR("DHCP")); break;
     case OBTAIN_IP:    print.prog(PSTR("IP"));   break;
     case INIT_CLIENT:  print.prog(PSTR("CLNT")); break;
     case NORMAL:       print.prog(PSTR("NORM")); break;
     case CONNECT:      print.prog(PSTR("CNCT")); break;
     case SEND_DATA:    print.prog(PSTR("SEND")); break;
     case RESP_DATA:    print.prog(PSTR("RESP")); break;
     default:           print.prog(PSTR("????")); break;
     }
   print.endl();
   print.prog(PSTR("Fails:"));
   print.pad(state.connFailCnt,2);
   break;   
 case LCD_UPTIME:
   print.prog(PSTR("Up:"));
   print.upTime(false);
   break;
 }
      print.serial();
    }
}

Additionally for accutate computations of the radiation readings special care needs to be taken to compute the amount of time that has elapased between readings and updates of the clock. Poorly written clocking code will often drift quite dramatically, this destroying your readings accuracy. The key line is the "state.now = millis();" which samples and records the time atomically as possible, The calculations then account for timer overflows and computes the full time delta using the current and prior samples of the clock.

/**************************************************************************/
// calculate elapsed time. this takes into account rollover.
/**************************************************************************/
unsigned long elapsedTime(unsigned long startTime)
{
  unsigned long stopTime = millis();
  
  if (startTime >= stopTime)
    return startTime - stopTime;
  else
    return (ULONG_MAX - (startTime - stopTime));
}

void updateTime()
{
  unsigned long prevNow = state.now; 
  state.now = millis();
  
  unsigned long deltaMillis;
  if (state.now >= prevNow)
    deltaMillis = state.now - prevNow;
  else
    deltaMillis = ULONG_MAX - (prevNow - state.now);

  state.upTimeMillis += deltaMillis;
  if ( state.upTimeMillis >= 1000)
    {
      state.upTimeSec   += state.upTimeMillis / 1000;
      state.upTimeMillis = state.upTimeMillis % 1000;

      state.lcd_refresh |= (state.lcd_mode == LCD_UPTIME);
      
      if (state.upTimeSec >= 60)
 {
   state.upTimeMin += state.upTimeSec / 60;
   state.upTimeSec  = state.upTimeSec % 60;
   
   if (state.upTimeMin >= 60)
     {
       state.upTimeHours += state.upTimeMin / 60;
       state.upTimeMin    = state.upTimeMin % 60;
       
       if (state.upTimeHours >= 24)
  {
    state.upTimeDays += state.upTimeHours / 24;
    state.upTimeHours = state.upTimeHours % 24;
  }   
     } 
 }
    }
}

The final interface on the device is the Ethernet port. Using the port is a bit complex but luckly the Arduino has a Ethernet lib complete with a Web client. As a result all I really need to do is generate a raw HTTP get request to a waiting server with the current raw reading for it to save. And at some point later read back its response.

/**************************************************************************
    Send data to server 
**************************************************************************/
void handleClient() 
{
  if(state.state == CONNECT)
    {
      if (client.connected()) 
 {
   return;
   print.prog(PSTR("Disconnecting."));
   client.stop();
 }
      
      print.endl();
      // Try to connect to the server
      print.prog(PSTR("Connecting."));
      if (client.connect()) 
 {
   print.progln(PSTR("Connected."));
   state.lastConnectionTime = millis();
   
   // clear the connection fail count if we have at least one successful connection
   state.connFailCnt = 0;     
   state.state = SEND_DATA;
   state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
 }
      else 
 {
   state.connFailCnt++;
   print.prog(PSTR("Failed count:")); print.pad(state.connFailCnt);
   print.endl();
   uint8_t fail_max = settings.lanDhcpFails & FAIL_COUNT_MASK;
   if((fail_max >0) && (state.connFailCnt > fail_max))
     state.state = SOFT_RESTART;
   else
     state.state = NORMAL;
   state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
 }
    }
  else if(state.state == SEND_DATA)
    {
      print.prog(PSTR("Upload."));
      
      print.client();
      print.prog(PSTR("GET ")); 
      print.p(settings.uploadURI); 
      print.prog(PSTR("?cpm=")); 
      print.flt(state.countsPerMinute,1);
      print.prog(PSTR("&mSh=")); 
      print.flt(state.microsievertPerHour,4);
      print.progln(PSTR(" HTTP/1.0")); 
      print.endl();
      print.serial();

      state.state = RESP_DATA;
      state.lcd_refresh |= (state.lcd_mode == LCD_STATE);

      print.progln(PSTR("Uploaded."));
    }
  else if(state.state == RESP_DATA)
    { 
      //handle data transmission
      while(client.available()) 
 {
   // Echo received strings to a host PC
   char c = client.read();
   Serial.print(c);
 }

      if (!client.connected()) 
 {
   Serial.println("disconnected.");
   client.stop();
   state.state = NORMAL;
   state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
 }     
    }
}

This bring us to the main body of the code. The Arduino's dont have a main per-say. They use 2 functions. The "setup" function is a run once piece of code just after reset. The "loop" function is basically an endless while(1) loop. The Chibi board is setup so that Ardiuno uses "wdt_enable(WDTO_8S)" as a hardware stop watch. If the stop watch runs out the hardware resets and starts fresh(In this case its was set to 8 secs). The later "wdt_reset()" restarts the timer freash for the next iteration of the loop. This way the Geiger can never get stuck in an unexpected state.

Also note the only interrupt that is registered in the system is the onPulse function. This is the most critical piece of the code, it counts the output from the Geiger tube. Note that its not protected from multiple interrupts, this is because the geiger tubes dead time is sufficently long enough(and the code is next to atomic anyway) for the code to complete and be ready for the next event.

The main loop can be summarized as
* handle reset
* handle serial cmds
* handle user feedback of radiative events
* compute new total uptime
* compute new reading if sampling period is over
* handle button input LCD output
* handle network setup
* handle web client communication

/**************************************************************************/
/*!
    On each falling edge of the Geiger counter's output,
    increment the counter and signal an event. The event 
    can be used to do things like pulse a buzzer or flash an LED
*/
/**************************************************************************/
void onPulse() 
{
  state.count++;
  state.eventFlag = 1;  
}

/**************************************************************************/
// main setup and loop...
/**************************************************************************/
void setup() 
{
  delay(20);

  // fill in the UART file descriptor with pointer to writer.
  //fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
  
  // The uart is the standard output device STDOUT.
  //stdout = &uartout ;

  // set up the LCD's number of columns and rows: 
  lcd.begin(8, 2);

  // Print a message to the LCD.
  lcd.print("Reset!");

  Serial.begin(57600);

  print.progln(PSTR("Reseting..")); // tick to the usb console 

  // put radio in idle state
  pinMode(RADIO_SELECT, OUTPUT);
  digitalWrite(RADIO_SELECT, HIGH);    // disable radio chip select
  
  // reset the Wiznet chip
  pinMode(WIZNET_RESET_PIN, OUTPUT);
  digitalWrite(WIZNET_RESET_PIN, HIGH);
  delay(20);
  digitalWrite(WIZNET_RESET_PIN, LOW);
  delay(20);
  digitalWrite(WIZNET_RESET_PIN, HIGH);
  
  // get the device info
  eeprom_read_block((byte*)&settings, 0, sizeof(settings_t));
  
  // init the control info
  memset(&state, 0, sizeof(state_t));
  //
  // enable watchdog to allow reset if anything goes wrong      
  wdt_enable(WDTO_8S);

  // Attach an interrupt to the digital pin and start counting
  // Note:
  // Most Arduino boards have two external interrupts:
  // numbers 0 (on digital pin 2) and 1 (on digital pin 3)
  attachInterrupt(GEIGER_INTERRUPT_PIN, onPulse, RISING);

  state.state = SOFT_RESTART;
  state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
  state.count = 0;
  
  // kick the watch dog
  wdt_reset();

  print.progln(PSTR("Reset..")); // tick to the usb console 
}

void loop() 
{
  //polling loop.. very fast.. pfft
  
  // kick the dog only if we're not in RESET state. if we're in RESET
  // we will just let the device gracefully reset via the watchdag time out..
  if (state.state != RESET) wdt_reset();        
  else                      state.lcd_refresh |= (state.lcd_mode == LCD_STATE);

  if (state.state == SOFT_RESTART) 
    {
      state.state = INIT_DHCP;
      state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
    }

  cmdPoll();

  // user events -- poll the command line for any input
  if (state.eventFlag)
    {
      // Advertise a geiger event
      state.eventFlag = 0;    // clear the event flag for later use
      
      print.prog(PSTR(".")); // tick to the usb console 
      
      tone(PIN_SPKR, 1000);      // beep the piezo speaker
      digitalWrite(PIN_LED, HIGH); // flash the LED
      delay(20);                  
      digitalWrite(PIN_LED, LOW); 
      noTone(PIN_SPKR);          // turn off the speaker pulse
    }
  
  // up time update -- to fast loosing to many millis
  updateTime();

  if (settings.readingIntervalMillis == 0) return;

  // uS per hour calc and dump...
  unsigned long deltaMillis = elapsedTime(state.lastReading);
  if (deltaMillis > settings.readingIntervalMillis)
    {
      state.lastReading = millis();
      // loop now cut down to about whatever the UPDATE has been changed to
      
      // count current cpm run..
      state.countsPerMinute = (float)state.count * 60000.0 / (float)deltaMillis;
      state.count = 0;
      
      // Convert from cpm to õSv/h with the pre-defined coefficient
      state.microsievertPerHour = state.countsPerMinute * settings.conversionCoefficient;
      
      //EMA compute
      float alpha = 2.0/((float)(settings.emaHalfLife+1));
      if (state.emaCountsPerMinute == 0)
 {
   state.emaCountsPerMinute     = state.countsPerMinute;
   state.emaMicrosievertPerHour = state.microsievertPerHour;
 }
      else
 {
   state.emaCountsPerMinute = alpha*state.countsPerMinute 
     + (1.0 - alpha)*state.emaCountsPerMinute;
   state.emaMicrosievertPerHour = alpha*state.microsievertPerHour
     + (1.0 - alpha)*state.emaMicrosievertPerHour;
 }

      print.endl();
      print.prog(PSTR("Up: ")); print.upTime();    print.prog(PSTR(" "));    
      print.flt(state.countsPerMinute,1);  print.prog(PSTR("CPM ")); 
      print.flt(state.microsievertPerHour,4);    print.prog(PSTR("uS/hr")); 
      print.prog(PSTR(" EMAve: "));
      print.flt(state.emaCountsPerMinute,1);     print.prog(PSTR("CPM ")); 
      print.flt(state.emaMicrosievertPerHour,4); print.prog(PSTR("uS/hr"));       

      state.lcd_refresh |= (state.lcd_mode == LCD_AVE_READING) ||
 (state.lcd_mode == LCD_INST_READING);
    }

  handleButton();

  if ((settings.lanDhcpFails & LAN_ON_MASK) != 0)
    {
      if ((settings.lanDhcpFails & DHCP_ON_MASK) != 0)
       {
         //// boot and handle dhcp
   //if (state.state == INIT_DHCP)
         //  {
         //    EthernetDHCP.begin(macAddress, 1);
         //    state.state = OBTAIN_IP;
   //    state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
         //  }
         //else
   //  handleDHCP();
       }
      else 
        {
          //no dhcp.. straight to client
         if ((state.state == INIT_DHCP) ||
             (state.state == OBTAIN_IP))
     {
       state.state = INIT_CLIENT;
       state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
       
       //need to fill in these
       Ethernet.begin(macAddress,
        settings.ipAddr, 
        settings.gateway,
        settings.subnet); 
       print.progln(PSTR("Static Ethernet setup.")); // tick to the usb console 
     }
       }

      if (state.state == INIT_CLIENT)
       {
   //boot and handle web client
   //client.stop();
   client.init(settings.uploadIPAddr, 80);
         state.state = NORMAL;
   state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
       }
      else if (state.state >= NORMAL)  //only 1 per watch dog tick.. hence else if
       {
         if (deltaMillis > settings.readingIntervalMillis)
           {
             // send data if ready
       if (state.state == NORMAL)
  {
    state.state = CONNECT;
    state.lcd_refresh |= (state.lcd_mode == LCD_STATE);
  }
           }
   
   handleClient();
        }
    }
}

Sunday, October 16, 2011

Homemade Geiger Counter - Part 2 -- The hardware

Homemage Geiger Counter - Part 2 -- Hardware build

The main objective here is cheap and some what accurate.

Allow me to clarify this point. Reversed biased PN junctions(avalanche photo-diode et el) are very desirable as a Geiger counters
* They are cheap to mass produce using common manufacturing processes.
* If the circuitry and PN diodes are well chosen then the counter can be more accurate and faster than a Geiger tube or even scintillation crystals.

The problem is post Fukushima Geiger counters where in such high demand that anything that ticks in the presence of radiation was acceptable. Basically all of these are PN junction based devices, that are badly slapped together devices rushed out to a sellers market. What is worst is that these cheap devices where selling up of 500 USD a pop shortly after the event. A quick check of amazon shows me that the same models are now selling at around the 200 USD mark after having peaked at around 1500 USD!

I figure if I going to get crap it may as well be crap i have created for 1/15 of the price. So the device I have is around the 100 USD mark with usb and networking capabilities.



I used a Kited setup that the Tokyo hacker space member Akiba put together.


It involves an Ardiunio base board called Chibi(bottom in the picture). And a second shield they called a NetRAD(top in the above), which is basically a board with a LAN networking chip and the Geiger counter circuitry.


The final shield is an LCD and button. As you can see it was just hacked together with a cheap a prebuilt 600yen LCD board and a pull up switch.

The NetRADs specs are here http://tokyohackerspace.org/ja/project/tokyo-hackerspace-netrad-geiger-shield. And the Dev history of the board is here. http://www.tokyohackerspace.org/en/blog/tokyo-hackerspacerdtn-geiger-shield-dev-history. It makes for a very interesting read.

A fuller set of picture are visible here

I was hoping to do a full tear-down of the device but I simply can't seem to get the time.

I have also noticed the device seems to have a wide working range, a series of readings can vary by any thing upto 100% from the average. If this is due to the nature of radioactivity or something iffy in the Geigers power supply I don't know. Besides this the device is still excellent. The readings still average out to a stable reading over a 10min period.

Wednesday, October 12, 2011

dyndns for an iphone server

Dynamic IP are common at residential properties and are quite a problem if you want your home server to be constantly reachable from outside. The solution is a dynamic IP tracking host like dyndns. Since im working from a hacked iphone a second problem is that perl doesnt seem to exist for it. So using a standard package is a bit of an issue. Here is a quick hacked up ruby daemon that can handle the dyndns updates.

Save this ruby script at /var/mobile/dyndns/update_ip.rb, (dont forget to chmod it to runable.)

UPDATE: It appears that dyndns gets peeved if you keep updating to the same IP. The code has been patched for it.
#!/usr/bin/env ruby

require 'base64'

host = "example.dyndns.tv"
auth = Base64.encode64('user:password')
timeout = 600

while 1
  output = ""
  puts "Discovering current IP.."
  
  # current ip resolve from checkip.dyndns.com
  IO.popen("telnet checkip.dyndns.com 80", "w+") do |pipe|
    output = pipe.readline
    puts output
  
    pipe.puts "GET / HTTP/1.1"
    pipe.puts "Host: checkip.dyndns.com"
    pipe.puts "Connection: close"
    pipe.puts ""
    pipe.puts ""
  
    puts "SENT ip query.."
    output = pipe.readlines.join("");
    puts "---------------RESPONSE--------------------"
    puts output
    puts "---------------  END   --------------------"
    pipe.close_write
  end
  
  if output =~ /.*Current IP Address: ([0-9]+.[0-9]+.[0-9]+.[0-9]+).*/
     ip = $1
     ip = ip.chomp
     puts ""
     puts "DETECTED At: " + ip 
  end

  oldip = ""
  File.open('currentIP.txt', 'r') do |f|
    oldip = f.gets
    oldip = oldip.chomp
  end
  puts "PRIOR At: " + oldip   

  if oldip == ip 
    puts "MATCH sleeping..."
  else
    File.open('currentIP.txt', 'w') do |f|
      f.puts ip
    end  

    exit    
    puts ""
    puts "installing new IP..."
    
    #http://login:password@members.dyndns.org/nic/update?hostname=example.dyndns.tv&myip=99.99.99.99
    IO.popen("telnet members.dyndns.org 80", "w+") do |pipe|
      output = pipe.readline
      puts output
    
      pipe.puts "GET /nic/update?hostname=" + host + "&myip=" + ip + " HTTP/1.1"
      pipe.puts "Host: members.dyndns.org"
      pipe.puts "User-Agent: codeslimjim/1.0 rubyscript/1.0"
      pipe.puts "Authorization: Basic " + auth
      pipe.puts "Connection: close"
      pipe.puts ""
      pipe.puts ""
    
      puts "SENT new ip.."
      output = pipe.readlines
      puts "---------------RESPONSE--------------------"
      puts output
      puts "---------------  END   --------------------"
      pipe.close_write
    end
  end

  #timeout for x sec..
  sleep timeout
end

Then install the plist daemon
touch /Library/LaunchDaemons/com.dyndns.plist
chmod go-wrx /Library/LaunchDaemons/com.dyndns.plist
nano /Library/LaunchDaemons/com.dyndns.plist

Paste in the code
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.dyndns</string>
    <key>ProgramArguments</key>
    <array>
        <string>/var/mobile/dyndns/update_ip.rb</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>UserName</key>
    <string>mobile</string>
    <key>WorkingDirectory</key>
    <string>/var/mobile/dyndns</string>
</dict>
</plist>

And boot/load up the daemon and test
launchctl load -w /Library/LaunchDaemons/com.dyndns.plist
ps aux | grep dyndns 

Remmber if u need to shut it down use
launchctl unload -w /Library/LaunchDaemons/com.dyndns.plist

Saturday, October 1, 2011

Homemade Geiger Counter - Part 1 -- The basics

My Blog has been kind of quiet lately. the Reason is that I have been focused on building a Geiger counter. So here is what I have learnt.

Geiger basics. A Geiger counter is basically a simple event counting device. A Geiger tube connects to an amp, noise filter, comparator circuit and some form of counting system.

The Geiger tube is basically a outer housing, containing gas and a cathode and anode. The housing can be metal or even ultra thin foils, The gas is normally some inert gas and the cathode/anode can be made using anything from glass to metals.

The Geiger tube is charged to high voltage, this excites the gas inside the tube. In this state no current is yet flowing and the tube appears as an open circuit. When radiation is incident on the tube it must first travel through the outer housing, and strike one of the excited gases particles. This collision will tear free one or more electrons and these with then bump into other atoms and tear free more electrons and thus creating an avalanche effect.

Note carefully the limitations of the device,
1) the radiation must travel through the tubes outer housing and strike a gas atom. So the outer housing of the tube will absorb some particles without causing a detectable effect. Also particles can travel right thought the tube without causing an event. The size and thickness of the tube also effects how sensitive it is to radiation. But designers of the tube need to consider mechanical damage from shocks and contact with other items. Most directional tubes have a very thin wall made of mica that is very delicate. In summary there are a large range of tubes with different sensitivity to the various forms of radiations.

2) The main incident particle transfers energy to a single electron knocking it off the atom, this electron then hits another, these 2 electrons then knock more free and so on until there are no more electrons to be knocked free, this is the avalanche effect. This sea of freed electrons flows out the cathode, and eventually the gas recharges to high energy state. Now if the radiation was to knock a second electron free or second unconnected event was to hit the gases in the tube at the same time the tube is discharging then there is no way to tell the difference. This period is the tubes dead time. A Geiger counter that is taken into a high radiation area will suddenly go silent this is not broken it is because there are so many events hitting the tube that avalanche will never stop and the comparator will never detect the switch between Geiger's tubes quiescent state and the avalanche discharge state

3) This is a very simple device all it does is count events. Using it for anything other than the number counts requires some level of interpretation.

What these limitations mean is that what caused the event that was counted cannot be determined. If we assume your measuring air and it was an alpha particle then it is unlikely to penetrate skin and is therefore a lower dose. If it was gamma it will likely pass through your body and the wall behind u, thus giving it a bit more potential to cause some real damage to you. (Its not that simple but sufficient to highlight my point). So the conversion of counts to dosage is highly dependent on the type of tube your using, and the procedure that you used to measure it, what is causing the count (IE the spectrum of radioactive substances your measuring), and what the interaction of the substance is with the living being being dosed. In summary the devices dose conversion calibration needs to understood rather carefully, and the device used correctly.

Now Geiger counters don't necessarily need to be made with a a Geiger tube. A cheaper an less accurate design is to apply a similar circuit to a standard solar panel or LDR that is covered to prevent the entry of normal light but allow gamma radiation to still be incident on the panel or junction. These designs will measure radiation however, solar panel's and LDRs are not designed for radiation readings and so these designs have even more drawbacks than a standard Geiger counter.

Unfortunately post fukushima, Geiger counter prices sky rocketed and supplies became rarefied. These cheaper devices where affordable and available in a market with buyers who were desperate to get their hands on something/anything that can read the radiation. As a result these cheaper devices have been sold en-mass in japan and few customers realize the difference between what they have purchased and what the real item is. Keep in mind that the device does work so its technically not illegal to sell it as a radiation measurement instrument... Its just that if the customer is expecting a real device then they are getting hoodwinked by their own limited understanding. I have tried explaining this to Joe average before only it never seems to work. The discussion seems to always work out like explaining the difference between 24 Carat Gold and 12 Carat Gold to a person who only wants to understand that Gold is yellow.

Also note that Geiger tubes are far from the ultimate device to measure radiation a scintillator crystal is capable of producing varying colors and intensity of light as radiation strikes it, devices using these crystals are a level(and cost) far beyond the humble Geiger counter.

Thursday, September 22, 2011

Too Smart to survive the company culture

The power of stupidity is Amazing. Its rampant in companies and I have had a serious headache trying to understanding what was going on.. I have finally come to understand it...

Here is a scenario based of it a recent event.

3 team members work for a department. They are given a blind choice:
A) perform an hard task and earn the company 120 dollars.
B) perform an easy task and earn the company 90 dollars.

* The choice is blind. you don't know what other people will choose until your committed.
* Both tasks take the same amount of time and become easier with more people.
* The tasks require at least 2 people to be doable.
* The reward doesn't scale with number of people.
* An employee who does not earn at least 30 dollars will be fired.


Now lets say that you the intelligent engineer realize the more your company earns the better off you are also. So your choice is A. Your assuming your rational team mates will also choose A the maximize the companies profit to 120. each of you would earn 40 dollars and keep your jobs.

Now what if your 2 team mates choose B. You have no support and can't complete your task.. you earn 0 and get fired. Your team mates each earn 45, far above the goal and get bonuses! And the company makes 90 dollars. but now they need to hire a replacement for you and have to pay out the bonuses to your team mates. So over all the company and you got screwed.. Furthermore the company has lost the good worker and reinforced the laziness of the other team mates... Also the new hire is subjected to this environmentally reinforced bad behavior and is high likely to fall prey to it on the next task...

Tuesday, August 23, 2011

How to locate and replace the next line

How to locate and replace the next line in a block of XML.. Ie find "findme" and then replace 80 on the next line with 100

perl -pe 'if($on == 1) { if(m/c>80/) {s/80/100/}; $on=0 };  $on=1 if m/findme/;' test.txt


<a>
  <b>findme</b>
  <c>80</c>
</a>
<a>
  <b>ignoreme</b>
  <c>80</c>
</a>

Thursday, August 4, 2011

Code Profiling classes

A simple code profiling class that will profile for the entry point "TIME()" to the closing curly "}". Enjoy

#include <iostream>
#include <iomanip>
#include <list>
#include <time.h>
#include <math.h>

//test header
#include <unistd.h> 
#include <stdlib.h>

class TimerStats
{
public:
    typedef std::list<TimerStats*> TimerStatsList;
    typedef time_t                 TimeUnit;

    static TimerStatsList& statsList()
    {
        static TimerStatsList list;
        return list;
    }

    static void printAll(std::ostream& out)
    {
        TimerStatsList::iterator it;
        for(it  =  statsList().begin();
            it !=  statsList().end();
            it++)
        {
            out << *(*it);
        }
    }

    static TimeUnit getTime()
    {
        return time(NULL);
    }

    TimerStats(const char* file_, const uint32_t line_) : 
        file   (file_),
        line   (line_),
        count  (0),
        sum    (0),
        sum_sqr(0),
        max    (0),
        min    (0)
    {
        statsList().push_back(this);
    }
    
    void addValue(double val)
    {
        count++;
        sum += val;
        sum_sqr += (val*val);
        if (count == 1)
        {
            max = val;
            min = val;
        }
        else
        {
            max = max > val ? max : val;
            min = min < val ? min : val;
        }
    }
    
    const char*     file;
    const uint32_t  line;

    uint32_t      count;
    double        sum;
    double        sum_sqr;
    double        max;
    double        min;   

private:
    friend std::ostream& operator<<(std::ostream& out, TimerStats const& stats);
};

std::ostream& operator<<(std::ostream& out, TimerStats const& stats)
{
    double ave =0;
    double sdev=0;

    if (stats.count > 0) 
    {
        ave  = stats.sum/stats.count;
        sdev = stats.sum_sqr/stats.count - (ave*ave);
        sdev = sqrt(sdev);
    }

    out << " Count:"    << std::setw(10) << stats.count   
        << " Ave:"      << std::setw(10) << ave
        << " Sdev:"     << std::setw(10) << sdev
        << " Max:"      << std::setw(10) << stats.max     
        << " Min:"      << std::setw(10) << stats.min
        << " Total:"    << std::setw(10) << stats.sum
        << " " << stats.file << ":" << stats.line
        << "\n";

    return out;
}

class Timer
{
public:
    Timer(TimerStats& stats_) :
        stats(stats_),
        start(TimerStats::getTime())
    {}
        
    ~Timer() 
    { 
        stats.addValue(TimerStats::getTime()-start);
    }

private:
    TimerStats& stats;
    TimerStats::TimeUnit start;
};

#define TIME() static TimerStats stats##__LINE__(__FILE__,__LINE__); \
     Timer timer##__LINE__(stats##__LINE__)

int main(int argc, char const * const *argv)
{
    {
        TIME();
        
        for(int i= 0; i < 5; i++)
        {
            TIME();
            for(int j= 0; j < 5; j++)
            {
                TIME();
                // sleep(1);
                int k = rand() * rand();
                
                std::cout << k << "\n";
                while (k > 0) k--;
            }
        }
    }

    TimerStats::printAll(std::cout);
}

Thursday, July 14, 2011

Data decimation

The some times u just cant send out 100s of MBs of data, hell sometimes you cant process it. Basically you need to decimate the results into a usable shape. The average joe understands averages and maximums. So break it down based on what you need to see for the current level of zoom.


function decimate($points, $bounds)
{
  $lngcnt = 20;
  $latcnt = 20;
  
  $cdl = array();
  foreach($points as &$p)
  {
    $lngper = ($p['long'] - $bounds[1])/($bounds[3] - $bounds[1]);
    $latper = ($p['lat']  - $bounds[0])/($bounds[2] - $bounds[0]);
    $latidx = intVal($latcnt*$latper);
    $lngidx = intVal($lngcnt*$lngper); 
    $val = $p['cpm'];

    #create arrays if needed
    if(!isset($cdl[$lngidx]))
    {
      $cdl[$lngidx] = array();
    }

    if(!isset($cdl[$lngidx][$latidx]))
    {
      $cdl[$lngidx][$latidx] = array();
      $cp = &$cdl[$lngidx][$latidx];
      $cp['sum'] = $val;
      $cp['cnt'] = 1;
      $cp['max'] = $val;
      $cp['max_long'] = $p['long'];
      $cp['max_lat']  = $p['lat'];
      # $cp['min'] = $val;
      # $cp['min_long'] = $p['long'];
      # $cp['min_lat']  = $p['lat'];
    }
    else
    {
      $cp = &$cdl[$lngidx][$latidx];
      $cp['sum'] += $val;
      $cp['cnt']++;
      if ($val > $cp['max'])
      {
        $cp['max']      = $val;
        $cp['max_long'] = $p['long'];
        $cp['max_lat']  = $p['lat'];
      }
      # if ($val < $cp['min'])
      # {
      #   $cp['min']      = $val;
      #   $cp['min_long'] = $p['long'];
      #   $cp['min_lat']  = $p['lat'];
      # }
    }
  }
  
  $res = array();
  foreach($cdl as $lng => &$lngcdl)
  {
    foreach($lngcdl as $lat => &$cp)
    {
      if($cp['cnt'] > 0)
      {
        $pnt = array();
        $pnt['cpm']      = $cp['sum']/$cp['cnt'];
 $pnt['long1']    = ($bounds[3] - $bounds[1])*$lng/$lngcnt + $bounds[1];
 $pnt['lat1']     = ($bounds[2] - $bounds[0])*$lat/$latcnt + $bounds[0];
 $pnt['long2']    = ($bounds[3] - $bounds[1])*($lng+1)/$lngcnt + $bounds[1];
 $pnt['lat2']     = ($bounds[2] - $bounds[0])*($lat+1)/$latcnt + $bounds[0];
        $pnt['max']      = $cp['max']     ;
        $pnt['max_long'] = $cp['max_long'];
        $pnt['max_lat']  = $cp['max_lat'] ; 
        # $pnt['min']      = $cp['min']     ;
        # $pnt['min_long'] = $cp['min_long'];
        # $pnt['min_lat']  = $cp['min_lat'] ;
 array_push($res, $pnt);
      }
    }
  }
  return $res;
}

php sqlite3 queries.

Getting sqlite into php is easy as Here is an example query. Of course im forced to use sqlite in the first place because im running an iphone as a web server.

try 
{
  $db = new PDO("sqlite:data.sqlite");
}
catch(PDOException $e)
{
  echo $e->getMessage();
  echo "<br><br>Database -- NOT -- loaded successfully .. ";
  die( "<br><br>Query Closed !!! $error");
}

#away with ye hackers..
$bbox = sqlite_escape_string($_GET['b']);
$bounds = split(" ", $bbox);
if ( count($bounds) != 4) { die("[]"); } 

$sql  = $db->prepare("SELECT * FROM readings WHERE lat >= ? AND long >= ? AND lat <= ? AND long <= ? LIMIT 1000");
if (! $sql->execute($bounds)) { die("[]"); } 
$query = $sql->fetchAll(PDO::FETCH_ASSOC); 

$decimated = decimate($query, $bounds);
jsonOutput($decimated);

A php sqlite to json converter page

Next I build basically a quick converter page to convert an sqlite query result into json

function jsonOutput($query)
{
  #json convert
  $ofirst = true;
  print "[\n";
  foreach ($query as $row)
  {
    if($ofirst) $ofirst = false;
    else       print ",\n";
    
    $first = true;
    print "{";
    foreach ($row as $key => $value)
    {
      if($first) $first = false;
      else       print ",";
      if(is_numeric($value))
        print $key . ': ' . $value;
      else
        print $key . ': "' . $value . '"';
    }
    print "}";
  }
  print "\n]\n";
}

And ajax test page

While building the radiation map prototype i put together a ajax data grabber here it is

<html>
<head>
<script type="text/javascript">
function loadData(str)
{
var xmlhttp;    
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
    }
  }
xmlhttp.open("GET","/safecast/data.php?lat=1&long=2",true);
xmlhttp.send();
}
</script>
</head>
<body>

<div id="txtHint">
<button onclick="loadData('')">Get Data</button>
</div>

</body>
</html>

Tuesday, July 12, 2011

perl to translate chars

$a =~ tr/[A-Z]/[a-z]/; 

Try it with
perl -e '$a = "ABCD"; $a =~ tr/[A-Z]/[a-z]/; print $n;'$a;

Monday, July 11, 2011

google maps radiation maping for safecast

Just spent the day hacking together a google maps heat map mash up for safecast.org here is the prototype. Still needs alot of work... but it should be better than what they have at the moment..

Radiation Map Prototype type for Safecast.org

Will blog more about how it all works later.. But interesting thing to note is that its running of my iPhone web-server... very geeky...

Update: Ok.. its fallen over 4 times already... its an iphone calm down people...

Friday, June 24, 2011

Golden rules of Software engineering

  1. One mustn't imply the existing code is a reeking pile of spaghetti...
  2. Managers understand that no engineering feat is impossible, why cant Software engineers..
  3. All problems no matter how complex can be solved with meetings and PowerPoint slides...
  4. Bug trackers are for time tracking and customer communication not fixing problems in source code....
  5. When defining how an interface looks the words "Best and World Class" implies exactly what colors, layout and content is needed...
  6. Designers and Customers are gods. If they tell you to they want the confirm button to play Mozart at the top volume then by god it needs to play Mozart irrespective of what the user feedback says...

Sadly I have encountered each and every one of these items in my own personal experience.

Tuesday, May 24, 2011

Iphone web server with php

I have an old iphone 3G that I upgraded to a 4G. Now I use it as my lowpower server. To do this you first need to jailbreak it and of course you need to have a home wifi setup. Serving private web pages is one of its uses. Here is the basic setup

Here is how to install lighttpd and php
  1. Jail break the iPhone using whatever setup you like
  2. Find the iPhones IP address in the wifi config area
    1. set your network IP to static.. and choose an IP at the end or out of your DHCP allocation range. (so that the DHCP doesnt keep moving your server around, nothing is worst for NAT setup..)
  3. Change the default passwords
    1. SSH into the iPhone using the username "root" and password "alpine"
    2. "root"s original password is "alpine"
    3. "mobile"s original password is also "alpine"
    4. double check for other accounts in /etc/passwd
      passwd 
      #enter old/new passwords for root
      su mobile
      passwd 
      #enter old/new passwords for mobile
      
  4. Remove password based ssh (makes it harder to hack in)
    1. copy/generate a ssh key from the entry machine.
    2. install the ssh key into the iPhone
    3. ssh into the phone (as mobile) and execute
      mkdir -p ~/.ssh
      chmod 0700 ~/.ssh
      touch ~/.ssh/authorized_keys
      chmod 600 ~/.ssh/authorized_keys
      cat >> ~/.ssh/authorized_keys
      #now paste in ssh key and hit crtl-D
      cat ~/.ssh/authorized_keys
      ls -al ~/.ssh/authorized_keys
      #confirm u see: -rw------- 1 mobile mobile ... /var/mobile/.ssh/authorized_keys
      
    4. Confirm login by ssh'ing in a diffrenet window
    5. BACKUP SSH KEY BEFORE CONTINUING. Its your way in from here on...
    6. Remove the password login.
      su
      #enter root password
      nano  /etc/ssh/sshd_config
      
    7. Now edit/add the following lines
      PasswordAuthentication no
      AllowTcpForwarding no
      X11Forwarding no
      AllowUsers mobile
      
    8. Restart iPhone (maybe /usr/sbin/sshd is sufficent to restart the sshd)
    9. Check the setting by ssh'ing with the key/username. You will be prompted for login as: but it should reject before it asks a password.
  5. Installing web server software
    1. run apt-get and install packages
      su
      #enter root password
      apt-get install php
      apt-get install lighttpd
      
    2. if you are missing apt get you will need to open Cydia and install the "AptBackup" package
  6. setup web software
    1. create the directory/files erc needed
      mkdir -p /htdocs/site/
      mkdir -p /htdocs/log/
      chmod 777 /htdocs/log/
      mkdir /etc/lighttpd/
      
    2. Configure the site.
      nano /etc/lighttpd/lighttpd.conf
      
    3. Copy this into the file:
      include "mod_fastcgi.conf"
      server.document-root = "/htdocs/site/"
      #server.port = 8080
      
      server.username = "_sshd"
      server.groupname = "_sshd"
      
      server.bind = "localhost"
      server.tag ="lighttpd"
      server.errorlog = "/htdocs/log/error.log"
      accesslog.filename = "/htdocs/log/access.log"
      
      server.modules = (
      "mod_access",
      "mod_accesslog",
      "mod_fastcgi",
      "mod_rewrite",
      "mod_auth",
      "mod_fastcgi"
      )
      index-file.names = ( "index.html", "index.php" )
      
    4. Configure the php module.
      nano /etc/lighttpd/mod_fastcgi.conf
      
    5. Copy this into the file:
      fastcgi.server = ( ".php" =>
       ( "localhost" => 
        ( "bin-path" => "/usr/bin/php-cgi", "socket" => "/tmp/php.socket")))
      

    6. Setup a trial page
      nano /htdocs/index.php
      
    7. Copy this into the file:
      <?
          echo "Welcome:", $_SERVER['REMOTE_ADDR'];
      
          echo "< br >< br >Heres some uptime data:< br >";
          passthru("uptime");
          echo "< br >< br >Heres whats running:< br >";
          passthru("ps | sed 's/$/< br >/'");
      ?>
      

    8. Boot the server manually and check it all
      lighttpd -f /etc/lighttpd/lighttpd.conf
      

    9. Open a browser and enter http://your_iphones_static_ip and you should see the trial page
  7. Right now lighttpd has to be manually started, it wont do it automatically.
    1. Set it to autoboot via a launchd control file at /Library/LaunchDaemons/com.lighttpd.plist the launchd process will start it and keep it running if it should crash or be killed.

    2. touch /Library/LaunchDaemons/com.lighttpd.plist
      chmod go-wrx /Library/LaunchDaemons/com.lighttpd.plist
      nano /Library/LaunchDaemons/com.lighttpd.plist
      

    3. Paste in the contents
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
          <key>Label</key>
          <string>com.lighttpd</string>
          <key>ProgramArguments</key>
          <array>
              <string>/usr/sbin/lighttpd</string>
              <string>-f</string>
              <string>/etc/lighttpd/lighttpd.conf</string>
          </array>
          <key>RunAtLoad</key>
          <true/>
          <key>KeepAlive</key>
          <true/>
          <key>UserName</key>
          <string>root</string>
          <key>WorkingDirectory</key>
          <string>/htdocs</string>
      </dict>
      </plist>
      

    4. load it with(and check)
      launchctl load -w /Library/LaunchDaemons/com.lighttpd.plist
      ps aux | grep light  
      

    5. You can shut it down again with:
      sudo launchctl unload -w /Library/LaunchDaemons/com.lighttpd.plist
      

I note that you need to keep the iPhone plugged into its charger to keep it connected to the wifi and alive. Otherwise it goes into a low power mode and shut off the wifi link, there by disconnecting your server.

I also iphones php package is rather limited in some aspects. For example create_socket doesn't work.

I also note that the user who is running the server seems bad.

Refer
http://www.esrun.co.uk/blog/lighttpd-php-on-the-iphone/
http://www.cyberciti.biz/tips/lighttpd-restrict-or-deny-access-by-ip-address.html
http://stackoverflow.com/questions/1181751/send-iphone-http-request-to-apache-php-webserver
http://arcoleo.org/dsawiki/Wiki.jsp?page=Autostart%20MySQL%20on%20Mac

quick wake on lan script with netcat

An WOL packet is simply the 6 bytes of FF followed by the 6 byte MAC address repeated 16 times.

So to script it first generate the wake packet.. Im assuming the mac is AA:BB:CC:DD:EE:FF and Im storing the packet so I can use it on a machine without xxd..hence the file dump.
ETHER="aa:bb:cc:dd:ee:ff"
ETHER2=`echo $ETHER | sed "s/://g"`
ETHER3="${ETHER2}${ETHER2}${ETHER2}${ETHER2}"
ETHER4="FFFFFFFFFFFF${ETHER3}${ETHER3}${ETHER3}${ETHER3}"
echo ${ETHER4} | xxd -r -p > wake.packet

Then send out the wake packet with Netcat. You can send it directly to the server or to your routers broadcast address it should goto either port 7 or 9. For the example i choose 192.168.1.200 and port 7.
netcat -c -v -u -n -x -p 80 192.168.1.200 7 < wake.packet
The contents of the packet should looks like this:
$ xxd wake.packet
0000000: ffff ffff ffff aabb ccdd eeff aabb ccdd  ................
0000010: eeff aabb ccdd eeff aabb ccdd eeff aabb  ................
0000020: ccdd eeff aabb ccdd eeff aabb ccdd eeff  ................
0000030: aabb ccdd eeff aabb ccdd eeff aabb ccdd  ................
0000040: eeff aabb ccdd eeff aabb ccdd eeff aabb  ................
0000050: ccdd eeff aabb ccdd eeff aabb ccdd eeff  ................
0000060: aabb ccdd eeff                           ......

Post iPhone UI design

UI design is always one of the most annoying and unportable tasks a programmer faces. (Aside from the problems with "professional" designs that have more photoshop effects that common sense). Todays devices are changing drastically the iPhone was the linchpin on this. It has fundamentally redefined the human machine interface. Mouse point and click interfaces now need to handle the the finger pinch and flick. Static window layouts wont live much longer in a world of rotatable and portable hardware.

The problem boils down to the fact that every joe and his dog has his own UI implementation. And after reviewing several of these in a post iPhone/Android world I have come the conclusion that they just are not going to survive beyond a few years from now. Now more that ever the clean separation between Model/Controller/View needs to be handled or your code is going to get outdated and become a maintenance time sucker.

I think Androids Fragment idea is the way to go on this. Release the constraints on the placement/layout and reconsider your UI from the perspective of a set of fragments or UI chunks that are correlated and interacting. Think dynamic and fluid layouts. This way it allows the segments to be shuffled around to meet the needs or your next port and code reuse.

While doing all this its best to stay as farway as you can from the Frameworks Implementation specific classes. Especially the pointless rewrite/rebuild of the string and networking class that seems to be in every framework. Wrap them if you must use them but don't let it them anywhere near your core implementation.

Refer:
http://developer.android.com/videos/index.html#v=WGIU2JX1U5Y

Sunday, May 22, 2011

forcing i386 installs on 64bit

Sometimes you just cant find a 64 bit version of stuff.. force that 32 bit deb to install with.

sudo dpkg --force-architecture -i downloaded_package.deb

rtti to traits for auto uuiding classes

There are times when you want automatically UID or index a class. The rtti is string based and I am assuming that simple Hashing of of that string is not sufficient. So here is a quick hack up of trait class to achieve the result.

#include <iostream>
#include <stdint.h>
#include <map>
#include <typeinfo>

class RttiCasterBase
{
private:
  class RttiRegFactory
  {
  private:
    typedef std::map<std::string, uint32_t> RttiIDMap;
    
    //0 is the bad marker..
    uint32_t g_uid;
    RttiIDMap rttiIDMap;
    
    RttiRegFactory() :
      g_uid(1)
    {}
    
  public:
    static RttiRegFactory& instance()
    {
      static RttiRegFactory instance_;
      return instance_;
    }
    
    uint32_t getID(std::string rttiName)
    {
      if (rttiIDMap.find(rttiName) != rttiIDMap.end())
 return rttiIDMap[rttiName];
      rttiIDMap[rttiName] = g_uid;
      return g_uid++;
    }
  };
protected:
  static uint32_t  lookup(std::string rttiID)
  {
    return RttiRegFactory::instance().getID(rttiID);
  }
  
public:
  RttiCasterBase() {}
  virtual ~RttiCasterBase() {}
  
  virtual uint32_t      uid() = 0;
  virtual std::size_t   size() = 0;
  virtual std::string   rttiID() = 0;
};

template<typename T>
class RttiCasterImp : public RttiCasterBase
{
public:
  typedef T Type;
  uint32_t uid_;
  
  RttiCasterImp() :
    RttiCasterBase()
  {
    uid_ = RttiCasterBase::lookup(typeid(T).name());
  }
  
  ~RttiCasterImp() {}

  virtual uint32_t uid()
  {
    return uid_;
  }
  
  virtual std::size_t size()
  {
    return sizeof(T);
  }
  
  virtual std::string   rttiID()
  {
    return typeid(T).name();
  }
  
  T* cast(void* buf)
  {
    return reinterpret_cast<T*>(buf);
  }
  
  void print(std::ostream& out)
  {
    out << "uid:  "   << uid()    << std::endl
 << "size: "   << size()   << std::endl
 << "rttiID: " << rttiID() << std::endl
 << std::endl;
  }
};

class A {};
class B { int var; };
class C { char stuff[10]; };

int main(int argc, char const * const *argv)
{
  RttiCasterImp<A> InfoA;
  RttiCasterImp<B> InfoB;
  RttiCasterImp<C> InfoC;
  RttiCasterImp<B> InfoD;
  
  InfoA.print(std::cout);
  InfoB.print(std::cout);
  InfoC.print(std::cout);       
  InfoD.print(std::cout);       
}

Output looks like:
$ a.exe
uid:  1
size: 1
rttiID: 1A

uid:  2
size: 4
rttiID: 1B

uid:  3
size: 10
rttiID: 1C

uid:  2
size: 4
rttiID: 1B

Social circle.

Google has put out a new cross-referencing set of pages. They basically tie your profile page to your know friends and contacts. Check it out for your self.

http://www.google.com/s2/search/social#socialconnections

cleaning up ubuntus file usage

FIRST OF ALL get into the apt-get and clear out the apps you dont want. Then purge, autoclean, autoremove, orphan remove all the remain pieces until nothing is left

Heres the basic clean up (plus some setup)
sudo apt-get -y install gtkorphan logrotate localepurge fslint
sudo apt-get autoremove
sudo apt-get clean all 
sudo apt-get autoclean all
sudo localepurge
dpkg -l | grep '^rc'| cut -d' ' -f3 | xargs dpkg -P

When you take out packages the best way is to --purge or autoremove them
 sudo apt-get --purge remove bluez-utils bluez-gnome 
 sudo apt-get autoremove bluez-utils bluez-gnome 

Then execute gtkorphan and keep killing unneed deps until it cant find any.

The Disk Usage Analyzer to sort out the biggest space wasters. Its found at Applications->Accessories->Disk Usage Analyzer.

There is also the computer janitor. Its at System > Administration > Computer Janitor.

And you can also hunt down the dead log files and other crap in you system with fslint.

You may need to loop around the whole process a few times to get it really clear

Several useless packages that tend to be installed by default are(sorry list is very short I got hacking before I thought about writing it down);
  • gnome-pilot
  • mesu-utils
Refer http://maketecheasier.com/8-ways-to-maintain-a-clean-lean-ubuntu-machine/2008/10/07

PHP: getting the users IP address

$_SERVER['REMOTE_ADDR']; 

c++ chomping white space

A Bit of code to chomp strings in c++

#include <string>
#include <iostream>

int main()
{
  std::string str;
  getline(std::cin, str);

  std::cout << "'" << str << "'" << std::endl;
    
  std::string::size_type pos = str.find_last_not_of("\n \t");
  if(pos != std::string::npos) 
  {
    str = str.substr(0, pos+1);
  }
    
  std::cout << "'" << str << "'" << std::endl;
}

Quick way list your LAN machines

Here is a quick way to grab the Ethernet MACs and IPs of the machines that are up on your LAN

arp -a

Suming a list of numbers of the shell

Manipulating lists on the shell can be done with awk. The most common one I encounter is summing numbers. Here is a quick bit of shell magic to sum up the size of all log files below a certian dictionary. Awk has much more power than this however.

find . -type f | grep "\.log" | xargs ls -al | sed "s/  */ /g" | cut -d " " -f 5 | awk -F: '{total+=$1} END{print total}'

Friday, May 6, 2011

Shell repeat/do/while for a count

Gahh.. simple thing but annoying its for bash shell

 i=245; while [ $i -ne 33 ]; do  echo $i; i=`expr $i - 1`; done

Wednesday, April 27, 2011

Earthquakes in japan between

Quick video showing earthquakes in Japan between 9 March and 14 March. 1 hour ~ 1 second. Big one is around 1:17.

Suming a list of numbers

cat numbers.txt | awk -F: '{total+=$1} END{print total}'

Monday, March 14, 2011

Japan Earthquake Crisis and Info site in english and Japanese

Japan crisis response page on google http://www.google.com/crisisresponse/japanquake2011.html#resources"

If you wish to donate go to the google page and donate to the japanese red cross.



If your in japan the power will be out in a set schedule around the effected areas as it is diverted to cover the missing power planets

The info is here http://www.tepco.co.jp/index-j.html but the site is very overloaded

The groups are as follows
 第1グループ 6:20~10:00 16:50~20:30 
 第2グループ 9:20~13:00 18:20~22:00
 第3グループ 12:20~16:00 
 第4グループ 13:50~17:30
 第5グループ 15:20~19:00

The number of missing is over 20000, dont be idiots with the sites.

The news is says they are looking for medical staff, medical supplies and portable electric generators.
They are also cooling for basic food water, cooked rice(they need to conserve water) and bread.

The Japanese metorology infomation site lists the series of aftershocks we have had here (as you can see there are over 30).

http://www.jma.go.jp/en/quake/quake_sindo_index.html

Saturday, March 12, 2011

Earthquake Marunouchi building Tokyo Station March 11, 2011



This is the initial quake. On the 19th floor. The buildings seismic meter picked it up as magnitude 4.5. I was in the express elevator on the way up and got kicked out about 10-15 seconds into it, felt like minutes. The sounds you are hearing on the video are the elevators wires and the building walls groaning. The guys you can see are walking back and forth because the building is moving around us like a jumping castle or fair ride. Hence the crappy filming.



This is the first after shock. and im on the 22 flour now. They have continued all night I have counted 6 at least. The buildings seismic meter picked it up as little over magnitude 4. I was back at my desk by this point. The building across the road is about 70-100 meters away. As you can see its moving and we are moving. Not nearly as bad as the first but still its clear to see the effects of quake on a high rise

Tuesday, March 1, 2011

Debuging random segvs

This code is a simple and Focused segv catcher, it is designed for transient and random errors, the ones where you never know when or if they are going to happen. The idea is to use a local varable to scope the monitered area and keep tabs of the last line of code with the jump function. There is a streaming mech to gather input params and other data along the way(ie you can log the local vars in it after the fact). Then when a segv actually happens it wakes up and informs you of where it feel over and what the logged data was.

#include <csignal>
#include <iostream>
#include <stdlib.h>
#include <sstream>
 
namespace SigCatchDebug
{
void (*original)(int);
bool installed = false;
std::string   file_;
int           line_  = 0;

std::stringstream data;

void debug_sig(int type)
{
    if(line_ != 0)
    {
        std::cout << "Signal Caught!!!" << std::endl
                  << "last trace loc:" << file_ << ":" << line_ << std::endl
                  << "----- data was ---- "                     << std::endl
                  << data.str() << std::endl
                  << std::endl;
    }

    if(original != SIG_DFL ||
       original != SIG_IGN)
        (*original)(type);
    
    std::exit(EXIT_FAILURE);  //<<-- this really needs to be here!
}

class AutoScope
{
public:
    AutoScope(const char* file,
              const int   line)
    {
        if(!installed)
        {
            installed = true;

            original = std::signal (SIGSEGV,debug_sig);
            if (original == SIG_ERR)
            {
                std::cout << "signal implosion" << std::endl;
            }            
            //std::cout << "signal installed" << std::endl;
        }
        
        file_        = file;
        line_        = line;

        data.str("");
    }

    template<typename T>
    inline AutoScope& operator<<(const T& t) throw()
    {
        try
        {
            data << t;
        }
        catch (...)
        {}
        return *this;
    }

    ~AutoScope()    
    {
        //std::cout << "signal disable" << std::endl;
        line_ = 0;
    }

    void jump(int line)
    {
        line_ = line;
    }

};

}

void evil_random_crashing_func()
{
    static count = 0;
    char** test = NULL;

    SigCatchDebug::AutoScope sigScope(__FILE__,__LINE__);
    sigScope << "test: " << test << "\n"
             << "count: " << count;

    sigScope.jump(__LINE__);
    if(count == 3)
        test[0] = "ABCDEFG";

    sigScope.jump(__LINE__);
    if(count == 4)
         std::cout << "evil_random_crashing_func: " << test[0] << std::endl;
    
    std::cout << "Evil " << count << std::endl;
    count++;
}

void evil_random_crashing_func2()
{
    char** test = NULL;

    test[0] = "ABCDEFG";
    std::cout << "evil_random_crashing_func2: " << test[0] << std::endl;
}

int main(int argc, char const * const *argv)
{
    try
    {
        evil_random_crashing_func();
        evil_random_crashing_func();
        //evil_random_crashing_func2();
        evil_random_crashing_func();
        evil_random_crashing_func();
        
    }
    catch (std::exception const &e)
    {
        std::cout << "Unexpected exception: " <<  e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unexpected exception" << std::endl;
    }
    return 0;
}