Introduction
My brother-in-law had an idea of how to manage luggage in airports, using simple and cheap Radio-frequency identification (RFID) stickers. In this article, I will show how I created a prototype for the idea.
Because of the shape of the prototype, we decided to call it 'Lampost'.
Background
Since 2010, there has been a drastic drop in the number of lost luggage items at airports, largely due to the introduction of new technology for luggage handling. It has been as low as 5%, but if you have to switch flights the chance of your luggage being misdirected can be as high as 40%, or more. Last year IATA started investigating the possible use of RFID technology to improve the situation, According to their research, the cost is $0.10 per passenger with overall savings of at least $0.20.
The reason is that RFID is very cheap, flexible and needs no maintenance by the customer. A passive RFID is just a simple sticker than can be stuck, stitched or implanted on the luggage item. It requires no battery or other power source to keep it active.
So the general idea is:
- All luggage items will be tagged with RFID stickers on check-in.
- There will be fixed RFID readers anywhere that luggage is transported. There will also be mobile readers for locations where a fixed reader cannot be attached.
- Each reader will report every item that passes it back to the central server application, which will keep track of the items' locations.
- This will include luggage being unloaded from an aircraft and transported to the terminal building, and being loaded onto a carousel.
The Hardware
The brains of the project was a RedBoard, which is an Arduino Uno, with mini USB. I paired it with a WiFi shield, powered by an ESP8266, and paired with a 2.4 GHz antenna. To spare me some soldering for the prototype, I used some stackable headers to plug them together.
The RedBoard, the WiFi shield and then stacked together. Images are courtesy of SparkFun Electronics, photographed by Juan Peña.
The next level of the solution is the RFID reader module. I chose one of the Micro modules made by ThingMagicm which met our requirements. This module comes on its own board, which is not compatible with the Arduino layout so (and to spare me some soldering) I got an Arduino Proto Shield, and asked a local professional to solder the RFID reader module to it (it has open edges - means edge soldering, so I preferred it that way). I then added that as 3rd level using some more stackable headers. The last piece was to attach the antenna to the RFID module.
The RFID reader module. Image is courtesy of atlasRFIDstore.
The Setup
The WiFi shield can communicate with the Arduino board using UART via pins 0/1 (hardware) or 8/9 (software). It can be configured using an on-board switch. I picked the software port to leave the 0/1 pair unused in case I need more hardware that has no such flexibility. The RFID module can work in a number of ways (including a direct hook to the PC and step-by-step programming). For this project, I configured it to work in autonomous mode, for which I hooked it to the PC and used utility software that came with the device. In this mode, the module starts scanning for RFID tags when powered on and makes the data available via the UART whenever a tag is identified.
When soldering, I had the TX/RX lines of the RFID module connected to pins 10/11 of the Arduino, to leave 0/1 free for the same reason as described above.
At this point, all I needed was to power up the Arduino and the hardware part is complete.
The Software
The software solution has three parts. The Arduino is responsible for configuring the 'Lampost' unit, to listen to the RFID module, and to call the web service when a tag is identified. The web service is there to receive the tag's data from the Arduino and do all the related checks and notifications. There is also a simple desktop application that can be used to communicate with the 'Lampost' and configure it.
For the Arduino part, I used the Arduino IDE , and for the web service and configuration tool I used Visual Studio.
Arduino - The Configuration
This part of the Arduino program deals with the incoming commands from the desktop application that is used to configure the unit itself. This configuration is there to set the network information and identification of the 'Lampost'. The information is stored in the EEPROM memory of the Arduino, so even after a power failure or other restart, the module can connect to the network.
#include <EEPROM.h>
static char _network[17]; static char _password[17]; static char _server[16];
void EEPROMwrite(char* buffer, int length, int start, int max)
{
for (int i = 0; i < max; i++)
{
EEPROM.write(i + start, (i < length) ? buffer[i] : 0);
}
}
bool valid(char* input, int cmd_len, int max)
{
strcpy(input, &input[cmd_len]);
if (strlen(input) > max)
{
return(false);
}
return(true);
}
void getInfo()
{
Serial.print(F("INFO;"));
Serial.print(_network); Serial.print(F(";"));
Serial.print(_server); Serial.print(F(";"));
Serial.print("\n");
}
void handleCommands()
{
if (Serial.available() > 0)
{
char inputLine[65];
memset(inputLine, 0, 65);
Serial.readBytesUntil('\n', inputLine, 64);
if (strncmp(inputLine, "SSID:", 5) == 0)
{
if (valid(inputLine, 5, 16))
{
EEPROMwrite(inputLine, strlen(inputLine), 0, 17);
EEPROMRead(_network, 17, 0);
esp8266.disconnect();
}
}
if (strncmp(inputLine, "PSW:", 4) == 0)
{
if (valid(inputLine, 4, 16))
{
EEPROMwrite(inputLine, strlen(inputLine), 17, 17);
EEPROMRead(_password, 17, 17);
esp8266.disconnect();
}
}
if (strncmp(inputLine, "SRV:", 4) == 0)
{
if (valid(inputLine, 4, 15))
{
EEPROMwrite(inputLine, strlen(inputLine), 34, 16);
EEPROMRead(_server, 15, 34);
}
}
if (strncmp(inputLine, "INFO", 4) == 0)
{
getInfo();
}
}
}
void setup() {
Serial.begin(9600);
while (!Serial);
}
void loop() {
handleCommands();
}
Arduino - The WiFi Shield
Now we have all the information the module needs to identify itself and communicate with the web service, so we can actually start the communication. But first, we need to load the proper library for the WiFi shield to use. The library (in zip format) can be obtained here: SparkFun_ESP8266_AT_Arduino_Library, while the documentation to install it into the Arduino IDE is here: Arduino Libraries.
The new parts are in bold (and some of the old were omitted to keep it simple). As you can see, I put the WiFi shield into station-mode, as I only want to send, and not listen to incoming traffic. At this point, no data is sent anywhere, but if you check your network, you can see this module is up and running.
#include <SoftwareSerial.h>
#include <SparkFunESP8266WiFi.h>
#include <EEPROM.h>
static char _network[17]; static char _password[17]; static char _server[16]; static char _mac[18];
void getInfo()
{
Serial.print(F("INFO;"));
Serial.print(_network); Serial.print(F(";"));
Serial.print(_server); Serial.print(F(";"));
Serial.print(_mac);
Serial.print("\n");
}
void setup() {
Serial.begin(9600);
while (!Serial);
EEPROMRead(_network, 16, 0);
EEPROMRead(_password, 16, 17);
EEPROMRead(_server, 15, 34);
while (!esp8266.begin(9600))
{
esp8266.reset();
}
while (esp8266.setMode(ESP8266_MODE_STA) < 0);
esp8266.localMAC(_mac);
esp8266.disconnect();
esp8266.connect(_network, _password);
}
void loop() {
if (esp8266.status() <= 0)
{
esp8266.localMAC(_mac);
esp8266.disconnect();
esp8266.connect(_network, _password);
}
handleCommands();
}
Arduino - The RFID Module
The RFID module is in autonomous mode, which means that it will not wait for me to ask it to scan for tags, but will do automatically, and collect the information in its internal buffer. So all I need is to open another serial port and read that internal buffer. As I decided to use a certain kind of tag (standard 96 bit information) for my prototype, all I need is to read in the 12 bytes of information and send them on to the web server.
In this section of code, there is a lot of working with the RFID module that is based on the documentation and code-samples that comes with it. I will not go into details of why - if you are intending to work with such a module, you will have to learn it for yourself.
static char _http[] =
"POST http://000.000.000.000/LocalAPI/api/RFID HTTP/1.1\r\
nContent-Type:application/x-www-form-urlencoded\r\nHost:000.000.000.000\r\
nContent-Length:27\r\n\r\nid=000000000000";
static SoftwareSerial _RFIDSerial(10, 11);
static byte _msg[16];
static byte _response[256]; static byte _op = 0x22;
static unsigned long _tag_count = 0;
static const unsigned int _crc[] PROGMEM = {
0x0000, 0x1021, 0x2042, 0x3063,
0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b,
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
};
static char _hex[] =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
unsigned int crc(byte* buf, byte len) {
unsigned int crc = 0xffff;
for (int i = 0; i < len; i++) {
crc = ((crc << 4) | (buf[i] >> 4)) ^ _crc[crc >> 12];
crc = ((crc << 4) | (buf[i] & 0xf)) ^ _crc[crc >> 12];
}
return (crc);
}
int rfid_msg(byte opcode, byte* data, byte len) {
int response_len = 0;
unsigned int msg_crc;
unsigned int response_crc;
_msg[0] = 0xff;
_msg[1] = len;
_msg[2] = opcode;
if (len > 0) {
memcpy(&_msg[3], data, len);
}
msg_crc = crc(&_msg[1], len + 2);
_msg[len + 3] = msg_crc >> 8;
_msg[len + 4] = msg_crc & 0xff;
_RFIDSerial.listen(); _RFIDSerial.write(_msg, len + 5);
response_len = _RFIDSerial.readBytes(_response, 256);
if (response_len > 0) {
if (response_len == (_response[1] + 7)) {
response_crc = crc(_response, response_len - 2);
}
if ((response_len == 5) || ((response_crc >> 8 == _response[response_len - 2]) &&
(response_crc & 0xff == _response[response_len - 1]))) {
if (_response[2] == _msg[2]) {
if ((_response[3] == 0) && (_response[4] == 0)) {
return ((response_len == 5) ? 0 : response_len - 7);
}
else {
return(-1 * (_response[3] | _response[4] << 8)); }
}
else {
return(-1); }
}
else {
return(-2); }
}
else {
return(-3); }
_client.print(_http);
}
void rfid_22() {
if (rfid_msg(0x22, new byte[5]{ 0x00, 0x00, 0x13, 0x01, 0xf4 }, 5) == 7) {
_tag_count = _response[8] << 24 | _response[9] << 16 | _response[10] << 8 |
_response[11];
if (_tag_count > 0) {
_op = 0x29;
}
}
}
void rfid_29() {
if (rfid_msg(0x29, new byte[3]{ 0x00, 0x00, 0x00 }, 3) > 0) {
byte count = _response[8];
for (int i = 0; i < count; i++) {
int offset = 9 + 18 * i + 4;
for (int p = 0; p < 12; p++) {
_http[160 + p] = _hex[_response[offset + p]];
}
if(_client.connected()) {
_client.print(_http);
while (_client.available())
{
_client.read();
}
}
}
_tag_count -= count;
}
else {
_tag_count = 0;
}
if (_tag_count == 0) {
rfid_msg(0x2a, new byte[0], 0); _op = 0x22;
}
}
void handleRFDI() {
if (_op == 0x22) {
rfid_22();
}
if (_op == 0x29) {
rfid_29();
}
}
void insertIP() {
int len = strlen(_server);
int empty = 15 - len;
memset(&_http[5], ' ', 22);
memcpy(&_http[5 + empty], "http://", 7);
memcpy(&_http[5 + empty + 7], _server, len);
memset(&_http[113], ' ', 15);
memcpy(&_http[113], _server, len);
}
void setup() {
Serial.begin(9600);
while (!Serial);
EEPROMRead(_network, 16, 0);
EEPROMRead(_password, 16, 17);
EEPROMRead(_server, 15, 34);
while (!esp8266.begin(9600)) {
esp8266.reset();
}
while (esp8266.setMode(ESP8266_MODE_STA) < 0);
esp8266.localMAC(_mac);
esp8266.disconnect();
esp8266.connect(_network, _password);
insertIP();
_RFIDSerial.begin(9600);
rfid_msg(0x06, new byte[4]{ 0x00, 0x00, 0x25, 0x80 }, 4); }
void loop() {
if (esp8266.status() <= 0) {
esp8266.localMAC(_mac);
esp8266.disconnect();
esp8266.connect(_network, _password);
}
handleCommands();
handleRFDI();
}
Desktop Configuration Tool
Now that we have the 'Lampost' up and running, we should configure it, so the WiFi connection should really send data to the web server (the RFID-Arduino part will work on its own, but the Arduino-WiFi part needs information).
It is a fairly simple application, all it does is scan the COM ports for a connected device. If one is found, it will display the current information on it and let the user set new values.
The Missing Parts
To enable holding information on the several 'Lampost's, one may have, it would be a good idea to send the information from this application to a central database (indexed by MAC address maybe) so one may create a physical map of the 'Lampost's. That would be essential to keep passengers informed of the whereabout of their luggage.
In a more sophisticated solution, the WiFi connected to the Arduino would work both as server and as client, which would enable configuring it via the network.
Web Service
This is where I cannot show most of the code as much of it is very implementation specific (and definitely not related to the Arduino). What I can show you is a C# skeleton of how the RFID data is received.
public class RFIDController : ApiController
{
public void Post ( string ID )
{
if ( !string.IsNullOrEmpty( ID ) )
{
}
}
}
Summary
As I mentioned above, this is not a live project, but a proof of concept prototype. The actual prototype demonstrates how the Arduino can be used to create some fairly complicated hardware that can receive and transmit data from/to different sources. Such hardware - paired with the proper back-office software (and not the one I wrote) - can be used to improve services in many areas; all you need is the right idea...
History
- 20th June, 2018: Initial version