Recently, a colleague of mine was working on a project with an Arduino Yún that involved reading a lot of sensor data using the Yún’s Arduino processor and writing it to the microSD card via the board’s Linux processor to be served to other devices via HTTP. She found that it took anywhere from 20 milliseconds to several seconds to get the data from the sensors written to the SD card. “Why is it not real-time?” she asked me.
Welcome to the world of embedded operating systems. They are not realtime. “Realtime” can mean many things. In automotive systems, for example, your car’s braking system had better react in “realtime” or you’re dead. That might mean only a couple of milliseconds. When measuring high-speed rotation, it might even mean microseconds.
My colleague was measuring her function’s response time in tens to hundreds of milliseconds. That function read one controller’s analog input pin, sent the result via asynchronous serial to another controller, and then stored the result on an SD card. I haven’t measured it, but I’d wager you’ll see the same response times on a BeagleBone or Raspberry Pi, or any embedded Linux board. Here’s why:
All computers run programs. In a microcontroller like the Arduino, there’s only one program running. That program is made up of instructions stored in memory, stacked in a particular order. The computer moves through those instructions one at a time. Sometimes it jumps around in the stack of instructions, when a particular instruction tells it to do so. Jumping around in the instruction stack takes more time than moving sequentially, however. Reading data from the input/output pins and transferring that data to memory also takes time.
Microcontrollers typically only run one program, but more complex controllers like your laptop, tablet, or phone processor run operating systems. An operating system is a program that manages several other programs. An operating system includes a kernel function that manages which programs get priority over the processor, a scheduler that schedules the order in which functions are to be run, and a file system to manage memory in a structured way. Operating systems treat other programs as if they were subroutines of the OS. The scheduler manages when those programs run, and moves their data in and out of the file system as needed. Operating systems are generally designed to optimize fair sharing of the processor. They don’t guarantee the precise time that a given function will run, just that it will be given time to run. Sometimes you need a particular function to run in a guaranteed time. If so, you give that program priority in the scheduler. But even then, it’s still subject to the scheduler’s orders.
Even before the programs you run get time, there are other OS tasks that take time. Disk management, for example, is one of the most time-intensive tasks. Perhaps the only longer task is a network transaction, because the data is going through many computers, each with its own operating system.
At the electrical level, all computers are made up of transistors, so in computing, the fastest version of “realtime” means “how fast can you read and act on a changing voltage on a transistor?” Some of the input/output pins of a microcontroller are usually hardware interrupt pins, meaning that they can be programmed such that if there’s a change on that pin, the program running on the controller is immediately interrupted and a special function is run. This function, called an interrupt service routine or ISR, is typically very short. Normally an ISR simply stores the result of the interrupt in a variable. Then the processor returns to the main program.
Interrupts are the ultimate realtime in computing. They are the lowest level of computing. They are hardware switches that can literally stop your program in order to get their service routines to run.
In order to use an interrupt, its service routine has to be written into the lowest level of your processor’s software. If you’re running an operating system, then, the service routine has to be written into the kernel of the OS.
If you need real-time response, you typically use one of three options:
- no operating system. Your processor runs one program only. Like Arduino
- a Realtime Operating System or RTOS. RTOSes are stripped-down operating systems that leave many core functions out, and that give priority to interrupts. Most RTOSes are tersely written for speed and not very user friendly. I haven’t seen one I like running on any of the hobbyist boards yet, but I wouldn’t be surprised if we don’t see one soon.
- Run a microcontroller in communication with an OS processor.
The Arduino Yún takes this third approach. When we designed the Yun, we decided we’d give users the benefits of both real-time and an operating system. What you do on the Yún’s Atmega 32U4 (the Arduino processor) is real-time because there is no operating system. What you do on the Linux processor (The Atheros AR9331) is not, because it’s running Linux, an operating system. The typical approach to a networked project (whether a Yún or other system) is to do all the real-time operations on the non-OS microcontroller, then send the results to an operating system computer after the action has happened.
This approach is fairly common, it’s why you see add-on boards like the AlaMode for the Raspberry Pi, for example. The BeagleBone, in contrast, has a separate core inside the on-board processor, called a Programmable Realtime Unit, or PRU. The PRU is actually separate from the processor that’s running the operating system, even though it’s in the same physical chip. And it’s attached to some of the same I/O pins as the main processor. As of this writing, the PRU is only programmable in assembly language, but if anyone ever writes a higher level compiler for the PRU, the BeagleBone is going to be a fantastic tool for realtime/networked applications.
Here’s a use case that illustrates the use of real-time and an operating system together:
Let’s say you want to control a remote control wheeled vehicle using a touchscreen device that’s networked to the vehicle over Wifi. You’d read the vehicle’s speed using a rotary encoder on a microcontroller with no operating system, and control its steering mechanism on the same processor. You’d report the speed and the current state of the steering mechanism to the steering device via a data connection, but you’d manage the realtime computation of speed and steering control on the vehicle’s processor.
Rotary encoders measure rotation by counting pulses generated by a rotating shaft. When the rotation is fast, the pulses happen very fast. If your processor is executing another instruction instead of reading the I/O pin in that instant, you lose the pulse. On a microcontroller, this is typically sensed using a hardware interrupt. The Arduino Uno has two hardware interrupts, and Paul Stoffregen’s encoder library allows you to use them for reading rotary encoders.
The way you’d build this application is to have the rotary encoder attached to the hardware interrupts of the vehicle’s microcontroller. This controller is your physical I/O controller. You’d write a program that calculates the speed based on the pulse rate and users that value to adjust the steering.. One function of the program counts the pulses. That’d be done by the function called by the interrupt. Another function calculates a running speed based on the changing count. A third function regulates the steering mechanism based on the speed value. A fourth function sends the calculated values over a serial connection to the display computer.
The serial connection of the physical controller might be connected to a networked modem like the Wifi shield or a Bluetooth radio. That modem is just another single-program controller, designed to take data in via its physical connections and send it out via radio (and vice versa). That transmission takes time, and you don’t want to take processor time away from counting the pulses, so your physical I/O controller doesn’t handle this transmission, it only counts pulses and sends the value on. The radio controller the data on to a display computer. That display computer is running an operating system, and not working in real time, but that’s okay, because humans aren’t going to react in more than a half-second or so most of the time.
The diagram below gives you an idea of the number of different processors that might go into an application like the one described here. You can see how many different communications protocols might be involved as well. Each of those protocols comes with its own temporal cost as well.
What you see onscreen is a report of the sensor readings, but it’s an average or aggregated reading, not the raw, realtime reading. The delay depends on the transmission time of the data. The steering is not real-time either. The user gets to set the general direction, but the fine control of the axles is managed by the physical I/O controller based on the user’s choice and the reading from the speed sensors.
It is possible for a controller that’s running an operating system to have hardware interrupts, and for it to have interrupt service routines. But those are typically part of the operating system kernel, not part of the user programs. You have to write custom kernel routines and restart the OS in order to use them. So unless you’re a kernel programmer, you typically won’t use these on BeagleBone, Raspberry Pi, or Arduino Yún. This is why there are many projects that combine the Pi or the BeagleBone with an Arduino or other non-OS controller to do real-time sensing, and it’s why the Yún has a separate processor for that as well.
A typical personal computer is made up of several controllers. There’s normally a CPU, a network controller, a controller inside any disk drive (if it’s a drive with a motor), a graphics processor, a hardware bus controller, and more. These are usually tied together with synchronous serial communication like SPI or I2C or some other means of communication. The dedicated controllers handle their subsystems in realtime, and report back to the CPU, which reports in user time (not real time).
When you introduce a network, the time delays get even larger. When you connect your remote vehicle to your website, for example, you just introduced several more transactions through several more computers. Your physical I/O controller sends the data to a WiFi access point, which sends it to a router, which sends it to your internet service provider’s router, which sends it to your web host’s router, which sends it to your web host’s server. That’s five or more computers, all running operating systems, and not operating in real time. Even when they’re optimized for speed of transmission, there’s always delay.
In conclusion, What we humans perceive as real time, what electronic circuits perceive as real time, and what computer networks transmit as real time are very different time scales. If you’re referring to reading a sensor and controlling a mechanism in response, real time is much faster than human perception or reaction time. When you add in network connections, or even just disk storage to a project, you add delay in managing the transfer of data. When you’re designing systems that control physical mechanisms, you have to combine some automation, in the form of a simple program on the physical I/O processor that’s reading your sensors and controlling your mechanisms, with feedback from users over non-realtime systems.