Blinking Logfile Map
I always wanted to see where the readers of my blog are comming from, so I took a picture frame and inserted a world map, a handfull of leds and an arduino. Some soldering and some rubyscripts later I had my hardware geo-aware logfile visualization.
I have a script running at the server that parses new ip adresses from the log file and geocodes them. Than the continent code is sent to my mac where i have a little script that forwards the continent code to the serial port. And finally the arduino in the picture frame is making the leds blink. The whole project was hacked together as a weekend project so the scripts might need some "fine-tuning" :-)
This is my day 5 project for 30DaysOfCreativity
so please read my blog to make the leds blink :-)
The world map is released under a creative commons licence and cand be downloaded here
The geocoding is done using the iprange to continent mappings from countryipblocks.net
The hardware part looks like this (just some leds and resistors)
This is the code i'm running on my arduino int count[] = { 0,0,0,0,0,0,0 };
void setup() {
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
pinMode(8,OUTPUT);
Serial.begin( 9600 );
}
void loop() {
if (Serial.available() > 0) {
byte in = Serial.read();
if (in >= '1') {
count[ in - '1' ] = 50;
}
}
for( int i =0; i < 7; i++) {
if (count[i] > 0) {
digitalWrite( i+2, HIGH);
count[i]--;
} else {
digitalWrite( i+2, LOW);
}
}
delay(10);
}
This is the script that runs on the computer the arduino is connected to
require 'rubygems'
require 'serialport'
require 'socket'
include Socket::Constants
SERIALPORT="/dev/tty.usbserial-A600ag8n"
sp = SerialPort.new( SERIALPORT, 9600, 8, 1, SerialPort::NONE)
sleep(2)
(1..7).each{ |i|
sp.write( "#{i}" )
sleep(0.2)
}
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, '' )
socket.bind( sockaddr )
socket.listen( 5 )
while(true)
client, client_sockaddr = socket.accept
led = client.readline.chomp
sp.write( led )
client.close()
end
sp.close()
and this is the script that runns on my server and geocodes the ip adresses
require 'socket'
include Socket::Constants
def ip2int(ip)
block = ip.split('.')
ipi = block[0].to_i * 256 * 256 * 256 + block[1].to_i * 256 * 256 + block[2].to_i * 256 + block[3].to_i
return ipi
end
def parseCountryFile( fn )
ranges = []
f = File.new("geocodeip/country_ip/#{fn}")
f.each_line { |l|
if l[0] != '#'[0] then
tmp = l.split("-")
start = ip2int(tmp[0].strip)
stop = ip2int(tmp[1].strip)
ranges << [start, stop]
end
}
return ranges
end
def findIp( map, ip )
ipi = ip2int( ip )
map.each{ |continent,ranges|
ranges.each{ |start,stop|
return continent if ipi > start and ipi < stop
}
}
return "4"
end
def process( map, line )
t = Thread.new do
continent = findIp( map, line.split(" ")[0])
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'nikkimac' )
socket.connect( sockaddr )
socket.puts continent
socket.close
puts continent
end
end
map = {}
map["1"] = parseCountryFile("Oceania_range.txt")
map["2"] = parseCountryFile("Asia_range.txt")
map["3"] = parseCountryFile("India_range.txt")
map["4"] = parseCountryFile("Europe_range.txt")
map["5"] = parseCountryFile("Africa_range.txt")
map["6"] = parseCountryFile("South_America_range.txt")
map["7"] = parseCountryFile("North_America_range.txt")
puts "parsing done ..."
f = File.new( "/var/log/apache2/access.log" )
f.seek( 0, IO::SEEK_END );
last = f.pos
while true
sleep(1)
size = f.stat.size
if last < size then
l = f.readline
process( map, l )
while !f.eof
l = f.readline
process( map, l )
end
last = f.pos
end
end
See also:
Day 30 of 30DaysOfCreativity - RadioPI interface module
Day 28 of 30daysofcreativity - testing the radiopi software
Day 26 of 30DaysOfCreativity - prototyping the controls
Day 24 of 30DaysOfCreativity - a breadboard arduino