Wednesday 24 February 2016

The Near-miss-o-meter, Part 3

From Breadboard to Pegboard

A slightly longer delay from the last post than planned. Partially due to my slow wordsmithing, but primarily due to the distraction of writing smart phone version as a 'cheap-as-chips' nearmiss-o-meter. More on that later.

Rendering in the most compact form.
This post will document the build of the standalone Arduino prototype. This could be packaged either on a custom PCB or home brewed onto a peg board within ~50mm x ~50mm x ~40mm cube (including battery).

The test version I have created is single sided (for ease of wiring), has 8mm high sockets (to enable me to re-use the components) and still only measures 50mm x 70mm x 45mm. It also includes a 3.5mm mono headphone socket for a remote switch.

Please note: All the information here is for guidance only. If you're unsure of what you're doing, please get someone qualified to help (i.e. not me!). Most of the components are fairly benign (although if you accidentally connect a 3.3 volt LED to the 5v USB input, it'll blow the top off and hit you on the forehead...), but the battery stores a lot of energy and must be treated with utmost respect.

Remember, electronic components run on magic smoke - when the smoke is released, they won't work.

The list of components is as follows:
  1. Adafruit Feather 32u4 Data Logger [>]
  2. Ublox NEO-7M GPS [>]
  3. Maxbotis Maxsonar [>]
  4. SPDT PCB Slide Switch [>]
  5. SPST Push Button Momentary PCB switch. [>]
  6. RGB LED 3.3v [>]
  7. 500mAh LiPo battery (optional) [>]
  8. 50mm x 70mm Double sided prototype board [>]
  9. 3.5mm Mono headphone socket (optional) [>]
  10. Square pin header sockets (optional) [>]
  11. JST connector(s) (optional, but the one on the battery will be wrong) [>]
The links to eBay listings are not necessarily recommendations (I'm not a great eBayer...), they were simply chosen as UK based suppliers. The links may also have expired! If you are willing to buy direct (usually from China), prices could be significantly lower. That said, I have had excellent service from all the vendors - some have been above and beyond expectations. If there is enough interest, I may try to put a 'kit of parts' together with one of the vendors.

The headers (10) and the JST connectors (11) can be omitted if you're brave enough to solder the components directly to the peg board. The LiPo battery is optional if you're happy to power via the USB with a phone charger battery.

You will also need to beg, steal or borrow a soldering iron with a very small tip, a small amount of 0.2mm2 solid core insulated wire and a blob of Blu-Tack to stop the GPS module wobbling (it should really be screwed down).

This is quite a lot more expensive than planned, so since building this, I have acquired a few more ultrasonic rangefinders (and one infra-red one) for testing. All of them are much cheaper than the Maxbotix one and two of them have built in temperature compensation in serial mode, so may be better suited. The limitation is the Adafruit Feather board runs at 3v3, which rules out the 99p HC-SR04. I will write a sonar/board comparison post(s) as soon as time allows.

3D Modelling

Rendering with header sockets and pegboard.
To model the interaction of the components, I used a brilliant open source modelling tool called OpenSCAD.

Even (especially?) if you have never used a 3D program before and have the merest hint of programming or scripting knowledge, give OpenSCAD a try. I have created 3d models of each of the components which I will also upload to Github.

The picture to the right shows my double-sided modelling but as it was my first attempt, I swapped to single sided as I started to solder up. I later learnt you're supposed to do all the wiring on the opposite side to the components. It would probably be neater than my efforts. The PCB image from the previous post shows how to wire it up if you're so inclined.

This is the end result. All I need now is a box, a way of mounting it and the remote switch.

 

Coding for Arduino

The open source/Free software & hardware community really is a gift that keeps on giving. Without all the contributions of so many, this project would not have been possible.

Not only are the specifications of the Arduino hardware open source (which is why you can buy such cheap clones), the IDE (Integrated Development Environment) used to program the Arduino is also freely downloadable. It ships with a *huge* number of example 'sketches' - each and every new library has examples included.

'Proper' embedded programming is new to me. Although I do some embedded Linux, my bread & butter has been enterprise scale databases.  This was going to be a whole new experience. From terabytes to less than 32Kb. Oh my!

Here's the Arduino coding 101: There are just two main parts to an Arduino sketch - the setup() function, which runs once when you power on and the loop() function which runs over and over again until you power off or make the code barf (even then it seems to detect this and restart itself). This makes simple stuff really simple. You want to flash an LED? OK, turn it on, delay 1 second, turn it off, delay 1 second, rinse & repeat:

// From the Blink example:
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}


Great. Or not so great. While you're delay()-ing, nothing else happens. If your GPS is reporting a position change, you're not going to see it, if your want to ping the sonar - it's not going to happen.

A 'normal' computer handles all of this behind the scenes. As a programmer, if you want a process to not interrupt your user interface, you spawn it off into a different 'thread' that runs in the background - the operating system then 'schedules' all the threads for you. If you're really scaling, you can spawn it off to a whole new datacentre... Not on the Arduino. You have one loop.

Building a simple state machine

Vanilla Arduinos do not do threads (although guess what, there is a library or two for that!), but it does have a number of internal timers. You can obtain the number of milliseconds (or microseconds) since your program started. If you now define how frequently you'd like to turn the LED on or off, you can count the number of milliseconds that have passed then take an action. But while you're counting, you can do other things.

//Pseudo code
void loop() {
  now = millis(); //Get the number of milliseconds since program start
  if (now - previousOnOff >= 1000) { // If one second has passed

    if (ledState == LED_OFF) { // If the LED is OFF...
      digitalWrite(13, HIGH);  // then turn it ON
      ledState = LED_ON;       // and record the state
    }
    if (ledState == LED_ON) { // If the LED is ON...
      digitalWrite(13, LOW);  // then turn it OFF
      ledState = LED_OFF;     // and record the state
    }
    previousOnOff = now; //Save the time we last turned the LED on or off
  }

  //Here we can do other things
  //. . .
  //And even more things 
  //. . .
}

There is a surprising amount of functionality required that has to fit into just over 28kB. Looking through the code now, I have implemented the following:

GPS Read

Using the TinyGPS library, the location is read via serial, once per second. This is the default transmit rate for most GPS modules. The rate can be changed but the setup codes appear to be different for each version, so it went in the 'too hard' pile. Once per second works well, but the code is designed to cope with faster or slower updates.

From the GPS data, in addition to longitude, latitude, heading and speed, we also get the (very precise) time, which we use for both logging and to create the name of the log file.

When we do not have a fix, the raw NMEA data is sent to the USB serial port for debugging. The red LED will remain lit until a fix is found (or subsequently lost), then flashes once per second.

Sonar Ping

With a little empirical research, I settled on a 'sonar heartbeat' of once every 200 milliseconds. This ensures even fast passes are caught but gives sufficient time for other functions to happen. Each 'ping' is derived from the median of 5 rapid pings to remove outliers, handled by the NewPing library. I have also written my first Arduino library for the two serial sonars which is partially API compatible with NewPing. This means it will only require changes to a couple of lines of code to switch between different sonar modules.

If a near miss occurs (less than one metre), the blue LED will flash rapidly until we have 5 clear pings. This also gives an indication if the sonar is blocked or has barfed and can be used to check calibration.

Write to SD Card

Arduino has two libraries to support SD cards (SPI and SD) which take care of most of the heavy lifting. One minor limitation is the 8.3 file name format (MSDOS is still biting us on the bum), so I settled for YYMMDDHH.csv. If a file already exists, it will be appended. The data is written after each ping but only flushed once every two seconds to reduce battery consumption.

On startup, the presence of the SD card is checked and the green LED flashed twice to confirm. When recording there will be one short flash per second. If a file open or write error occurs, then there will be two long flashes per second.

Flashing the LEDs

To provide an easier interpretation of status, I've used a RGB LED. I've implemented a design that splits each second into 8 segments allowing each state to be indicated by each LED without clashing. The patterns are easy to update and 'visual' in the code:

const byte GPS_NO_FIX    = B11111111; // Continuous
const byte GPS_HAVE_FIX  = B00000100; // Once per second
const byte RECORD_ERROR  = B11101110; // Long flash, twice per second
const byte RECORDING     = B00000001; // Once per second
const byte TAGGING       = B00010000; // Twice per second (in combination with RECORDING
const byte NEAR_MISS     = B10101010; // Four times per second
const byte LOW_BATTERY   = B01010101; // Four times per second OR'd with GPS_HAVE_FIX


These constants also serve as status values, saving a few essential bytes.

Single switch control

The push button implements a medium press for record on/off and a short press for tagging on/off. It could also implement a long press if required. A side effect of the implementation also deals with the 'debouncing' of the switch contacts. No, I'd never heard of it either!

Battery State

This small but essential indicator was the final piece that I was able to squeeze in. On compile I now get the message:

Sketch uses 28,096 bytes (97%) of program storage space. Maximum is 28,672 bytes.

It has taken some effort to trim the code to fit - so although quite functional, it is currently in need of a bit of tidying...

Next Steps

I'm currently learning/working on an Android OTG version, which will significantly reduce the cost of the primary parts. The phone will provide the GPS, logging and power, with the slight downside of a cable connection. Logging to the phone should also enable easier uploads of the data and may provide a basis for a Bluetooth version. However, this is incredibly distracting (read: time consuming) so have not yet started the Github learning curve...

Part 4: The Arduino Code

Thursday 4 February 2016

The Near-miss-o-meter, Part 2

This post documents the route I took to arrive at the current configuration of the nearmiss-meter, so you don't have to.

The Digispark in my previous post introduced me to the Arduino family, but with only 8kB of memory it was never likely to have the capacity to process both GPS and sonar data and write it to an SD card. Arduino is not a technology I had played with before starting this project but turns out it is really easy to set up and get going. The 'Blink' sketch is the Arduino equivalent of the ubiquitous 'Hello World' of every programming language ever invented - one of many, many examples that come with the freely downloadable IDE.
En route to the Pantomime on Christmas Eve, we detoured via Maplin picked up an Arduino/Genuino Micro. This comes with a heady 32kB rather than the 8KB of the Digispark.
Adruino Micro, HC-SR04, USB GPS & SD card
Once the festivities subsided a little, I pulled together my collection of components and realised I couldn't just plug the USB GPS into the Arduino. I also couldn't justify (or wait for) a PCB GPS so after a glass or two of brain lubrication I started to cobble together a Python script to read the GPS via my PC USB port, spit it out of another USB port to the Arduino and also read back from the Ardino... Easy peasy eh?

It turns out Python blocks when it reads/writes to/from a serial port so I had to lean about Python threading. Then I had to learn about how you can Ctrl-C from a Python thread so I didn't have to keep manually killing the process! (As an aside, I also learnt enough Python to control my half price Maplin 433MHz remote sockets from my Pi. I'm easily entertained...)

There are a huge number of libraries for Arduino, so the chances are, if you want to do something, someone has probably done it already (and than made a library out of it). NewPing was the first library I 'discovered', when I setup the Digispark. This library does all the complicated stuff for ultrasonic sensors so I don't have to.

Next up was TinyGps, a small but perfectly formed library for parsing NMEA [pdf] data (this is a standard set of strings or 'sentences' that most GPS devices spit out). The sensor built by Dr Ian Walker used a combined Real Time Clock (RTC) and data logging shield. Microcontrollers (and the Raspberry Pi) do not have a RTC so do not know or store the current date and time. The Pi often gets its time from ntp when it boots up, but this requires a network connection. With a GPS we can get the exact time - down to the nano second!

By default, most GPS devices output location data once per second. It is possible to reconfigure the frequency, but the setup strings are not standardised across devices so I've left this alone for now. We definitely want more frequent readings from the sonar and after a little trial and error, I settled on a 'sonar heartbeat' of five times a second. This should not swamp the SD card and will also give sufficient time to get a median reading from multiple 'pings'.

Meanwhile, an SD card board had arrived, so I gingerly plugged it in. The repeated dire warning in many of the pieces of documentation I'd read about SD cards was: "Don't use 5v, anything more than 3v3 will fry your card.". The board I'd bought was listed as running on 3 or 5v, but I suspect I'd not be the first person who bought something from eBay that wasn't quite as described...

I tested with some old/broken SD cards and when they didn't smoke I put in the one and only spare I had. The example scripts for Arduino are brilliant.

I now had a working prototype, albeit with a laptop attached, so I stood at the side of the road and tested of the rudimentary setup. Thoughts turned to power supplies and charging - this Arduino Micro is 5v, so to power it on the move I would need either a portable USB charger or a LiPo battery + a step-up booster + a charging circuit. More components, more complexity. The USB battery is definitely an option when I revisit the Pi version, but for ease of shareing, the unit really needed to be self-contained. Enter the Adafruit Feather  Adalogger.

This featherweight microprocessor had an on board SD card and built-in battery charging. The only downside was it ran at 3.3 volts whilst the cheap HC-SR04 sonars run at 5v. However, it removed the need for several components with just the step up booster required.

With a new breadboard circuit made up, including the newly arrived Ubox GPS board, I now had my first fully portable system. Two resistors gave me the step down for the returned echo from 5v to 3v3.

I'd also ordered a couple more cheap sonars - I wanted to see how consistent their measuring was, but I couldn't get the two new ones to work. It seems not all HC-SR04s are created equal. Some will trigger with 3v3, others absolutely require 5v. Not happy. This would mean having a logic level shifter (new to me too!) - yet another component.

On the plus side, the Adafruit Feather performed well, despite only running at 8MHz but it was time to bite the bullet and buy a more expensive sonar that ran at 3v3. This is the Maxsonar MB1010, from Maxbotix. They have a huge range of sonars for every application, but my choice was based on price and easy availability in the UK as well as noting Ian Walkers use and comments. The top end fully waterproof models are way out of the target price range for this project, but may appeal to some who want to log data in all weather.

A couple of (late) nights later, with the great Antoine de Saint ExupĂ©ry sitting on my shoulder, I had reduced the design to the minimum: a single sided PCB with just three main components. Although this design is operational on a breadboard, this PCB layout is currently untested as my local PCB fabricator wanted to charge me £800 'consultation' fee before looking at it! The next step is to implement this on a peg board, to improve the connection reloability of the prototype, then I'll probably use Fritzing to get a test board made up.

Other than the plethora of new new things I've learnt, one of the biggest personal benefits of this project was that for almost the whole of the Christmas and New Year period I watched very little television but we're back into the swing of work/life, time is a little more limited.

I now have the Arduino code mostly written and a rudimentary Leaflet based web 'viewer' of the data.

This is a local street on the way back from my local bike shop that is absolutely awful to cycle along. The colour grading of the line shows red for less than 1m and shades from red to yellow to green for 1m to 2m passing distances. The data is only ever held locally (using Javascript FileReader), so privacy is ensured.

The next post will document the device I have prototyped in greater detail, along with the functionality implemented in the Arduino code. I have created a Github repository, but that is another learning curve I need to overcome before I populate it.


Part 3: Breadboard to Pegboard