Embedded Development is All About Iteration
I’m starting on a new side project for fun. I want to create a scoring box for fencing (the olympic sport, not building fences). If you don’t know much about fencing, the score box is wired to each fencer and their blade and it detects not only when a touch has occurred, but also if the touch is on a valid target area of the body.
It turns out that in order to properly detect these touches, the scoring box needs to sample four analog channels. The shortest time span of interest is for foil, where contact of greater than 2 milliseconds initiates a “touch.” The general rule of thumb for any sort of measurement is that you need to sample at twice the frequency of the measurement of interest, so I need to sample once every millisecond (or 1000 times per second – i.e. 1kHz) on four channels.
Obviously I want to try doing this with a Meadow. Since Meadow has six on-board Analog input channels, it makes sense to try them first. I did a quick test reading four channels in a loop as fast as possible, and unfortunately the results were not nearly fast enough to do what I need. It turns out that it takes roughly 300ms to sample the on-board analogs. That feels like it’s longer than it should be, and I’ve opened an issue to take a look at them, but I also know that analog inputs have a settling time, and maybe it’s relic of that, or maybe it’s overhead from the .NET runtime and when we enable AOT (which will be soon, I promise!) it will get better.
To see if I could narrow it down without diving into the depths of the Meadow OS code, I decided to look at other options. Maybe I could get an external module that runs over I2C or SPI that is designed for high speed? A little searching and I came across the TI ADS1015, which is a 4-channel, 12-bit Analog converter that supports up to 3300 samples per second and is accessed over the I2C bus. Sounds perfect right?
I ordered one up, as well as the ADS1115, which is higher-resolution but slower, to round out the driver I would have to create. Once I received the modules, however, I was disappointed. While they have 4 input channels, they go through a multiplexer (mux) internally and write to a conversion register, meaning that you can only actually read one channel at a time with it, so even at max speed I’d have to continuously change across the four channels, effectively cutting my sampling by 75% which ends up, even theoretically, too slow.
Even though I knew the modules wouldn’t meet my needs, I had them and there was no Meadow Foundation driver for them, so I decided it was a good opportunity to expand the library and I could still test to see just how fast these modules could read, even if it’s on a single channel.
After getting the library working and making sure the chip was at its fastest rate with the fasted supported I2C bus speed, I peaked out at just under 2ms per read. While this is still quite a bit below the theoretical maximum, it is two orders of magnitude faster than the on-board Analog inputs. This definitely makes me think that we need to revisit the Analog input code in Meadow OS to see if there are efficiencies to be gained.
Since I didn’t know if the difference between measured and theoretical is overhead of the runtime or the communication bus, the next step was to try the same test on another platform. A great way to to do that with very little work is to use Meadow.Linux. If you’re not aware of the project, it is an implementation of the Meadow Core that allows me to run my Meadow Code on popular IoT platforms like the Raspberry Pi with any Linux distribution. I ported the application project over – the driver is directly usable, but the application requires a slight change due to pinout differences – and ran it on a Raspberry Pi 4B. With that hardware I was able to query the ADS1015 1000 times in about 720 milliseconds, so it’s roughly 2.5 times faster than Meadow on the F7. Considering the processor differences and the possibility that the .NET 5.0 runtime being used on the Raspberry Pi could be more efficient, I’d say it was pretty close match up. Once we get AOT working on the F7 I suspect that gap will close some, but even with this speed, the multiplexing of the ADS1015 makes it unlikely I can read 1KHz on 4 separate channels, so I need to continue looking.
So in the end, I don’t yet have an exact hardware plan for my project, but this is the typical process of investigation and learning when you’re doing any embedded project. You start with some requirements, then you have to choose peripherals that you hope will exceed those requirements and write some tests. If the tests pass, great, you can start doing PCB layout. If not, you iterate. Choose different hardware. Look at your software for areas that can be improved. In this case I’m close enough to meeting the requirements that I’m confident I’ll get it to work, though it might have to wait until we have AOT working.