Thursday, 18 July 2013

Homemade Speaker Stands

Quite some time ago I bought a pair of Bowers & Wilkins 685s, and recently I decided to build some small speaker stands to put them on.






The top and bottom are made from 25 mm MDF, and the centre column is made from 3" schedule 40 PVC tube. There is an aluminium disk to strengthen the MDF where it attaches to the tube, and it is all held together by six M3 countersunk screws that tap directly into the walls of the PVC tube.

The central tube is filled with sand, to give the stands some weight, and to dampen vibrations from the speakers.

I sprayed the MDF with several coats of grey primer, sanding it down between coats. I then painted it with satin black spray paint. Finally I sprayed the PVC tubes with a few coats of gloss black lacquer.


Here is the aluminium disk in more detail:


Wednesday, 10 July 2013

A Multiple Encoder Library For Arduino

Here is an Arduino library which allows you to connect and decode up to 5 rotary encoders. It is basically an extension of my original encoder library, which can only decode one encoder.

You can download the library here: https://github.com/frodofski/Encoder_Polling_V2

Here is the example sketch:

#include <EncoderV2.h>

const int pin_A = 4;  // Encoder input pins
const int pin_B = 5;
const int pin_C = 6;
const int pin_D = 7;

void setup()
{
  Serial.begin(9600);
  encoder_begin();  // Start the library
  attach_encoder(0, pin_A, pin_B);  // Attach an encoder to pins A and B
  attach_encoder(1, pin_C, pin_D);  // Attach another encoder to pins C and D
}

void loop()
{
  int dir_0 = encoder_data(0);  // First encoder
  int dir_1 = encoder_data(1);  // Second
  
  if(millis() > 10000)
  {
    detach_encoder(1);  // After 10 seconds, detach encoder 1 
  }
  
  if(dir_0 != 0)  // Check for rotation
  {
    Serial.print("Encoder 0: ");
    Serial.println(dir_0);
  }
  
  if(dir_1 != 0)  // Check for rotation
  {
    Serial.print("Encoder 1: ");
    Serial.println(dir_1);
  }
}

The library is super easy to use, as it has only four functions:

  • encoder_begin() starts the library by setting up timer2 (note: this may conflict with the tone library)
  • attach_encoder(encNum, pin_A, pin_B) attaches an encoder to pin_A and pin_B, and sets these pins as inputs. encNum can equal 0, 1, 2, 3, or 4, allowing for up to five rotary encoders. The library will now start polling those two input pins.
  • detach_encoder(encNum) will detach the input pins corresponding to that encoder, and it will stop polling those two input pins. The pins can now be used as regular io again.
  • encoder_data(encNum) returns the state of the specified encoder. It returns a 1 or -1 if the encoder has turned, depending on the direction of rotation. It will return a 0 if the encoder has not turned, or if there is no encoder attached to that particular encNum.

The library will store that last known direction of the rotary encoder, until the encoder_data() function has been called for that encoder. This allows you to run the main sketch at any speed, without having to worry about missing a step.


Finally, you may need to use a hardware debouncer for each of the input pins if your're not using an optical encoder, or if your mechanical rotary encoder is particularly bouncy.

Tuesday, 9 July 2013

An Even Better IR Decoder Library


I improved my old IR decoder library by adding some new features, without making it any more complicated. What's nice it that this is all done in the interrupt routine, so the library is still non-blocking. Here are the new features:

  • It no longer records invalid IR codes, and automatically resets when an invalid code is received (who cares if the code is invalid anyway?).
  • It removes the start bits, and the toggle bit from the code, to make it easier to determine what button was pressed on the remote.
  • It can determine whether a code is a repeat, or a new button press by the user.


The library can be downloaded here: https://github.com/frodofski/RC5_Decoder_V2


As before, the ir_begin() function starts the decoder, but ir_data() is a little different:
  • If there is no valid code present, it will return a 0. 
  • If there is a new code, it will return the code. 
  • Finally, if it is receiving a string of codes with each one being a repeat of the previous one, it will return -1 until the string is broken.

Whats nice about the repeat detection, is that you don't have to worry about stuff happening more than once when you hold down a button on the remote. And if you do want something to happen for as long as the button is held down, like a volume control for example, you just need to look for the -1s.

Here is the example code:

#include "irDecoderV2.h"

const int IR_pin = 10;

int code = 0;

void setup()
{
  Serial.begin(9600);  
  ir_begin(IR_pin);  // Start the decoder
}

void loop()
{
  code = ir_data();  // Check for IR code
  
  if(code > 0)  // If a code is available...
  {
    Serial.print("Code: "); // Print it
    Serial.println(code);
  }
  
  else if(code == -1)  // If its a repeat
  {
    Serial.println("Repeat");  // Print it
  }
  
  delay(250);  // Prevent clogging up serial monitor
}




As before, the library configures timer2 in a way which might conflict with the Tone library, and also may cause problems when trying to use pwm on digital pins 9 and 10.

Wednesday, 3 July 2013

Bit Banging Data with the Arduino

Normally if you want to send data over SPI you would use the Arduino's built in hardware SPI controller, which is accessible via the SPI.h library. The problem with this is that you are confined to a specific set of pins, and to sending data in multiples of 8 bits. But what if you want to send data over a different set of pins? Or 12 bits of data for example? Or what if your microprocessor doesn't even have an SPI controller?

Bit banging is the technique of manually manipulating the microprocessor's pins in software to transmit the data, rather than using a dedicated hardware controller. Although it is a lot efficient than a hardware controller, it is a little more flexible.

To keep things simple, we will be looking at SPI mode 1 only. In this mode, the base value for the clock is 0, and data is captured on the rising edge.

Lets take a look at the SPI timing diagram for mode 1:


This first thing we need to do, to communicate with a slave, is pull the SS pin low. This is usually done in software anyway, and not by a dedicated controller, so this isn't really part of the bitbanging process.

Here are the steps we need to do to bitbang the data:

  1. First we need to set the MOSI (Master Out Slave In) pin to a 1 or a 0, according to the first bit in our data variable. 
  2. We then need to set the SCK (Clock) pin high. This causes the slave to capture the state of the MOSI pin (ie. the first 'bit' of the data) into its own memory.
  3. Next, we read the state of the MISO (Master In Slave Out) pin into the first bit of a new variable. When the transmission is complete, this variable will hold the data sent from the slave to the master.
  4. Now the SCK pin can be set low.
  5. Finally, we repeat this process, moving on to the next bit in the variable each time, for however many bits are in the data variable.

I wrote an example sketch to demonstrate this:

const int SS_pin = 11;
const int SCK_pin = 10;
const int MISO_pin = 9;
const int MOSI_pin = 8;

byte sendValue = 74;   // Value we are going to send
byte returnValue = 0;  // Where we will store the value sent by the slave

void setup()
{
  digitalWrite(SS, HIGH);  // Start with SS high
  
  pinMode(SS_pin, OUTPUT);
  pinMode(SCK_pin, OUTPUT);
  pinMode(MISO_pin, INPUT);
  pinMode(MOSI_pin, OUTPUT);
}

void loop()
{
  digitalWrite(SS_pin, LOW);        // SS low
  returnValue = bitBang(sendValue); // Transmit data
  digitalWrite(SS_pin, HIGH);       // SS high again 
}


byte bitBang(byte _send)  // This function is what bitbangs the data
{
  byte _receive = 0;
  
  for(int i=0; i<8; i++)  // There are 8 bits in a byte
  {
    digitalWrite(MOSI_pin, bitRead(_send, i));    // Set MOSI
    digitalWrite(SCK_pin, HIGH);                  // SCK high
    bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO
    digitalWrite(SCK_pin, LOW);                   // SCK low
  }
  
  return _receive;        // Return the received data
}

This is all well and good, but the problem with this code, specifically the bitBang function, is that it is really slow. The reason for this is that the digitalWrite, digitalRead, bitWrite, and bitRead functions are all inherently slow. This is no fault of the people over at arduino, but is just the way it is.

To improve the speed, we can replace these functions with direct port manipulations, and bitwise manipulations.

Here is the bitBang function again, but much faster:

byte bitBang(byte _send)   // This function is what bitbangs the data
{
  byte _receive = 0;
  
  for(int i=0; i<8; i++)   // There are 8 bits in a byte
  {
    if(_send & _BV(i))     // Set MOSI
      PORTB |= _BV(PORTB0);
    else
      PORTB &= ~_BV(PORTB0);
    
    PORTB |= _BV(PORTB2);  // SCK high
    
    if(PINB & _BV(PORTB1)) // Capture MISO
      _receive |= _BV(i);
    else
      _receive &= ~_BV(i);
      
    PORTB &= ~_BV(PORTB2); // SCK low
  }
  
  return _receive;         // Return the received data
}

Much more complicated, isn't it? But faster and more efficient than before. Unfortunately however, this in itself is not without problems. This code is far less portable than the previous example, meaning it probably wont work on a different Arduino board with a different microcontroller. Also, it is not as simple as changing a single variable to choose the pins you want, but the pins that you want to use must be hard coded into the sketch (or else it gets quite complicated).

As a result, we have struck a compromise between portability vs. speed.

If you don't know anything about port manipulations, I would stick with the first example. But if you know what you're doing, then clearly the second option is much better.

One last thing that I nearly forgot: You can change the number of loops in for loop if you want to send fewer or more bits (provided you change _send and _receive to something bigger than a byte).