
Integrating Hardware into a Software System
Integrating hardware with software is often a tricky business. The difficulty is that hardware, unlike the (relatively) consistent world of software, can be unpredictable. Software can be tested quite thoroughly using many testing environments, with different OS versions to ensure compatibility. When you add hardware into the equation, things become far more challenging.
Some of our recent work has involved interfacing Android based devices with devices that contain NFC tags. The NFC tags needed to be able to be written to, and read from, using an android app. The app component of the system wasn’t hugely challenging to create, and the basic functionality of the app wasn’t too difficult to test. The difficulty presents itself when you have to test the interaction between the NFC tag and the application. In developing and testing this interaction, there were a few challenges, some of which are involved in any hardware integration, others which are specific to this case.
Debugging
When something breaks, the first port of call for many developers is setting some relevant breakpoints near where the IDE is complaining, and stepping through the code to find out which specific thing breaks. This is a problem while testing with hardware, as the debugger needs to halt the execution, to enable it to move step by step through the code. Working with only software, this behaviour is useful. While trying to precariously maintain NFC device contact to investigate a bug in a write operation, it is not.
This halting behaviour caused many issues, like for example, if you happened to slide one device just over the approximate 4cm range required for NFC communication while trying to step through the code, the connection can break. If the connection breaks whilst you are debugging, errors and exceptions unrelated to the bug you are trying to fix can be thrown, potentially leading you on a wild goose chase.
Hardware Testing & Consistency
If you run a section of code 100 times, in the same way, the result will be near identical. This is because nearly any external variables will be abstracted away. When you move into the real world however, things get more complicated. Are you using the hardware in exactly the same way each time? Are there variables about the environment this hardware is going to be used in that you need to consider? Do different devices / operating systems interact with the hardware in the same way? There are many different considerations to be made when integrating hardware into a system.
During our development and testing phase, we discovered and addressed a number of problems in this area. Firstly, hardware consistency can cause an issue with testing. In our case, every time we believed there was a bug, it had to be confirmed in detail, with multiple confirmations. It is important to be able to ascertain if a certain operation, such as a read or a write, was failing due to a software bug, or due to NFC inconsistencies.
Was there a problem with the data structure we had created, or was it simply a case of one of the devices moving slightly and the connection being interrupted? This sort of thing can be very difficult to test without purpose built equipment, which is able to do things like make two NFC devices touch each other in the exact same place, every time. A large number of logging messages and some trial and error helped us to overcome this type of issue. If you repeat the operation 5 or 6 times, user introduced inconsistencies can be mostly avoided.
Another challenge that hardware introduces, is that a physical device is often needed. It is common that an android emulator won’t suffice, as emulation of the hardware, and the factors involved with using it, would not be feasible. The regular standard of testing can be far more difficult to achieve due to this, because to test multiple android versions, you would physically need devices running all the desired versions to test with. If a bug is reported with a specific device, it may be required for us to own that specific device, to test and reproduce the bug.
A whole new can of worms is opened when the system hardware is handled by the end-user. With software, it is far easier to limit the number of things a user could do wrong. With hardware, it is far more difficult to specify how they will interact with the system, as opposed to simply how they should. Even a few basic applications of HCI (Human Computer Interaction) principles can lead to a solid software system, in which the vast majority of users will have positive experiences. In the physical world though, people can make mistakes or perform tasks incorrectly. Even if instructed clearly and concisely, a user may simply misread the instruction, or misunderstand what a picture is asking them to do. Perhaps they lack the level of motor skills required to reliably perform the task, such as in the case of the elderly.
It was a combination of these factors that led us to discover a particularly unpleasant bug with many devices running Android 7.1 and NFC.
NFC & Android 7.1
As described above, our system includes functionality to read from and write to NFC tags. The target demographic for the system contained some users who may be elderly, and may not be as technologically adept as others. This is quite important, as certain assumptions commonly made about what a user knows may be incorrect. In this case, we found that despite instructional messaging, some users may remove the NFC tag from their device, mid-write, breaking the NFC connection.
Normally this wouldn’t be too much of an issue, the system can simply detect if the connection was broken before the write was finished, notify the user, and the write can be attempted again. However, if the end-user was using Android 7.1, and removed the tag at exactly the wrong moment, there was a chance something bad could happen. The NFC tag could be left in a state where attempts to write to the tag or clear it, would fail. This issue is not present in Android 8, and it caused the Android 7.1 device to be unable to detect if the NFC tag was even formattable to the required format.
The bug left the devices containing the NFC tags to be unusable with the software system, and understandably, was a very large problem. The fix involved a multi pronged approach, to reduce the likelihood of the event occurring, and to fix the tag should it occur.
First, the level of messaging and instruction provided to the end-user needed to be improved. Stopping (or at least reducing) the number of occurrences of this happening is a well-grounded approach. Using some of HCI principles, some UI changes were made to make it very clear to even less technical users, that the devices are doing something, and not to seperate them.
Next, we needed to deal with the eventuality that at some point, for whatever reason, this would happen again. In the world of hardware, it isn’t enough to bank on an unlikely event never happening for your system to function correctly. To create a workaround, a substantial amount of digging through NFC tag specification documents, and learning about the inner workings of NFC tags was required. Many of these documents are not free to obtain, and are also extremely technical. This is another key issue involved with hardware work, if something breaks, you may need to dig behind the software abstractions. Google, in general, provides high level wrappers for most, if not all of the types of hardware interfacing you may need to do with an android device. The difficulty is, to fix a problem like this, trudging through the android developer documentation on NFC isn’t of much help, as specific technical hardware information is required.
Eventually, following thorough research, including ploughing through technical specification documents, we did manage to create a workaround. Without giving away too much, a low level understanding of NFC was required, and we had to solve the the issue manually, byte by byte. Our final result produced made sure that this issue would happen very rarely, and even also integrated a method to fix any of the devices previously made unusable.