Sunday 4 May 2014

HC-SR04E Ultrasonic Range Finder

I've got this idea of using ultrasound triangulation to help guide a model helicopter during take-off and landing. You would have launch pads with three separated ultrasound transmitters, and the copter would have an ultrasound receiver, connected by some bus to its flight controller. It wouldn't have a particularly great range, but away from the ground other sensors (GPS, compass, gryo, accelerometer) are good enough for flight control.

A few weeks ago, I ordered four ultrasonic rangefinders from 12geeks, an online shop run by some very nice chaps in Singapore. I didn't know much about the sensors I'd ordered, apart from that they were cheap and widely used, didn't do exactly what I needed, but were sufficiently close to be interesting.



The particular rangefinder I'd ordered was named the HC-SR04, and had been originally designed by Devantech Ltd, a small Norfolk-based electronics firm specialising in robot sensors. The HC-SR04 works by sonar: issuing a burst of 40kHz sound from one ultrasound transceiver and measuring the time taken for the echo to reach a second transceiver. This time is then sent to the sensor's output pin as a pulse, with a duration proportional to the echo time.

On Devantech's site, there is a detail-packed page explaining the design of the sensor, including a circuit diagram, showing the connections between the microcontroller and transmitter/receiver.


You can see that the microcontroller is an eight-pin PIC12C508, which isn't reprogrammable. It was clear that if I was going to adapt the sensor to a different application, I would need to replace the microcontroller, so I ordered a bunch of eight-pin Atmel AVR chips (ATtiny85). I planned to remove the PIC chip, add some tiny pin headers, and use a daughter board for prototyping the AVR replacement.

Then the sensors arrived from 12geeks, and I realised the sensor had undergone various design changes. Gone was the eight-pin PIC, to be replaced with a 14-pin Taiwanese EM78P153S, with more connections to the send-receive circuits than could be handled by the eight-pin ATtiny. Bugger. Here's what it looks like, after the microcontroller has been removed:



Luckily, I found that someone else had been working on the same problem, and he had done almost all of the documentation necessary for me to work with the current design. Even better, he had redesigned the circuit board to take advantage of all the existing components, apart from replacing the microcontroller with a 14-pin AVR (ATtiny24a) and crystal oscillator. Building the board should be a simple matter of removing components from one board and soldering them into the same position on the new board (which has been tentatively named as the HC-SR04E). I ordered the boards from a prototyping PCB printer, and am waiting for them to arrive. When they do, I will probably have to invest in a hot-air soldering gun if I'm not going to fry the components.

While I'm waiting for the boards to arrive, and plucking up the courage to tell my wife I need a hot-air gun, I've ordered some 16-pin PCB headers and sockets, together with a SOP14 to DIP14 adapter, which I can use to build a daughter board.



I've also been thinking about software. Most sensors used in flight control use I2C as a communication bus, so my first task has been working on getting an interrupt-driven implementation running on the ATtiny24. I'm used to working with the ATmega328P, so thought this would be a simple matter. It is not. The ATtiny24 doesn't have a hardware implementation of TWI (Atmel's version of I2C); instead it has something known as USI (universal serial interface).

After communication is done, I'll start looking at how the sensor's clock can be synchronised to a more accurate source, which will be necessary if the HC-SR04E is ever put into mass production, with uncertain precision of crystals being used.

Anyway, there's very little on GitHub yet, but should be some progress soon: HC-SR04E source code.

Please get in touch if you'd like to get involved with the project, especially if you're in Singapore, in which case I'll be able to share some of my hardware.

Saturday 5 April 2014

MultiWii 328P Flight Controller with FTDI & DSM2 port (MWC_328P)

I've been waiting a while to get my hands on one of these flight controllers, but the airmail shipping times from the US to Singapore seem quite variable. Luckily, I know a student at Nanyang Technological University who was able to buy one through his department and meet up in a Changi car park to exchange goodies. Very clandestine. Thanks Sherman.

So, in essence, this is a circuit board that tries to incorporate the various sensors needed to stabilise a remote-control aircraft in flight. There is a three-axis compass from Honeywell (HMC5883L), similar to the ones found in mobile phones. The Bosch barometer (BMP085) should be able to detect an altitude change of 25cm, although these devices are susceptible to wind and vibration. An InvenSense three-axis gyroscope (ITG3205), similar to the one found in Nintendo Wii game controllers, measures angular velocities down to about one-tenth of a degree per second. Finally, a Bosch three-axis accelerometer (BMA180) is used to detect changes in tilt.

As a group, the sensors are pretty good selection of low-cost components. The board sells for less than US$30 at HobbyKing, which is a bit of a bargain.


As you can see, the board has a USB port for serial communication, enabled by the FT232RL USB to serial UART interface (U2). This can be used to program the microcontroller through the Arduino IDE, or through avrdude directly, if you prefer.

This setup of microcontroller and USB is similar to the Arduino Duemilanove, and so in the Arduino IDE you need to choose the board "Arduino Duemilanove w/ ATmega328" or the equivalent from the tools menu before uploading. If you forget, you'll be presented with sync errors.

From avrdude, the only points to remember are setting the correct baud rate (57600) and mcu (atmega328p):

avrdude -F -V -c arduino -p atmega328p -P /dev/ttyUSB0 -b 57600 -U flash:w:blinkenlights.hex




U2 - FT232RL - USB to serial UART interface
U3 - ATmega328P - AVR microcontroller
U4 - [marked IL341] - 3.3V regulator
U5 - HMC5883L - 3-axis compass
U6 - BMP085 - pressure sensor
U7 - ITG3205 - 3-axis gyro
U8 - BMA180 - 3-axis accelerometer
U9 - TXS0102 - 2-bit bidirectional voltage-level translator (hidden in this photo behind the white plastic Spektrum satellite connector

There are two main collections of pin headers directly connected to the microcontroller. The pins to the right of the USB connector are connections for the RC receiver. The pins below the microcontroller are signals for the motors (on the right), and three columns of pins on the left for stabilising a camera mount.

Thursday 20 March 2014

Starting a new project with Ceedling

WORK IN PROGRESS!!! Installing Ceedling and the AVR toolchain

THIS NEEDS A BETTER INTRO ABOUT UNITY AND THE TOOLCHAIN Ceedling is a build system for C projects that is something of an extension around Ruby’s Rake (make-ish) build system. Ceedling is primarily targeted at Test-Driven Development in C and is designed to pull together CMock, Unity, and CException -- three other awesome open-source projects you can’t live without if you're creating awesomeness in the C language. In order to spread the awesomeness around, Ceedling is an extensible contraption with a nice plugin mechanism.

Before we install Ceedling, let's start by installing a compiler for the AVR ATmega328P, the microcontroller onboard the Arduino Uno. I'm going to show the installation commands for a typical Linux system, but the instructions for Windows and OSX follow a similar pattern. Open a terminal and type the following command:

ngourlay@craxine:~$ sudo apt-get install binutils-avr avr-libc avrdude gcc-avr

After installing Ceedling and the avr tools create a new project and edit the generated rakefile using your favourite text editor:

ngourlay@craxine:~$ ceedling new 74HC595
      create  74HC595/vendor/ceedling/docs/CeedlingPacket.pdf
      create  74HC595/vendor/ceedling/docs/CExceptionSummary.pdf

... many other messages from Ceedling ...

      create  74HC595/project.yml
      create  74HC595/rakefile.rb

Project '74HC595' created!
 - Tool documentation is located in vendor/ceedling/docs
 - Execute 'rake -T' to view available test & build tasks

ngourlay@craxine:~$ cd 74HC595/
ngourlay@craxine:/74HC595~$ emacs rakefile.rb


You'll need to add a couple of targets ('convert' and 'program') , plus a dummy target ('serial_port') to ensure that the required environmental variable has been set. For most projects, the following rakefile will be sufficient:


Now edit the project.yml file. Uncomment the 'release_build' variables, and add some environmental variables. Changes to the standard boilerplate are marked in bold, below.

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: TRUE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
  :release_build: TRUE
  :test_file_prefix: test_

:release_build:
  :output: 74HC595
#  :use_assembly: FALSE

:environment:
  - :mcu: atmega328p
  - :f_cpu: 16000000UL
  - :serial_port: /dev/ttyACM0  # change this to the correct port
  - :objcopy: avr-objcopy


:extension:
  :executable: .bin

:tools:
  :release_compiler:
    :executable: avr-gcc
    :arguments:
      - ${1}
      - -DTARGET
      - -DF_CPU=#{ENV['F_CPU']}
      - -mmcu=#{ENV['MCU']}
      - -Iinclude/
      - -Wall
      - -Os
      - -c
      - -o ${2}
  :release_linker:
    :executable: avr-gcc
    :arguments:
      - -mmcu=#{ENV['MCU']}
      - ${1}
      - -o ${2}.bin

Monday 10 March 2014

Approximate division using multiplication and right-shift

Problem

Many processors have no native floating-point capabilities, leading to the choice between integer maths or floating-point emulation. One important range of microcontrollers, ATmega, also has no native arithmetic division operation. Thus, the Arduino project, which uses the ATmega chipset, must use a compiler that emulates both integer and floating-point division in software. In some cases, it might be better to ignore the compiler and roll your own less-accurate version of the division op.

Decimal Tricks

All numerate people have learned mental tricks to help with arithmetic division. We learn multiplication tables so that, given we know 55 is five times eleven and 66 is six times eleven, then we also know that 57 divided by 11 is slightly more than five.

5 < (57 / 11) < 6

Dividing is equivalent to multiplying by a reciprocal, and some of these reciprocals have a simple decimal expansion. For example, given we know that one-eighth is 0.125, we can assume that division by 125 is equivalent to multiplication by eight, with some shift of the decimal point.

(x / 125) == (x * 8 / 1000)

Further, some decimal expansions of reciprocals are close to being helpful. The reciprocal of 77 is around 0.01299, meaning that we can approximate a division by 77 with a multiplication by 13, followed by some shift of the decimal point.

(x / 77) =~ (x * 13 / 1000)

Binary Tricks

We can apply the "multiplication and right-shift" trick to binary division, which is slightly more useful when programming. First, of course, any division by a whole power of two is equivalent to a right-shift.

(x / 2) == (x >> 1)
(x / 4) == (x >> 2)
(x / 8) == (x >> 3)
(x / 16) == (x >> 4)
(x / 32) == (x >> 5)
...

Next, we can use approximations that give us good results within a limited range. For example, given that x is between zero and 255, and we are calculating an integer result, the following expressions are true:

(x / 3) == ((x * 171) >> 9)
(x / 5) == ((x * 205) >> 10)
(x / 6) == ((x * 171) >> 10)

Of course, if we are limiting ourselves in this way to integer maths, and 8-bit numerators, every result for divisors over 128 can be calculated with a simple comparison:

(x / 129) == (x < 129 ? 0 : 1)
(x / 130) == (x < 130 ? 0 : 1)
(x / 131) == (x < 131 ? 0 : 1)
(x / 132) == (x < 132 ? 0 : 1)
...

There are a few problems in replacing division by multiplication in this way. The best example is division by seven:

(x / 7) =~ ((x * 147) >> 10)

Now, this expression is mostly true, but starts to become inaccurate at x=209, with seven off-by-one errors in the whole range. Whether these off-by-one errors are acceptable is a matter of personal choice, but given the limitations of integer maths, it seems reasonable to sacrifice a little accuracy for speed here.

Use case

A sensor can produce a voltage on an Arduino analog pin which will be converted into an unsigned 10-bit integer. To format this value in human-readable units (centigrade, metres, hours, etc.) requires division by a constant divisor. Often, humans don't care about off-by-one errors when reading the temperature of their ovens. Using the multiply-and-shift method can reduce time and memory usage, and produce a result in a constant number of clock cycles.

Complete list of multiplications

Here's a complete list of approximate divisions in the 8-bit space. Change the Perl script for the equivalent 10-bit approximations.

Source code