Friday, 24 June 2016

Resin casting with a hand-drill

Not much to say about this.
Mike wanted to resin-cast some ball-shapes.
This contraption is as brilliant as it is simple

Thursday, 23 June 2016

Running Arduino atmega328 at 3.3v from lipo battery

Here's something that kept us stumped for a full day - getting an atmega328 to run at 3.3v.
We're using an accelerometer and a sound module that both run at 3.3v, so it made sense that we run our Arduino/AVR at 3.3v too.

Using our spanky TQFP socket, we could easily drop code onto an atmega328 chip; in the IDE simply select Sketch - Upload with programmer


And everything worked just fine.
But what we really wanted to do was make a "bootable" chip, so - if necessary - we could fine-tune the firmware, once the actual chip had been soldered onto the (tiny, cramped) PCB.

Which meant burning a bootloader - so we selected the appropriate board (since we're running at 3.3v we ditched the 16Mhz crystal and went with an 8Mhz ) and hit "burn bootloader".




Again, everything worked, and we could upload sketches from the IDE to the (naked) chip over serial. So then we pulled the power and connected up our lipo battery. For testing, we simply put the lipo output voltage - about 3.8v - through a diode to drop it down to around 3.35v

And then things started getting a bit weird.
Because the chip would respond at 5v, and at 3.7v. But at 3.3v it appeared dead.

(shocking ferric-fingers just look like dirty fingernails - how embarrassing!)

Very peculiar. Whether using the usb-to-serial 3.3v option or the "output" from the diode on the lipo, at anything less than about 3.5v the microcontroller appeared dead.

The first clue about what was going on came from this post: http://arduino.stackexchange.com/questions/23245/confusion-with-atmega328p-and-3-3v-8mhz

Already we were beginning to suspect the brown fuse settings, but we were unable to correctly change them and get a fresh bootloader burned. The "extended" fuse settings are where the brown-out voltage resides:


And we definitely wanted to disable the brown-out detector. So a value of 0xFF seemed like the best option. In the boards.txt file, we found the 3.3v/8Mhz option and changed it. After burning a fresh bootloader to the chip, we got this:


No bootloader. No flashing LED. Nothing.
But if bits 7-3 are not used, and we wanted to set bits 2-0, an extended fuse value of 0xFF seemed like the best option. Except it wasn't. It was only after setting masking the extended fuse value, and ensuring that the first 5 (most significant bits) were cleared could we even get the bootloader to burn.




With the extended fuses set to 0x07 (bit pattern 00000111) the bootloader not only burned, but the mcu started to work with voltages of 3.3v (and lower). So today's lesson: in Microchip and AVR land, "not used" means two very different things!




Wednesday, 22 June 2016

Sacrificing pins to make PCB layout easier

Wherever possible, we like to try to keep to single-sided boards, even for designs going "into production". While it's easy to throw together a poorly-designed, double-sided PCB and send it off to a factory to get made up, that means delays and waiting; and quite often, a much slower development cycle - particularly if, like us, you have an iterative design process!

Keeping designs to single-sides means you can knock up some ferric chloride, try out your design and have a working board in hours, not days or even weeks.

But even if your project isn't time sensitive and you can afford to wait weeks, between ordering and receiving your boards, it often means either:

a) when the board needs redesigning (because you forgot to include the ground plane, or re-route that pin you left a "to-do" note against and then ignored - don't pretend it hasn't happened) you've got another four-to-six-week wait while the new boards arrive OR

b) tack-soldering a few wires and slicing traces with a craft knife, just to get the thing up and working.

Either way, you end up with a less-than ideal PCB or weeks and months of delays.
Homebrew etching is ugly. The results are less than perfect. It's messy and you'll stain your hands yellow for days (and possibly the sink forever if you're not careful). But it gets a result - quickly.

So if you need to redesign a board, you can simply print out a new design, slap down some toner transfer, whack it in some ferric chloride and an hour or two later, you've an amended design. And once you're absolutely sure your design works, you can send the exact same PCB layout to your favourite factory/fab-house and then wait (one time only) for the nice, shiny, silk-screened-and-solder-masked versions to arrive, for the final product.

That's why we still make our own PCBs.
It might not be fashionable (in fact there are plenty people who ask why, when having PCBs manufactured is so cheap, would you want to still etch your own boards) but it is quick and allows the iterative design process to complete fully - no boards with bits of wires tacked on because we've committed to a single design now (even with faults/errors) and can't wait any longer to get the thing soldered up and tested! That's why we favour single-sided designs.

But there are a couple of cheats we use to simplify our single-sided boards.
And they're not always obvious.

The one we use a lot - and that generates a lot of questions - is "pin sacrificing".
Simply put, it means identifying not only which pins on a microcontroller we want to use, but also which ones we can live without. And for every pin we can live without, we have a little bit more room on our board.

Here's an example of  a board we recently came up with.
Space is really tight with this one, so routing complex round-the-houses traces everywhere was out. We also wanted to etch the board ourselves, so double-sided wasn't an option either.


The chip in the middle is a 32-pin ATMega328 TQFP with pin one in the top-left corner. In our design, power is coming in on the bottom-right of the board (where the upside-down number 16 is).

In order to route power to the AVCC and VCC pins on the board, we'd probably have had to used either a double-sided board or made the whole design much bigger, to accommodate either 1206 jumpers across tracks, or routing tracks around, in and through many other pins on the board.

But, looking at the layout of a TQFP atmega chip:



The pin we're trying to get to is 18 (AVCC).
But if we sacrifice pin 17, we can simply join them together. There's no need to go around pin 17 (B5) we can just plough straight through it! Then in our firmware, we can make B5 a digital input or hi-Z tri-stated pin and it's as if it's not even there.

Similarly, when trying to route power to the top of the board, we struggled to run a trace around the outside of pins 4 and 6. But by sacrificing pin 1 (D3) we can run our power trace straight through it, and up towards the top of the board with plenty of room to spare.

It's all too easy to take a working breadboarded layout and try to shoe-horn it into a PCB layout - when changing a few pins would simplify the design no end.

Well it's also possible to make things much easier on yourself if you give up a few of the pins on the microcontroller. After all, if you're not using them, does it matter if they're tied to ground (or power, or even other pins on the mcu)? And if not, don't waste time trying to around them!



Tuesday, 21 June 2016

Messing about with a MMA8452 accelerometer (and Arduino)

As much as we groan and complain about Arduino, a few of the nerds have been doing quite a bit of "real work" with Arduino and AVR chips; mainly because when you hand over a project (in return for money) the recipient likes to understand what's going on - and there are plenty more (non-industrial) developers who understand Arduino than PIC based languages. So that's the platform of choice.

For work.

We've been playing with the idea of using physical objects as controllers. So, for example, rotating a mug on a coaster as a crude volume control. Or selecting images from a TV listings guide to change channels.

All pretty pointless but - let's be honest - pretty fun, ideas to play about with. One device that gets called up a lot when playing with the objects-as-controllers-idea is the accelerometer. Having an object that you can rotate in 3d space is a pretty powerful controller concept, with loads of possibilities. But first, we had to understand what all the funny numbers meant.


Setting things up was pretty simple. The accelerometer uses I2C so we connected A4 to the SDA line and A5 to the SCL, stuck on a bit of power and read back the values:


#include <Wire.h> // Must include Wire library for I2C
#include <SparkFun_MMA8452Q.h>

MMA8452Q accel;

void setup() {
   // put your setup code here, to run once:

   Serial.begin(9600);
   Serial.println(F("MMA8452 Test"));
   accel.init();
   
}

void loop() {
   // put your main code here, to run repeatedly:
   if(accel.available()){

      accel.read();
      printAccels();
      printOrientation();
      delay(1000);
   }
}


void printAccels(){
   Serial.print(accel.cx,3);
   Serial.print("\t");
   Serial.print(accel.cy,3);
   Serial.print("\t");
   Serial.print(accel.cz,3);
   Serial.println("");
}

void printOrientation(){
   byte pl = accel.readPL();
   switch(pl){
       case PORTRAIT_U:
       Serial.println(F("portrait up")); break;

       case PORTRAIT_D:
       Serial.println(F("portrait down")); break;

       case LANDSCAPE_R:
       Serial.println(F("landscape right")); break;

       case LANDSCAPE_L:
       Serial.println(F("landscape left")); break;

       case LOCKOUT:
       Serial.println(F("flat")); break;
   }
}


Then we captured the raw data from the serial window.
Starting with the breadboard face up on the table, I picked it up and slowly rotated it, longways, until all the gubbins was on the underside. Then continued rotating, until it was the right way up again. The output from the serial window was dumped into a Google Docs spreadsheet




And converted into a graph/chart



Even before we started, we were expecting to see some kind of regular pattern between the values. It's only really once we saw the offset sine-waves that it became immediately obvious and apparent what was happening.

The axes on the accelerometer are all off-set by 90 degrees.
And in our graph, we can see two values consistently offset by 90 degrees.
So as the x-axis value increases, the z-axis value decreases. When x is at it's maximum value, z is zero. And all the while, the y value consistently remains at zero.

All we had to do was work out which "plane" in 3d space each axis related to.
Luckily, we've been doing some 3d layout work in Google Ketchup recently, and throwing objects around in Unity, so 90-degree-offset 3d axes are things we're quite comfortable with at the minute (maybe not so much in a few months time, when it's all be forgotten!)

If we imagine our y-axis as a "horizon" and consider that we were rotating our breadboard around this axis, this is pretty much what was going on:




And when you look at that diagram, you can immediately see that the x and z axis should, indeed
always be exactly 90 degrees apart. Which is exactly what we observed.

Now complex 3d maths, matrix transformations and sine/cosine calculations are quite a bit to ask of a lowly 8-bit microcontroller. They only just cope with floating point maths! But with this in mind, there's no reason why we couldn't create a series of look-up tables, to estimate the sine-wave curves of the axis and use these to estimate the rotation of our objects, in world space.

And if we can do that, we'd have some pretty nifty objects-as-real-world-controllers....

Playing audio with a WTV020M01 and Arduino

There are quite a few posts on the interwebs about these little audio playback devices.
First up, they're not always accurately described. We bought a few to try out, based on the idea that they were "mp3 playback modules" (according to the Chinese seller on eBay).
It turns out that the M01 version of the module (although there are -SD versions which may work differently) uses a proprietary format, .ad4

This is a highly-compressed audio format - but one that plays surprisingly well from such a little device! At least as well as our PIC-based audio-players (though at twice the price, I should think so too!). Now a lot of people online have complained that these are picky little devices, some people can't get them to work at all and some just call them sh!te and buy an Adafruit wavshield.

With some trepidation, we wired our little device up to a breadboard.
Although the title of this post suggests an Arduino is involved, to get it to actually play sounds, you don't even need a microcontroller - just some power and a ground connection to the "play" pin on the module.

In the fullness of time, we'll try playing specific files using the SPI interface, but for now we just wanted to hear the little module make some noise. The first thing to ensure is that the correct voltage jumper is set. We suspect that many of the people who simply said these things don't work probably missed this important step!

(see that big blob of solder? We added that, to make it work from a 3.3v supply)


We're using 2Gb Sandisk microSD cards - again off a cheap Chinese seller off eBay, so there's no guarantee that they're genuine; but they worked first time (so maybe they are). To make sure any potential problems were not down to us using the wrong audio format, we downloaded some sample .ad4 files from http://www.buildcircuit.com/how-to-convert-mp3-and-wav-files-to-ad4-format-wtv020sd-tutorial/ and simply dragged-and-dropped them onto the SD card.



Then connected a speaker directly to the SPK+/- PWM pins and added power. The busy line sinks to ground when the device is not playing, so we stuck an LED on there -  and that was it. Pull the "play/pause" line low to make it start playing. No microcontroller needed - just poke a wire into a hole for the same effect.




That'll do us for now.
What was really impressive was the volume of the audio, straight from the pwm pins - no need for an additional amplifier or even a transistor/capacitor combination (as we favoured in our PIC-based audio module). Just pop a speaker onto the pins and off you go!

According to the datasheet, you can use a simple SPI interface to send commands to the module.
If the files are stored with the filenames 0000.ad4, 0001.ad4, 0002.ad4 etc. then you can use the next/prev pins to skip between files. Commands include:

0xFFFF - stop the device
0xFFFE - play/pause toggle
0xFFF7 - set volume to 7

alternatively, simply send 0x0000 to play file 0000.ad4, 0x0001 to play file 0001.ad4 and so on.
As it's late, we'll have to investigate that in the morning.......




Wednesday, 15 June 2016

Making scriptable characters in Unity3D

While waiting for some quotes for PCBs for the light-up-LED-guitar project, we thought we'd revisit another never-ending project (thanks to some prompting from Steve at nerd night last week). Namely, our electronically enhanced board game.

Except making PCBs and hand etching great bit clumsy boards is still a bit boring - so we had a look at some Unity3D. In fact, a few of the nerds are working on some "real life" commissions, using Unity as a multi-platform app development IDE, so it was a perfect excuse to try out some Unity coding techniques, without it feeling too much like real work!

Anyway, after  couple of days, we managed to build a nice framework for a strategy-based game. It consists of a simple map (although getting the lighting right is a real nightmare) and some modular characters (with character-controller scripts). The game framework could be used for a few different game types (turn-by-turn you-go-I-go type games, or real-time simultaneous "command and conquer" type games to name just two).

The character controller accepts a list of commands in a simple script format, and plays back the instructions in the game environment. Here's a video to demonstrate



(the screen refresh rate of our screen capture software isn't brilliant, and Unity uses a lot of cpu cycles on the laptop this example runs on, so it's a bit jittery)

What seems curious is that we don't always get consistent results.
Soldier one has been programmed with the following commands:
  • c.addAction ("M0126");
  • c.addAction ("T0133");
  • c.addAction ("T0103");
  • c.addAction ("F0115");
  • c.addAction ("F0115");
  • c.setFocusOnWalk (true);
This is "move to board 01, square 26" then "turn to face board 01, square 33", then "turn to face board 01, square 3" finally "fire at board 01, square 15" (twice)

Soldier two has been programmed with the following commands:
  • c2.addAction ("T0115");
  • c2.addAction ("M0115");
  • c2.addAction ("T0126");
  • c2.addAction ("F0126");
Which equates to "turn to face board 01, square 15" then "move to board 01, square 15" then "turn to face board 01, square 26" and finally "fire at board 01, square 26".

So the scripts are the same on both soldiers and do not time.
Animations are carried out using Unity's Animator (state-machine-based animation) setting a parameter value to blend the current animation into any other animation

(to blend from the current animation into the "shoot" pose, for example, we simply set the anim_state value to 4 from one of our scripts)

What's peculiar is that, four times out of five, when these scripts are run at the same time, the soldier towards the bottom of the screen gets his shot away first, killing the guy further away.
But - every now and again - the other soldier manages to fire first!

Watch the video again - although the laser beams don't animate well enough to see who is shooting, just watch which soldier falls over at the end of the firing sequence. We seem to have an inconsistent result. From running exactly the same script.

Einstein once said that "madness is doing exactly the same thing over and over again and expecting a different outcome".

I think Unity has gone a bit mad....




Sunday, 5 June 2016

Writing data from Arduino to 24LC256 eeprom chip over serial/UART

Since spending some time with Arduino in recent months (not necessarily our microcontroller of choice, but when working on collaborative projects, sometimes you just have to go with what others are comfortable with) we seem to have spent quite a bit of time writing very similar routines for storing and playing back animation data from eeprom.

One of the easiest external eeprom chips to work with is the 24LC256 chip, and the Wire library in Arduino makes it super-easy to interface with (it's not actually that difficult to do, with a bit of pin-wiggling, but Arduino is all about making things easier, so let's stick with that!)

(although many I2C devices require pull-up resistors on the CLK and SDA lines - thanks to open drain collectors on most devices - we can leave them off if connecting to an Arduino, and use the internal pull-up resistors on the AVR I/O pins)

The chip is wired up as any other I2C device would  be to the Arduino. We're using an Arduino Pro mini - yours may be different, but the main thing to remember is that I2C clock SCK/CLK goes to analogue pin A5. I2C data SDA/DAT goes to A4



Now for some simple code to test everything is working.
Here's a basic framework we like to use. It allows for sending data over serial, in short "packets". We tend to stick to value-to-hex-to-ascii and back again conversions for sending data over methods we're unsure about (this project might end up receiving serial data over bluetooth, or wifi, or even RF radio) so we're going to receive an entire packet of data as a string, and then parse it.

It also means that sending data to the device should be relatively straightforward - simply encode each value into a hexadecimal value and send as a string of 00-FF.


// this for reading/writing to eeprom 24LC256 chip
#include <Wire.h>
#define disk1 0x50      //Address of 24LC256 eeprom chip

long tmr;
long tmrStart;
long tmrNow;
int serialState=0;

String incomingMessage;
String msgToParse;
bool msgWaiting=false;
unsigned int eepromAddress;

// just some temporary variables
int i;
int j;
int k;
int h;

void writeEEPROM(unsigned int eeaddress, byte data) {
   Wire.beginTransmission(disk1);
   Wire.write((int)(eeaddress >> 8));    // MSB
   Wire.write((int)(eeaddress & 0xFF)); // LSB
   Wire.write(data);
   Wire.endTransmission();
   delay(5); // writing can take up to 5m/s per address
}

byte readEEPROM(unsigned int eeaddress) {
   byte rdata = 0xFF;
   Wire.beginTransmission(disk1);
   Wire.write((int)(eeaddress >> 8));    // MSB
   Wire.write((int)(eeaddress & 0xFF)); // LSB
   Wire.endTransmission();
   Wire.requestFrom(disk1, 1);
   if (Wire.available()){ rdata = Wire.read();}
   return rdata;
}

void resetTimer(){
   tmrStart=millis();
   tmr=0;
}

long getTime(){
   long t=millis();
   t=t-tmrStart;
   return t;
}

void getSerialData(){
   if(Serial.available() == true){
      while(Serial.available() == true && msgWaiting==false){
         byte b=Serial.read();
         if(b==0x0a){
            // this is an end of string/message terminating character
            // so update the message to parse and set a flag
            msgToParse = incomingMessage;
            incomingMessage = "";
            msgWaiting = true;
            
         }else if(b==0x0d){
            // ignore linefeed characters
         }else{
            // keep adding incoming characters to the receive string
            incomingMessage = incomingMessage + char(b);
         }         
      }      
   }
}

int getValueFrom(int k){
   // assumes a two-character value from the message string
   // starting at position k - so a string like AFE0596 might
   // be from 1 = 0xFE, from 3 = 0x05, from 5 = 0x96
   int b1 = int(msgToParse.charAt(k));
   int b2 = int(msgToParse.charAt((k+1)));

   if(b1 >= 48 && b1 <= 57){ b1 = b1 - 48;}
   if(b1 >= 65 && b1 <= 74){ b1 = b1 - 55;}
   if(b2 >= 48 && b2 <= 57){ b2 = b2 - 48;}
   if(b2 >= 65 && b2 <= 74){ b2 = b2 - 55;}
   
   int j = b1 * 16;
   j = j + b2;
   return(j);
}

void parseMessage(){   
   int deviceID = getValueFrom(0);
   int commandByte = getValueFrom(2);
   int commandValue = getValueFrom(4);

   Serial.print(F("device:"));
   Serial.print(deviceID);
   Serial.print(F(" command:"));
   Serial.print(commandByte);   
   Serial.println(" ");

   Serial.print(F(" value:"));
   Serial.print(commandValue);

   switch(commandByte){      

      case 'W':
      // write data to the specified eeprom address
      // (address should be two bytes)
      
      eepromAddress = commandValue * 256;
      commandValue = getValueFrom(6);
      eepromAddress += commandValue;

      Serial.print(F("writing to eeprom address: "));
      Serial.println(eepromAddress);

      // now go through the string until there are no more characters left
      // and "stream write" them to the eeprom chip
      k = 8;
      j = msgToParse.length();
      Serial.print(j);
      Serial.println(" characters in string");
      
      h = 0;
      while(k < j){
         commandValue = getValueFrom(k);

         Serial.print("pos:");
         Serial.print(k);
         Serial.print(" value:");
         Serial.println(commandValue);
         
         writeEEPROM(eepromAddress, byte(commandValue));
         eepromAddress++;
         h++;
         k += 2;
      }

      Serial.print(h);
      Serial.println(F(" bytes written"));
      break;

      case 'Q':
      // testing: read back data from eeprom address
      eepromAddress = commandValue * 256;
      commandValue = getValueFrom(6);
      eepromAddress += commandValue;

      commandValue = getValueFrom(8);

      Serial.print(F("reading "));
      Serial.print(commandValue);
      Serial.print(F(" bytes from eeprom address: "));
      Serial.println(eepromAddress);

      for(h=0; h < commandValue; h++){
         byte b = readEEPROM(eepromAddress);
         Serial.print(F("value: "));
         Serial.println(b);
         eepromAddress++;
      }
      
   }
}

void setup() {
   // put your setup code here, to run once:
   Serial.begin(57600);
   Wire.begin();   
}

void loop() {
   // put your main code here, to run repeatedly:

   getSerialData();
   if(msgWaiting==true){
      Serial.print(F("received: "));
      Serial.println(msgToParse);
      parseMessage();   
      msgWaiting=false;
   }
   
}

This approach allows us to send "command strings" to different devices on a network and - where necessary - have each device save the data sent to it, to it's own local eeprom chip.

For example, let's send the command "turn your RGB LED to blue" to device number 3.
Our data packets are in the form:

device_id : command_byte : values/parameters

So let's say we use the command byte (or character) 'C' for colour. And perhaps blue could be a colour in a palette, indexed at... let's say 15. So to send our message, we'd send the decimal values

3 (device id) 67 (ascii for the letter C) 15 (the colour palette index).

Converting this into hex, we end up with 03, 43, 0F
So we send a string (followed by the usual carriag return/line feed combination) 03430F.
That's all well and good, but what about this pesky eeprom chip?

Well, having now understood our "data packet protocol" we can tweak it a bit, so that when we receive a particular "command byte" (let's say, W) it's an instruction to write the data that follows to the eeprom chip. We'll also need to send an address to write the data to. And that should be enough.

Except, if were writing a few bytes into consecutive locations, that's a lot of message overhead per byte. So we made the routine a little more intelligent: if it receives a write command, it expects the next two bytes to be the eeprom memory location to start writing to, and then every pair of characters after that represents the hex value to be stored. The routine then updates the eeprom address (increases it by one) so that a number of bytes can be "streamed" and written to eeprom.

Here's an example.
Let's say we want device number 5 to store the values 48,19,203 to it's external eeprom chip, starting at address 100 (so 48 goes into location 100, 19 into 101, 203 into 102 etc).

We'd need to send the decimal values 5 (device id), 87 (ascii for W), 0, 100 (memory location), 48, 19, 203 (values - note the zero before the value 100 - that's because our memory location is a two-byte value). Turning this into hex, we get 0500643013CB



Great - so it says it wrote that data to eeprom; how do we find out?
In our routine we wrote some test code, that allows us to send a Q character, followed by an address (two-bytes) and a number of bytes to read back from memory. So we send

decimal value 5 (device id), 81 (ascii for Q), 0, 100 (memory location), 3 (number of bytes to read) which turns into the string of hex characters: 0551006403


So now we have a simple framework for robustly sending messages that can be decoded in a nice-easy-to-understand string format (a lot of people don't like character/byte arrays for some reason). The routine is written so that a carriage return indicates the end of a "string" and parsing will begin at the next available opportunity in the main loop. Even if string decoding hasn't begun, the next message can start to arrive over serial.

There are a couple of caveats:
The Arduino serial library uses interrupts to receive data into a circular buffer. This buffer has only 64 bytes, so don't make your strings of text too long! If you need to store lots of data to eeprom, split it up and send it over a number of shorter messages.

Also, if an entire string has been received but parsing hasn't begun before another full string (terminating with the CrLf combination) is received, it's quite possible that the earlier message will be lost. A simple "stack" of messages could be implemented if necessary, but for now we're just sticking with a relatively simple framework.