Fun With Lasers: Part 1 - Extracting a DVD/RW Laser Diode

Friday, July 04 2014 @ 06:44 PM

This post is the first of a series about building a tool to simplify the production of PCB boards at home. I was inspired by this post about using diode lasers to prepare PCBs for etching - it seemed far quicker than the toner transfer method I'm currently using (in terms of active 'hands on' time at least) and the results seem to have a much higher precision. I already have a 3D printer so I was hoping to make an attachment to the print head that would hold a laser diode and use it to prepare the boards for me.

The trouble with things like this that you find on the internet is that they cannot always be replicated - so the first step was to see if I could access a laser diode and use it to burn paint from a PCB. This post is all about that process.

Safety First!

I must point out that playing with lasers is inherently dangerous and you MUST take all precautions before doing so - even reflected laser light can permanently damage your eyes.

Here are some simple precautions you need to take before trying this for yourself:

  • Make sure you have a pair of laser safety glasses suitable for the wavelength of laser you are going to be using. These are relatively cheap and easily available. Wear them every time you use the laser.
  • Do not point the laser directly at yourself - reflected laser light (even at the relatively low powers being used here) can permanently damage your eyes, direct light can cause permanent blindness.
  • Lasers generate a lot of heat in a very small area - this is a fire hazard and you need to take it into account.

Generally you should treat a laser as if it were a power tool - it's useful but if used without due caution you could easily lose a limb or a major organ.

Getting a Laser Diode

Laser Diode

The easiest way to acquire a laser diode with reasonable amount of power is to extract one from an old DVD/RW drive. These drives will generally contain two diodes, a 300mW to 400mW red (650nm) diode and an infrared one - we are only interested in the red diode. The diodes are in a TO-18 package, it looks like a small tin can with the diode lens at the top and three legs out the bottom.

There are plenty of tutorials on the internet showing how to extract the diode but I found that not all DVD drives are the same (the images in this post are from my third diode extraction - each of the drives was different). What is shown here is repeated elsewhere on the internet but I'm trying to be as generic as possible - rather than a specific set of instruction this is more of a guide of what to look for.

NOTE: This is a time consuming process which requires some patience and care. If you are applying excessive force at any step you are probably doing it wrong, step back - take a breath - and think it through. It is very easy to damage the diode so you do need to be careful to get the optimum results.

Tools and Parts Required

Before you begin make sure you have everything you need handy. I wound up using the following tools:

  1. Jewellers Screwdrivers - the standard set of phillips and flat headed drivers you can buy from most hardware stores will do the trick.
  2. Needle nose pliers - something with a good grip and small gripping area at the tip.
  3. Small Wire Cutters - the wire cutter on the pliers will not be good enough, you need something with a cutting edge at the tip.
  4. Small Files - not your normal woodworking files, you need something small. I have a set I got from a hobby shop for working with plastic models - they work well with soft metal as well.
  5. Dremel - any rotary tool really. You will need a cutting disk to go with it.
  6. Helping Hands - something to hold small parts firmly in place while you work on them.
  7. Soldering Iron.
  8. 3V Battery Pack - you will use this to do basic testing of the diode. I used a small 2 x AA battery pack for this.

Obviously you will also need a DVD/RW drive. I recommend collecting a few (at least two) before starting. It took me three attempts to extract a completely undamaged laser diode (the first two still worked despite the damage but were not optimal). The older the drive the easier it is to extract the diode, newer drives have more compact write heads and more tightly packed components.

And it goes without saying that you have the necessary safety equipment - most importantly a pair of laser safety goggles. At various times during the process you will need to apply power to the laser diode to test it - at these times please ensure the following:

  1. You are wearing your laser safety goggles.
  2. Always point the laser away from you (one of the lasers is an infrared laser and shows no visible beam).
  3. Make sure there are no metal fragments or shiny material in front of the laser - you do not want any reflections coming back at you.

Step 1: Expose the Read/Write Head

This is the simplest part - the laser diodes are on a small assembly that is moved back and forward over the surface of the DVD. All we want to do is extract that assembly and leave the rest of the drive untouched. We need to expose that assembly so we can get at it and unfortunately I didn't take any photographs of this part so you will have to go by description alone. Thankfully it's pretty straightforward.

First you will need to remove the outer metal shell of the DVD drive. Usually this involves removing 4 screws that hold it into place (there may also be a plastic clip connecting the front panel to the shell that you will have to release).

Once the metal case is removed you need to open the DVD tray to give yourself a bit of room to work in. There is usually a plastic slide you can push to do this. After that you need to remove the logic board - first unplug (or cut through) any cables connected to it and then detach it from the case. In all of the drives I have worked on it is simply attached by a plastic clip, some of the examples I have seen on the internet show it attached with screws - it should be fairly easy to disconnect and remove regardless. At the end of this you should be looking at something like the image to the left.

Step 2: Remove the Read/Write Head

The Read/Write head is suspended between two metal rods (see the image above), we need to remove it from the rods and (preferably) leave everything else intact. Generally you can achieve this by undoing a few screws and sliding the assembly off the rails it is attached to.

The image to the right shows it removed (the head assembly is in the bottom left of the image). In this photo I have re-attached the sliding rod it was attached to - this could be re-used at a later date for a small plotter or CNC machine.

Step 3: Remove the Laser Diode Assemblies

When you look closely at the read/write head assembly you can see the laser diodes attached to it. These consist of the laser diodes themselves wrapped in a heat sink and some wiring attached to it. At this stage we want to remove the laser diodes and heat sinks, the wiring is not of any use to us so we can sacrifice it. It is easiest to simply cut away any connecting wires and get them out of the way first.

In most cases I've found that the heat sinks are held in place by a mix of mechanical force and some waxy substance. You can scratch away the wax, check for any screws that may be present and remove them in necessary and then gently wriggle the heat sinks away from the main assembly - what you will end up with should look like the next image.

As I mentioned earlier there are two lasers in a DVD drive - one is a low power infrared laser (and doesn't generate any visible light), the other is red diode that we really want. At this stage we need to test both diodes to determine which one is the one we want to continue working with. The remaining parts you can either discard or keep for use later - I added everything I didn't use to a junk box on the assumption it might be useful later (one of the reasons the 'Garage Lab' requires a garage and not just a cupboard).

Step 4: Identify the Red Laser

As I mentioned in the previous step you should have removed all connecting wires to the laser diodes. You may need to make use of a solder sucker, desoldering braid or other desoldering tools as well as judicious use of side-cutters to achieve this. The end result should look something like the image to the right.

You can use a simple two AA (or AAA) battery pack to check the laser output, this will provide enough power to activate the laser diode without damaging it. Before you do this step make sure that;

  1. The lens of the laser is pointing away from you and there are no reflective surfaces in it's path.
  2. Wear your laser safety glasses.

The markings on the laser aren't very clear so you will need to test each pin combination. Simply attach the ground to one pin and touch the positive wire to each of the other pins in turn; repeat this process for each pin combination until you get a result.

One of the lasers will generate red light, the other generates infrared which you will not be able to see. If you don't get a response from either diode it has probably been damaged either before or during the removal process - you will have to start again with a different DVD drive.

Step 5: Remove the Heatsink

The diode needs to be removed from the heatsink material so we can insert it into a lens assembly. They are packed very firmly into the heatsink so trying to remove it by force (just pushing it out) will only damage the head of the diode. Luckily the heatsink is made of very soft metal so the safest way to remove it is to cut through the heatsink and then peel it away from the diode.

The technique I have used with some success is to carefully cut through the thinnest part of the heat sink with a dremel using a cutting disk. You need to take some care that you don't cut into the diode itself so check how deep the cut is at regular intervals. When the cut is deep enough you can grip each end of the heatsink with a pair of pliers and peel it apart.

This process involves a bit of trial and error - if you find yourself applying a lot of force with the pliers you probably need to cut some more before trying again. The end result should be a nice (and hopefully undamaged) laser diode:

Next Steps

Now we have a laser diode and we have proven it works. The next step is to build a decent power supply for it, add a lens to concentrate it on a specific point and see if it can do what we want it to. I will cover these steps in the next post.

The Bluebell, a Bluetooth Notification Device

Wednesday, June 04 2014 @ 09:35 PM

The Bluebell

After more than three weeks, numerous interruptions and delays the project I though would take a few days is finally finished. Finished enough to publish at least.

This post describes the Bluebell - a small Bluetooth controlled ambient device built around an ATtiny85 CPU. This is one of the first complete projects based around my ATtiny85 template library which is a bit of a milestone for that project.

The Hardware

The project pulls together a number of things I've spoken about in recent posts such as the software based UART, a HC-05 Bluetooth module and RGB LEDs driven by PWM so I won't discuss them in detail here.

I've put those components together in a simple circuit, added support for battery powered operation and wrote some firmware to provide a very simple serial protocol for remote control. The whole thing is then packaged in a 3D printed case. As usual, the entire project is available on GitHub under a Creative Commons License if you would like to build your own.

You can see the schematic of the logic board in the image to the left. To make things fit more neatly in the final case I split the LEDs out on to a separate board and use male and female pin headers to join the two together. Although the schematic shows NPN BJT transistors driving the LEDs I am actually using 2N7000 N-channel FETs for this task - I couldn't find a suitable Fritzing footprint for them but the generic NPN transistor was the closest matching component with a compatible PCB layout.

The only part of the circuit that I haven't discussed in previous posts is the power supply. Previously I have used AA battery packs (4 batteries for a total of 6V output) for my projects but I wanted to try and minimise the size of this one so I opted for a 9V battery as the main source of power instead.

There are two regulators in the circuit - a 5V switching regulator and a 3.3V LDO regulator. I'm using a RECOM DC/DC converter for the 5V supply, it is a drop in replacement for the classic 7805 regulator but far more efficient and more suitable for a battery powered device. The output from this regulator is used to drive the LEDs and provide the input to the next regulator. The 3.3V regulator is a MCP1702 LDO regulator which can provide up to 200mA - more than enough for what we need.

To monitor the battery voltage I pass it through a voltage divider and into an analog input. We want to report this back over the Bluetooth connection so it's easy to know when the battery is going to need replacing.

The Firmware

The firmware that is currently in the repository is very simple and doesn't use all the features available on the device - there is no read of the battery level for example even though it is available on an analog input.

Essentially all it does is listen on the serial port for incoming commands from the controlling PC. Each command consists of 6 bytes - a start byte (I'm using the '!' character for this), the red, green and blue values for the LEDs, a time period (in seconds) and a termination byte (the newline character). The use of start and termination characters makes it easier to identify a valid command and to re-sync with incoming commands if there is any noise on the line.

Rather than simply set the requested colour the LEDs will cross fade from their current colour to the requested one, hold it for the specified number of seconds and then fade back to off. This serves two purposes - it means the device is not always glowing at you so you are more likely to pay attention when it does start glowing and it improves the battery life.

There are two RGB LEDs in the device, both of which always display the same colour. In the worst case (the LED's are full white) that means a current draw of 120mA (20mA per colour for two LEDs).

To establish the serial connection you simply connect to the Bluetooth device as a virtual serial port. I have pre-configured the HC-05 module as described here so no additional work is required on the firmware side.

Controller Software

To drive the device I wrote a simple Python wrapper that uses pySerial to communicate with the serial port. I separated that out into a separate class that you can use in your own applications. There is a sample command line tool that simply takes the serial port name, the colour values and (optionally) the time and sends that to the device.

Next Steps

Although I have something up and working it is still very much the seed of an idea rather than the complete fulfilment of one. The whole idea of an ambient device is great and I would really like to spread them through my house like confetti. Currently available implementations seem too overpriced for my liking and lack an easy interface.

The current iteration is very clunky, to say the least, and the position of the two LEDs isn't the most appealing when they are lit. I would really like to shrink it down to a much smaller size. Improving the power consumption or moving to a different power source would help - it's the battery and supporting power management circuit that takes up a lot of room.

The boxy form factor needs to go as well. I recently came across some small LED lights at a dollar store that are fairly appealing - you can see one of them in the image to the right. If I replaced the clear plastic top cover with something that provide a bit more diffuse light it would be a perfect size and easy to mount unobtrusively around the house and the garage.

The Bluetooth based connection works well (and the HC-05 module is very easy to use) but requires a controller that is always on so the connection remains established. It also means you need a separate virtual serial port for each device you have connected - not bad if there is only one or two of them but I would like to have the ability to use a larger number than that.

The biggest improvement would not be related to hardware at all but a simple communications protocol that could be used to control devices such as this as well as reading data from remote sensors that makes it easy to integrate everything together. That however, is a much larger project.

PWM Output on the ATtiny85

Monday, May 19 2014 @ 07:10 PM

The next step in the Bluebell project is a way to provide notifications. We can control the device remotely using Bluetooth and now we need to make it display the data sent to it in an easily noticeable format.

I'm going to use an RGB LED for this - it can be driven through standard digital pins to give 7 different colour values but it would be nice to expand that range. Because the ATtiny doesn't have any way to control the current or voltage on an output pin we have to simulate that using PWM.

What is PWM?

PWM (Pulse Width Modulation) is a way of simulating different voltage levels with a single digital (on/off) output pin. This is achieved by modifying the duty cycle (the portion of time that the output is high) of the signal to simulate a lower voltage (or current) over time.

PWM Generation

The graph to the left gives a brief example of how this works. In this case we want the equivalent of a 3V output from a 5V digital output pin. In the graph the blue line represents our target voltage and the green line represents the actual pin output. The output pin is pulled high for 3/5ths of each cycle and low for 2/5ths - over time this is the equivalent of a 3V output.

You can use this technique to control the brightness of an LED (or the speed of a motor) by changing the effective current passing through the device over time. If the LED is connected to the output pin with a current limiting resistor to allow 20mA of current the duty cycle will change the effective amount of current flowing through the device. Using a duty cycle of 50% (the output is high for 50% of the time and low for the other 50%) means that, over time, the effective current through the LED will be 10mA - ff the output pin was always on the LED would consume 20mAh after one hour, with a 50% duty cycle that is reduced to 10mAh.

The frequency of the PWM signal is important as well. When you are driving a LED a frequency above 30Hz will be high enough to control the brightness without any visible flicker. When controlling a motor or simulating an output voltage you will need a much higher frequency. For the purpose of this post I'm just going to concentrate on driving LEDs, motor control and other applications will be left to another post.

PWM Hardware on the ATtiny

The ATtiny has direct hardware support for generating PWM output using the two timer/counter modules on the chip. Both of them can be used to drive the output of a digital pin to generate a PWM signal through the use of a comparison register (OCR0A and OCR0B for timer 0). The counter associated with the timer will cycle from 0 to 255, reset to 0 and then repeat the sequence continuously.

When the output compare function is enabled the corresponding output pin will be set high while the current counter value is less than the comparison register and switch to low when it exceeds the value.

Each timer on the ATtiny is capable of driving two PWM output pins. One of these pins overlaps (it can be driven by either TIMER0 or TIMER1 but not by both simultaneously) which gives a total of 3 possible PWM outputs.

My template library provides control over the two PWM outputs associated with TIMER0 leaving TIMER1 available for other purposes. In this implementation the PWM runs at around 32KHz and has a full 8 bit range giving 256 discrete steps from fully off to fully on. The trace to the left shows the PWM output for a 25% and 50% duty cycle on the outputs.

Implementing PWM in Software

To use the maximum 3 channels of PWM output requires using both timer modules. It is possible, however, to simulate PWM output in software on as many pins as needed using a single timer.

Both timers can generate an overflow interrupt which is triggered when the counter transitions from 255 back to 0. Using this interrupt to generate the time base, an array of our own comparison registers and by manually changing the state of the output pins we can generate a PWM pulse ourselves (although it will be at a much lower frequency than the hardware generated pulse).

The main purpose I have in mind for the software driven PWM is to drive an RGB LED - in this case the PWM frequency needs to be above 30 Hz, enough to have some control over the brightness of the LED without any visible flicker. For this application we don't need the full 8 bit range of values either (realistically the human eye is not going to notice much of a difference when you change the output by 1 bit in either direction).

I settled on a 60Hz PWM frequency with 6 bits (64 steps) of resolution. There are a number of advantages to this:

  1. The overflow interrupt will be triggered at 3840 Hz (60 x 64) which is once every 2083 clock cycles when using an 8MHz clock. Using an average of 2 clock cycles per instruction means that the interrupt is triggered once every 1000 instructions or so. The interrupt handler is less than 100 instructions in length so we are using less than 10% of the processors capacity to provide the PWM.
  2. The 60Hz frequency is a reasonable base for longer time measurements. Using the same interrupt I maintain a tick counter which measures the number of ticks that have elapsed - at this frequency there are 60 ticks per second.

Setting up the timer for this is fairly straight forward:

// Set up the prescaler and enable overflow interrupt
TCCR1 = (1 << CS12); // Divide by 8
TIMSK |= (1 << TOIE1);

Most of the timer settings are left at power up defaults. I use the system clock (8MHz) as the timer clock source and use the prescaler to divide by 8 which will increment the counter at 1MHz. The overflow interrupt will be triggered every 256th count (when the counter wraps around) or 3906 times a second - about as close as to the 3840Hz target frequency as we can get.

Every time the interrupt is triggered we update a ticklet count. To map the 6 bit range to a full 8 bit range we increment it by 4 each time prior to using it - this means that 0 is still 'off' and 255 is 'on' just as they are for the hardware PWM even though there are only 64 steps available. Finally we compare the current counter value against our target to determine if we should turn the output on or off. The code looks like this:

// Update the 'ticklet' count
g_ticklet += (256 / TICKLETS);
// Update PWM outputs

The output is a nice stable PWM signal at close to 60Hz as shown in the trace to the right. The library uses #define statements in hardware.h to specify the number of PWM channels to enable and to assign each one to an output pin.

Although you could theoretically use all 6 IO pins as PWM outputs using this method I've imposed an upper limit of 4 pins in the implementation. This can easily be extended if needed.

A typical set up (in this case for 3 PWM outputs on B0, B1 and B2) looks like this:

/** Enable the SOFTPWM functions */

/** Use 3 PWM channels */
#define SPWM_COUNT 3

/** Pin associated with SPWM0 */
#define SPWM_PIN0 PINB0

/** Pin associated with SPWM1 */
#define SPWM_PIN1 PINB1

/** Pin associated with SPWM2 */
#define SPWM_PIN2 PINB2

You can use both the hardware PWM and software PWM side by side. The software implementation is useful for controlling LED's while the hardware PWM can be used to control motors or servos.

Controlling an RGB LED

Using the software PWM output to control an RGB LED is straight forward. You will need three channels as shown above (one each for the red, green and blue elements of the LED). I run my ATtiny from a 3.3V source which is not enough to drive a blue LED (they typically have a forward voltage drop of over 3V) so I drive the LED's from a separate 5V supply through a 2N7000 FET. This also has the advantage of minimising the current being pulled through the output pins of the ATtiny (20mA per LED which means 60mA if they are all on simultaneously).

The sample code below transitions the LED from red to green to blue and back again:

static uint8_t g_red = 0;
static uint8_t g_grn = 0;
static uint8_t g_blu = 0;

/** Set the target values for the LED's
 * @param red target for red LED
 * @param grn target for green LED
 * @param blu target for blue LED
static void ledSet(uint8_t red, uint8_t grn, uint8_t blu) {
  g_red = red;
  g_grn = grn;
  g_blu = blu;

/** Update the current PWM values to bring them closer to the target
static void ledUpdate() {
  // Update RED value
  uint8_t val = spwmValue(SPWM0);
  else if(val>g_red)
  spwmOut(SPWM0, val);
  // Update GREEN value
  val = spwmValue(SPWM1);
  else if(val>g_grn)
  spwmOut(SPWM1, val);
  // Update BLUE value
  val = spwmValue(SPWM2);
  else if(val>g_blu)
  spwmOut(SPWM2, val);

static bool ledMatch() {
  return ((spwmValue(SPWM0)==g_red)&&(spwmValue(SPWM1)==g_grn)&&(spwmValue(SPWM2)==g_blu));

// Main program

/** Program entry point
void main() {
  // Set up output values
  uint32_t output = 0x00FF0000L;
  ledSet((output>>16)&0xFF, (output>>8)&0xFF, output&0xFF);
  while(true) {
    if(ledMatch()) {
      output = output >> 8;
        output = 0x00FF0000L;
      ledSet((output>>16)&0xFF, (output>>8)&0xFF, output&0xFF);

Rather than simply force the requested colour output the code performs a smooth transition by incrementing (or decrementing) the actual PWM output over time until the target is reached.

As the video above shows this results in a set of intermediate colours before the target is reached and is a good visual test of how smooth the PWM output is. In this case I'm very happy with the result - it's exactly the sort of look I'm after for my notification device.

Next Steps

The core parts of the notification system are now in place - it can be controlled remotely over a Bluetooth link and I have a way of providing some visual feedback using an RGB LED. Now I just need to put those components together in a single circuit, add a battery powered supply and put it in a case. That is going to be the topic of the next post.

Using the HC-05 Bluetooth Breakout

Tuesday, May 13 2014 @ 06:38 PM

In my previous post I described the implementation of a two pin, interrupt driven serial interface for the ATtiny - in this post I'll describe how to configure and use a HC-05 Bluetooth breakout board with that interface so you can remotely control any ATtiny based circuit.

The HC-05 Bluetooth Module

HC-05 Breakout

These modules are widely available and usually relatively cheap (about $AU 6 on eBay). There are two common configurations available - one is a module with a pair of IO pins down each side and the other format is a breakout board with some supporting circuitry and a set of 6 pins at one end. Both have the same functionality but using the module gives you easier access to the GPIO pins on the module as well as some additional state - they are a little harder to design into your circuit though.

For this post I'm using the breakout board, the techniques I describe can still be used with the module format although the pin connections will be in a different location.

Module Configuration

The modules I am using (and, from what I can tell, most other variants) default to using the device name HC-05 and communicate at 9600,N,8,1. We certainly want to change the speed and it would be nice to change the name as well. Thankfully - the chipset has an AT command mode you can enter which allows you to reconfigure the device settings. The changes you make using this method are persistent and will be preserved across power cycles.

HC-05 Configuration

To talk to the module for configuration I am using a 3.3V FTDI Friend cable to provide power as well as communications lines. The diagram to the left shows how to connect the devices together. When you wire it up on the breadboard do not connect the 3.3V lines before you plug the FTDI cable into the USB port.

According to the datasheet there are two ways to enter AT command mode:

  1. If the STATE pin is held high when power is applied to the module it will boot into command mode and set the serial port to 38400N81 regardless of the current serial port settings on the device.
  2. While the device is running pulling the STATE pin high will switch to command mode at the current serial port settings.

For this example we are going to use the first method. First, connect all wires except the 3.3V ones. Plug the FTDI adaptor into your USB port and connect to it with your favourite terminal program at 38400N81. Make sure your serial program is configured to send CR/LF (\r\n) line endings. Connect a wire from the FTDI 3.3V output to the STATE pin and then connect the wire from the FTDI 3.3V output to the 3.3V pin on the breakout board. The red LED on the board should start flashing slowly (about once every two seconds).

To test the connection, try the following command (in these examples the < and > characters indicate the data transfer direction - don't type them).

> AT
< OK

That example simply sends the basic AT (attention) command and the device should respond with a simple OK to indicate that it got it. If you don't get the OK response double check your wiring, the voltage level and your serial port settings.

NOTE: These are relatively cheap devices and are not always of the best quality - it is well worth buying more than one just in case you get a unit that doesn't work. I bought a batch of 5 from the same supplier and one of them simply refuses to enter command mode for some reason even though I can communicate with it over Bluetooth with the default settings.

Once you have access to command mode there are a wide range of options you can change (see the datasheet for a full description of what is available). In this post I'm just going to concentrate on some of the most common options.

The first thing we will want to change is the serial configuration - 9600 is way to slow for most purposes (and my software UART implementation won't work at such low speeds anyway). The following commands will do the trick ...

< OK
< +UART:9600,0,0
> AT+UART=57600,0,0
< OK
< OK
< +UART:57600,0,0

In this case we are querying the current settings ('AT+UART?') and the device responds with the currently active settings (9600 N81 in this case). Next, we tell the device to use 57600 baud, no parity and 1 stop bit ('AT+UART=57600,0,0' - the command format and parameters are described in the datasheet). Just to check that everything is saved away safely we query the current settings again to make sure they match what we just set.

The next thing to change is the device name - this is what appears when you browse for available Bluetooth devices. The default name for the module is HC-05 so something that better reflects what you are connecting to would be nice. Use this sequence of commands ...

< +NAME:HC-05
< OK
< OK

Once again we query the device for the current settings, change it to the value we want (in this case 'BLUEBELL' which is what I've decided to call the device I'm working on) and then query it again to make sure that our changes have gone through.

Finally, we want to change the pass-code used to pair with the device from the default value (1234) to something that is (presumably) secret.

< OK
< +PSWD:1234
> AT+PSWD=0000
< OK
< OK
< +PSWD:0000


Testing is very easy - from the ATtiny side we are simply writing to a serial port, no other setup is required and the only pins you need to connect (apart from 3.3V power and ground) are the Tx and Rx pins. As long as the baud-rate and serial settings match we just read and write data as normal. From the PC side all we need to do is pair with the Bluetooth module and enable it as a serial port.

Serial Connection

I use Xubuntu on my desktop and pairing is done through the Bluetooth Manager application in the Settings Manager. The pairing process is straight forward and the device will automatically be detected as being a serial port.

On my system the pairing process results in a new serial port called /dev/rfcomm0 which I can connect to with my normal serial terminal (in my case I use CuteCom which is perfect for development tasks). Connecting on this port at 57600 now lets me talk to the code running on the ATtiny.

The sample program I'm using simply echoes back all the data it receives and converts lower case letters to upper case just to prove it is not going to some internal loop-back. The image to left shows the results in CuteCom.

The sample program is very simple - this is what the main function looks like:

void main() {
  while(true) {
    if(uartAvail()) {
      uint8_t ch = uartRecv();
        ch = (ch - 'a') + 'A';
      else if((ch>='A')&&(ch<='Z'))
        ch = (ch - 'A') + 'a';

Next Steps

Now we have a way to communicate with device without wires we can easily send and retrieve state from it. I'm planning on using an RGB LED for the notification indicator and I'd like to use PWM to get a full range of colours out of it. That is going to be the subject of the next post.

A Software UART for the ATtiny85

Saturday, May 10 2014 @ 08:18 PM

I've recently started work on a small project to provide visual notifications from a PC over a Bluetooth connection. I mentioned it on G+ a little while ago, it's essentially a minimalist version of this product.

I started working on a post describing the work I was doing but it quickly expanded into something much larger than I expected as I started to describe aspects of my ATtiny85 template library which is being used as the basis of the project.

I decided to split the post into a number of smaller ones so I can cover the various topics in detail without swamping you with information. This post, the first in the series, provides an overview of the software UART implementation in the library - how it's implemented and how it's used. The upcoming posts will cover:

  1. Using a HC-05 breakout module with the library. This will include using AT commands to configure the module and basic connections to the ATtiny.
  2. PWM output on the ATtiny and the template libraries equivalent to the analogOut() function in the Arduino library.
  3. Finally the full project including schematics and code. This uses RGB LEDs to provide colour coded notifications under control of a remote PC through Bluetooth. Use it to provide information in a glance - you can control the colour based on whatever data you choose, stock information, website analytics or Bitcoin exchange rate.

For now, a brief introduction to the library and how to use the software UART.

The ATtiny85 Template Library

I've mentioned my ATtiny Breakout Board and the associated template library in previous posts but only on a very superficial level. Although this post will concentrate on the software UART implementation it's worth running over it again.

Breakout Board

The library is available on GitHub at the link above and the documentation for it can be found here. I've done my best to provide enough information in the project documentation to make it easy to get started. I'd appreciate any feedback you might have on how to improve it.

To use the library as a basis for your own projects simply fork the GitHub project (or clone your own local copy) and start modifying the code in the firmware directory. To get started you only need to start modifying the file hardware.h (which contains the pin assignments and is used to enable or disable various parts of the library) and main.c.

To keep up to date with newer version of the library simply merge the master branch of the original GitHub library into your project every now and then. The majority of updates will be in the firmware/shared and firmware/include directories which contain the implementations and definitions of the library components respectively.

Serial IO Basics

Before I get into the implementation details of the UART functions it's probably best to run through how serial communication appears on the wire. Because we are generating these signals in software it's a good idea to know what is expected and what sort of issues to look out for.

The diagram above shows the voltage levels on the Tx pin when the ASCII character sequence "AVR" is being sent. Obviously this is what will be seen on the Rx pin as well on the receiver end of the connection. In this case the connection is running at 3.3V levels (not RS232 levels) and the connection is configured for 57.6 KBaud, 8 data bits, 1 stop bit and no parity (57600 8N1). I've marked each character on the diagram along with the values for the individual bits.

The diagram above shows the detail for a single character (in this case the ASCII character 'A'). Even though we are only sending 8 bits of data per character additional bits are required for framing (to allow the receiver to detect the start of a character and to determine it has reached the end). In every case there is a single start bit - this causes the communications line to transition from it's idle state (high) to low for at least the duration of a single bit. The data bits come next starting from the least significant bit (bit 0) to the most (bit 7) and finally, the stop bit. The stop bit ensures the line comes back up to it's idle state for at least one bit.

The width, or duration, of each bit is a function of the baud rate - for the speed we are running at that is 1/57600 seconds per bit (about 17 microseconds) - and each bit must be exactly the same width. For our configuration it takes 10 bits to transmit a single byte (1 start bit, 8 data bits and a stop bit) so it takes 170 microseconds per byte of data giving a peak transfer rate of 5.6Kb per second assuming there are no gaps between the bytes. In reality it will be lower).

From the first diagram you can see that there are no restrictions on the gaps between the characters (the inter-character delay). The line must remain at an idle state for this period (logic high) but the duration doesn't have to be a multiple of whole bits and can be as long as is needed.

Writing Serial Data

Writing data out the serial port is fairly straight forward - we know the width of each bit so we simply toggle an output pin as needed and ensure it stays in the right state for the full duration of a bit. All we need to do is pull the output pin low for one bit width for the start bit, send out the individual bits from LSB to MSB and then bring the output high for one bit width as the stop bit. The output is then left high to represent the idle state.

The current implementation does just that in a small loop of assembly code. Because the timing is critical interrupts are disabled while this is happening - you will need to keep this in mind if you are depending on interrupts in your code.

NOTE: The serial send and receive code in the library makes heavy use of the assembly routines developed by Ralph Doncaster and described here. I said that generating the output is straight forward and, as a concept, it is. The actual implementation to get the timing right based purely on the number of instruction cycles needed to execute each op-code is another matter. The code developed by Ralph is a really nice piece of work - I highly recommend visiting his site for more examples.

Reading Serial Data

Reading the serial data is the reverse operation. We simply watch the input pin and wait for it to transition from high (idle state) to low which will be our start bit. We then wait for 1.5 bit periods and then start sampling for the input pin once every bit period to get the value for the appropriate bit. We need 9 samples in all - 8 for the data and 1 for the stop bit.

The reason for the 1.5 bit delay at the start is to ensure that we are sampling the bit value in the middle of it's transmission period. If we are slightly off in our timing we will still get the right value for the bit.

The full implementation of the receiver can be found here. Initially this only provided a blocking implementation - if you called the uartRecv() function it would wait indefinitely until a start bit was detected before it would read and return a character. In cases where nothing happens until it is requested over the serial port (like a bootloader for example) it doesn't matter that the processor is locked in an endless loop with interrupts turned off.

If you want to be able to do work and only respond to serial input when it was available it was not the perfect solution. I've now added an option to have the start bit trigger a pin change interrupt which will cause the incoming byte to be read and buffered for later processing. There is also a uartAvail() function that tells you how many characters are currently buffered.

Pin Change Interrupts

The AVR allows for an interrupt to be triggered when the value of an input pin changes state (in either direction). On the ATtiny85 all of the inputs can be used to trigger this interrupt (which uses the vector PCINT0) but, if you enable multiple pins as triggers, you can't tell which one actually caused the interrupt. You can figure out what the transition was though - if the current reading on the input is 0 you can assume a high to low transition for example.

When the interrupt handler is invoked interrupts will be disabled until they are explicitly enabled by the handler code or the interrupt handler returns. The interrupts don't queue so if another triggering event occurs while the current interrupt is being handled it won't be immediately triggered again when it is done.

Handling the Interrupt

Supporting the interrupt is actually straight forward - what was the original code for the uartRecv() function has been moved to an ISR handler. The code to wait for the start bit is no longer necessary as it has already been detected to trigger the interrupt.

ISR(PCINT0_vect) {
  uint8_t ch;
  // Make sure it is our pin and it is 0
  if(!(PINB&(1<<UART_RX))) {
    // Start the read (assuming we have the start bit)
    asm volatile(
      "  ldi r18, %[rxdelay2]              \n\t" // 1.5 bit delay
      "  ldi %0, 0x80                      \n\t" // bit shift counter
      "RxBit:                              \n\t"
      // 6 cycle loop + delay - total = 5 + 3*r22
      // delay (3 cycle * r18) -1 and clear carry with subi
      "  subi r18, 1                       \n\t"
      "  brne RxBit                        \n\t"
      "  ldi r18, %[rxdelay]               \n\t"
      "  sbic %[uart_port]-2, %[uart_pin]  \n\t" // check UART PIN
      "  sec                               \n\t"
      "  ror %0                            \n\t"
      "  brcc RxBit                        \n\t"
      "StopBit:                            \n\t"
      "  dec r18                           \n\t"
      "  brne StopBit                      \n\t"
      : "=r" (ch)
      : [uart_port] "I" (_SFR_IO_ADDR(PORTB)),
        [uart_pin] "I" (UART_RX),
        [rxdelay] "I" (RXDELAY),
        [rxdelay2] "I" (RXDELAY2)
      : "r0","r18","r19");
    // Now put it in the buffer (if we have room)
      g_buffer[g_index++] = ch;

For our purposes we only want to respond to the start bit which is a transition from 1 to 0 on the input. We sample the Rx pin and if it is not 0 we simply ignore the interrupt and move on. If it is zero we take it to be the start bit and start reading the character.

Because the compiler generates some start up code for the ISR we take that into account in the delay time before we start reading bits.

Input Buffering

Because the receive code is not called directly and can't return a value we need to store the input character somewhere so the main application code can access it later. For this purpose we maintain a small FIFO buffer - newly read characters are added to the end of the buffer and calling the uartRecv() function will take any pending characters from the front of the buffer.

The size of the buffer is set in hardware.h for the project, it generally doesn't need to be too large - just enough to store characters until the main program gets a chance to read the cached characters and do something about them.

Using the Serial Port

The serial port, like other modules in the library, is enabled and configured in the firmware/hardware.h file. The relevant sections look like this:

/* Software UART
 * This feature provides a software implementation of a UART allowing you to
 * perform serial communications. It supports speeds of up to 250Kbps, can be
 * configured to use a single IO pin and has an option to be interrupt driven.

/* Baud rate to use
 * The implementation is optimised for higher baudrates - please don't use
 * anything below 57600 on an 8MHz clock. It does work at up to 250000 baud
 * but you may experience a small amount of dropped packets at that speed.
#define BAUD_RATE 57600

/* Define the pin to use for transmission
#define UART_TX   PINB1

/* Define the pin to use for receiving
 * If this pin is the same as the TX pin the code for the single pin UART
 * implementation is compiled. This means no buffering and no interrupts.
#define UART_RX   PINB0

/* Enable interrupt driven mode
 * Only valid in two pin configuration. If this is defined and you are using
 * two pins for the UART then received data will be read and buffered as it
 * arrives.

/* Size of input buffer
 * Only available in interrupt driven mode. This sets the size of the receive
 * buffer (max 256 bytes).
#define UART_BUFFER 4

The UART_ENABLED macro must be defined to enable any UART functions, UART_TX and UART_RX define the pins to use for Tx and Rx respectively (because the ATtiny only has a single IO port you can use the definitions from - PINB2, PINB3, etc). If UART_INTERRUPT is defined then interrupt driven IO with an input buffer is enabled. Finally UART_BUFFER defines the size of the input buffer to use - this must be at least 1, I've found that 4 is a reasonable value for most cases.

NOTE: Because this is implemented in software rather than hardware it is not full duplex. The code uses instruction cycles to determine the timing of each bit, to ensure that this is accurate interrupts are disabled during both send and receive. What this means is that if a character is incoming while you are sending when the send is complete the receiving code could detect a low data bit as the start bit. This will result in garbage data being detected as a valid character. The solution is to implement a command/response type protocol so both sides of the connection have an idea of when they should be expecting data and when they should be sending it.

With the new changes there are now 3 modes of operation for the UART that you can choose from. These are ...

Single Pin Mode

To use this mode simply define UART_TX and UART_RX to be the same pin, the code will recognise this and change it to be an input or output as needed for communications.

One Pin Serial

This mode is best to use when you want to minimise the number of pins in use on your project but it has a few drawbacks. First, it requires some additional discrete components to work (a transistor, a resistor and a diode - shown in the schematic to the right) and secondly it will echo any data it receives back to the sender. Any code you write to communicate with a device using this configuration will have to take that into account.

This is the configuration used in the sample schematics included in the template and used by the Microboot bootloader. In that situation we use PINB5 (and disable the RESET functionality normally attached to the pin) for code transfer. It's a good place to start with your project and enables you to easily add debugging output using the same connection you load code through.

This mode does not support interrupts so the UART_INTERRUPT macro will be ignored and calling uartRecv() will block until data is available. The uartAvail() function is still present but it will always return 0.

Two Pin Mode (Blocking)

In this mode Tx and Rx are on different pins so no external hardware is needed. It also eliminates the transmission echo that is present on the previous mode.

In the blocking version of the mode (UART_INTERRUPT is not defined) the functions behave as they do for the single pin mode - calling uartRecv() will block until data is available. The uartAvail() function is still present but it will always return 0.

Two Pin Mode with Buffering

If UART_INTERRUPT is set and UART_TX and UART_RX are defined as different pins you will get the interrupt driven implementation with a receive buffer. The value of UART_BUFFER will be used for the buffer size and the uartAvail() function will return the number of characters currently in the buffer - if this value is greater than zero than a call to uartRecv() will not block. If uartAvail() returns 0 then a call to uartRecv() will block until a character is received.

When you are using the interrupt driven implementation you will need to enable interrupts on the chip in your application initialisation as follows:

void main() {
  while(true) {
    // Do stuff here

Helper Functions

I've added a number of helper functions to the UART library that work with any of the operation modes. You can see a full description of them in the documentation.

Essentially they provide a simple way to print 16 bit values to the serial port (in decimal or hexadecimal) as well as zero terminated strings from either RAM or program memory. There is also a minimalist 'printf()' style function called uartFormat() that lets you use a format string to display a combination of strings and integers.

Next Steps

The next post will describe how to configure and use a HC-05 Bluetooth breakout board with the library so we can control a device remotely. Once that is done we can start adding visual notifications with an RGB LED and finally put it all together with a battery pack in a nice 3D printed case.

Follow me on Google+ to see build images, construction notes and previews of upcoming projects.

Recommended Sites

  • EEWeb

    Electrical Engineering News, Resources, and Community.

  • Sprites Mods

    A collection of projects from a very talented engineer.

  • Blondihacks

    A great collection of hacks and projects by Quinn Dunki.

  • Embedded Projects

    Embedded Projects from Around the Web.

Donate Doge

Like the site? Be kind and send a few Dogecoin my way.