automatic report of a raspberry pi's ip
In this post we are going to develop a simple reporting service for Raspberry Pis, which will allow them to report their IP address when they boot.
It’s no big surprise that nowadays you see more and more Raspberry Pis appearing in all kind of tech projects. In one of the projects I’ve worked on, we were using five Raspberry Pis to perform a specific task. These Pis, were connected to the TU/e’s WiFi network (see more about this), which has a (literally) huge pool of IP addresses, and we could not use static IPs for them. That being said, every time we powered on each Raspberry Pi, it could get a different IP address.
Finding this IP address could have been a much much easier task if we were working at our home, or at a place where we could have access to the network router. In the latter case, we could just connect to our router’s web interface and see the ip addresses given by the DHCP server, or we could scan our local network using nmap
and check for devices whose MAC address starts with B8:27:EB
(which is an identifier for Raspberry Pis), using the following command:
sudo nmap -sP 192.168.1.0/24 | awk '/^Nmap/{ip=$NF}/B8:27:EB/{print ip}'
This requires nmap installed on your computer. If you have a Mac, you can get nmap from homebrew. If you have Windows, try Wireshark.
Although the above-mentioned solutions seem neat, there are two major problems for our case. The first problem is that we don’t have access to the TU/e’s network routers or to the DHCP servers running there. The second problem is that scanning the whole WiFi subnet is not a very nice solution. Moreover, it is very possible that you will find more Raspberry Pis connected on this network and then you have to determine which ones are yours.
If we didn’t find an elegant solution to this problem, we would have to connect each Raspberry Pi to a screen and keyboard every time it was powered on. This possibility gave me extra motivation to start thinking of a possible solution. Long story short, the idea I started working on was based on the simple question: What if every time a Raspberry Pi boots, it reports its IP address to a server of our own?. We did have a linux machine sitting in the office, so I decided to use this machine to test my idea.
The concept we will example is rather simple:
Let’s start by thinking about the database we will need. We need to store there the hostname and the IP address of each Raspberry Pi. The simplest solution we can give, is a database of the following schema:
mysql> USE rpiobserver;
mysql> DESCRIBE raspberry_pi;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| hostname | varchar(50) | NO | | NULL | |
| ip_address | varchar(100) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
Nothing fancy here. A slightly different approach could be to set the hostname
as the primary key of the database and use only the hostname to identify the entries of the table.
After setting up your database, you need to insert the hostnames of your Raspberry Pis in this table. As you can easily understand, each Raspberry must have a different and unique hostname. By default the Raspberry Pis are configured to use the hostname raspberrypi
, but in our case, if you have 5 devices with the same hostname it would be very difficult to uniquely identify them. In order to change the hostname of your Raspberry Pi, you can follow either one of the following methods.
First method:
From the command line of your Raspberry Pi, run: sudo raspi-config
. Select “Advanced Options” and then “Hostname.” Change the default hostname to the one you want to use.
Second method:
Edit the file: /etc/hostname
with your favorite editor.
Now, let’s continue with the server’s script.
We are going to use Python’s Tornado web framework to develop our solution. As we saw earlier, on the server we want to do two things: to be able to see the IPs of our Raspberry Pis and to provide the opportunity to a Raspberry Pi to register its new IP address. The code that will allow us to provide this functionality is the following:
import torndb
import tornado.web
import tornado.ioloop
home_url = "127.0.0.1"
home_port = 4242
index_page = "index.html"
db_host = "localhost"
class MainHandler(tornado.web.RequestHandler):
def get(self):
db = torndb.Connection(db_host, "db_name", user="db_user", password="db_pass")
coll = db.query("SELECT * FROM raspberry_pi")
db.close()
self.render(index_page, title = "Raspberry Pis", data = coll)
class RaspObserver(tornado.web.RequestHandler):
# handle a get request
def get(self):
ip_add = self.request.arguments["ip"][0]
host = self.request.arguments["hostname"][0]
db = torndb.Connection(db_host, "db_name", user="db_user", password="db_pass")
db.execute("UPDATE raspberry_pi SET ip_address=%s WHERE hostname=%s", str(ip_add), str(host))
db.close()
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", MainHandler),
(r"/report", RaspObserver),
]
tornado.web.Application.__init__(self, handlers)
if __name__ == "__main__":
app = Application()
app.listen(home_port)
tornado.ioloop.IOLoop.instance().start()
Where you should define:
home_url
, the IP address of your serverdb_host
, the IP address of your database server (in case it’s on a different machine)db_name
, the name of your databasedb_user
and db_pass
, the credentials you use to connect to your database (you can also put these two in a separate python/config file and get them from there)A brief explanation of the code:
MainHandler
function acts as our main endpoint. When the user requests this endpoint, we create a collection from our database that contains the current information we have about the IP addresses, and then we pass this collection to a template page, named index.html
(we’ll discuss shortly about this page).RaspObserver
function acts as the endpoint through which each Raspberry Pi will update its IP address. Each Raspberry Pi will make a request to this endpoint and will pass two parameters: its hostname and its IP address. The url of the request will look like: http://ip_of_server/report?hostname=”rpi-hostname”&ip=”XXX.XXX.XXX.XXX”. After this request is made, we get the values passed to the parameters of hostname
and ip
, and then we update the database accordingly.Now let’s have a look at the index.html
template page:
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<table>
<tr>
<td>Hostname</td>
<td>IP Address</td>
</tr>
{% for item in data %}
<tr>
<td>{{ item.hostname }}</td>
<td>{{ item.ip_address }}</td>
</tr>
{% end %}
</ul>
</body>
</html>
This page is very similar to an HTML page, but you can also observe some differences (like the {% for %}
loop). In fact, this file contains the skeleton of the HTML page that will be produced in the end. We iterate over the items of the collection we pass to this template in order to create a table that contains these information. Then, the finalized version of this HTML page is served to us.
Let’s see now what do we need on the side of our Raspberry Pis.
As we discussed before, every time a Raspberry Pi boots, it needs to report its IP address to our server. For this reason we are going to create a Python script, which will make an HTTP request to our server and report the IP address and the hostname of each Raspberry Pi. This code of this script is:
import requests
import platform
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
ip =get_ip_address('wlan0')
hostname = platform.node()
payload = {'hostname': hostname, 'ip': ip}
requests.get('http://server_ip:4242/report', params=payload)
You can save this script on your home directory and name it report.py
.
The last thing that remains to do, is to execute this script every time your Raspberry Pi is booted. You can easily do this using a cron job. This can be done by executing crontab -e
, in order to add your job there. In the end of this file, add a line that will execute your script every time your Raspberry Pi is booted:
@reboot /usr/bin/python /home/pi/report.py