Reading: Refer to the official Python documentation on https://docs.python.org. The BeaglePlay has Python version 3.11 installed, so it is best to look at the documentation specifically for that version. You can use this documentation to find out more about the library classes and their methods (such as strings), how to open files, and many other things.
In this lab, you will write a program that returns a unique ID number for your device. You will need this capability in later labs.
You do not need any Internet access for this lab.
Internet-of-Things (IoT) applications typically involve more than an embedded system. They are much more interdisciplinary. In addition to embedded devices, an IoT application may also use databases, cloud computing, and even machine learning and artificial intelligence technology. The defining characteristic of an IoT application is in how multiple embedded devices interact and share data.
One archetypal scenario is a collection of embedded devices that are sensing the environment and that upload their sensor data to the cloud where a machine learning algorithm uses that information to make predictions about the environment's future behavior. In some IoT applications, commands are issued to the embedded devices as a group, and each device uses actuators to manipulate the environment based on the information all the devices have gathered collectively.
This framework for an IoT application implies that the devices are networked and have access to data storage (perhaps in the cloud), in addition to having the sensors and actuators needed for the intended application. We will explore these various aspects of IoT applications in future labs.
Data gathered by each device in an IoT application will be stored with data gathered from all other devices. Thus, it is important that every device has a unique ID number that can be used to identify from which device a particular data item has come. In this lab, you will write a program that generates such an ID number for your device.
Here are the requirements of the unique ID number you need to generate:
It must be unique. The ID number must not be shared with any other device that appears, has ever appeared, or might one day appear in the system.
It must be stable. That is, the ID number must not be subject to changes in the environment in which the device is located. For example, the device's IP address is not appropriate since IP addresses are frequently reassigned over the lifetime of a device.
It must be non-volatile. The ID number must be consistent across device reboots.
It must be time independent. The ID number must not be subject to change, even if the device is decommissioned or is moved to a different application. The ID number must be tied to the device and not an arbitrary assignment that is only meaningful in the scope of the application.
It must be hardware independent. Changes in the device hardware should not change the device's ID number.
It is challenging to meet all of these requirements at once. For this lab, we will use the MAC address of the eth0 Ethernet interface. MAC addresses are made unique by the manufacturer over the scope of all network interfaces that have ever been made by anyone. They also meet the stability, non-volatility, and time independence requirements. However, a change of network hardware might cause the MAC address to change, which is why we will use the MAC address of the Ethernet interface that is soldered to the board (making hardware changes unlikely).
It should be noted that most network software stacks allow the administrator to override the hardware MAC address of an interface. This is rarely done, however, and such an override is only in the software. The physical interface is not affected.
On Linux systems, you can find the MAC address of the eth0 network interface by looking in the file /sys/class/net/eth0/address. If the system does not have an interface named eth0, that file won't exist. However, there is a folder in /sys/class/net for each existing interface, so one could choose an alternative interface if eth0 does not exist. We will simply return an error code if there is no eth0 interface.
The MAC address appears in the address file as six 8-bit hex values separated by ':' characters. For example, 34:08:e1:85:10:1f. Ethernet MAC addresses are 48 bits.
We will want to return a single 64-bit unsigned integer holding the combined 48-bit value. This means that we'll need to do some processing on the text in the address file.
Create a file named lab-02.py on your BeaglePlay.
Your program should contain a function that returns the system ID. It should look like:
def get_system_id(): # Write code here! return 0
The main program should simply call get_system_id and print the result. Here is the complete main program:
id = get_system_id() print("System ID: %012lX" % id)
Splitting the logic of get_system_id into its own function will make it easier to reuse in later labs.
The format specifier %012lX means to print with zero-fill (in case the number has one or more leading zero digits), with a field width of 12. The lX means to print a long ('l') in hex ('X'), using uppercase letters for the hex digits A-F. Using a lowercase 'x' would have caused lowercase digits a-f to be used instead.
For this part, write a version of get_system_id that reads the MAC address from the specified file and prints the raw text it read. Use a `with` block so the file is closed even if an exception is raised (see the blinkLED.py function for the syntax). Use the readline method to read a line of text from the open file.
Modify get_system_id so it converts the text string to a single unsigned 64-bit value. The idea is to collapse the string to only include (hex) digit characters, and then use the int function to convert the number from a string into an integer. For example, first convert "34:08:e1:85:10:1f" to "3408e185101f" and then convert that value into an integer. Return the integer ID instead of zero. Also, remove the printing of the raw text from the get_system_id function. The ID should only be printed by the main program.
HINT: Removing the ':' characters in the string can be done using the replace method in Python's string class (replace the characters with empty strings). The int function takes a second parameter that specifies the base you want to use (base 16 in this case).
Temporarily misspell the name of the file you are opening in get_system_id. When you run the program, it should fail with an exception. While this can be okay, it is usually nicer to catch exceptions and translate them into user-friendly error messages or perform some other sort of remediation.
Wrap your main program in an exception handling block that catches the OSError exception in a manner similar to how the blinkLED.py sample program does. Print a user-friendly error message if the exception is raised.
Submit your final program to Canvas.
Last Revised: 2025-01-28
© Copyright 2025 by Peter Chapin <peter.chapin@vermontstate.edu>