Health badge

Electronics

I bought a 128×128 SPI Nokia 5110 display (The ILI9163)  to hook up to an Arduino.  I used an Arduino Mini which runs at 3.3v, since the display uses 3.3v logic.

Then wired it up like:

- LED       -->     +3.3V  -  (5V would work too) 
- SCK       -->     Pin 13 -  Sclk,  (3.3V level)
- SDA       -->     Pin 11 -  Mosi,  (3.3V level)
- A0        -->     Pin 10 -  DC or RS pin (3.3V level)
- RST       -->     Pin 9  -  Or just connect to +3.3V
- CS        -->     Pin 8  -  CS pin (3.3V level)
- Gnd       -->     Gnd
- Vcc       -->     +3.3V

(Note that SDA and SCK pins aren’t optional, because we’re using the arduino SPI hardware to drive this. The DC and CS pins are changeable if you change the code)

layout1_bb

And found a library to use this:

cd /libraries/
git clone https://github.com/adafruit/Adafruit-GFX-Library
git clone https://github.com/sumotoy/TFT_ILI9163C

I had to then edit TFT_ILI9163C/TFT_ILI9163C.h to comment out the #define __144_RED_PCB__ , and uncomment the #define __144_BLACK_PCB__ since my PCB was black. Without this change I got corruption on the top 1/4 of the screen.

Now to get something up and running, using the above pin definition:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <TFT_ILI9163C.h>

/* SPI hardware runs on:
 MOSI:  11
 SCK:   13
 */
#define __CS 8
#define __DC 10
#define __RESET 9

TFT_ILI9163C tft = TFT_ILI9163C(__CS, __DC, __RESET);

const uint16_t WIDTH = 128;
const uint16_t HEIGHT = 128;

void setup(void) {
  tft.begin();
  tft.drawFastHLine(0, 0, WIDTH, 0xFFE0);
}

void loop() {
}

This now gives us a black sceen with a yellow line. Woohoo!

Now, I have a bunch of images that I got an artist to draw, that look like:

face10

I have 10 of these, and their raw size would be 128 * 128 * 4 * 10 = 655KB. Far too large for our 30KB of available flash memory on the nano!

But given the simplicity of the images, we can use a primitive compression algorithm and instead store these as Run Length Encoded. E.g. store as them as pixel index color followed by the number of pixels of that color in a line. We turn to trusty python to do this:

import os,sys
import itertools
from PIL import Image

rleBytes = 0
colorbits = 3

if len(sys.argv) != 3:
    print sys.argv[0], " <input file.png> <output file.h>"
    sys.exit()

def writeHeader(f):
    f.write("/* A generated file for a run length encoded image of " + sys.argv[1] + " */\n\n")
    f.write("#ifndef PIXEL_VALUE_TO_COLOR_INDEX\n")
    f.write("#define PIXEL_VALUE_TO_COLOR_INDEX(x) ((x) >> " + str(8-colorbits) + ")\n")
    f.write("#define PIXEL_VALUE_TO_RUN_LENGTH(x) ((x) & 0b" + ("1" *(8-colorbits)) + ")\n")
    f.write("#endif\n\n")
    f.write("namespace " + os.path.splitext(sys.argv[2])[0] + " {\n")
def writeFooter(f):
    f.write("}")

def writePalette(f, image):
    palette = image.im.getpalette()
    palette = map(ord, palette[:3*2**colorbits]) # This is now an array of bytes of the rgb565 values of the palette
    f.write("/* The RGB565 value for color 0 is in palette[0] and so on */\n")
    # convert from RGB888 to RGB565
    palette565 = [((palette[x] * 2**5 / 256) << 11) + ((palette[x+1] * 2**6 / 256) << 5) + ((palette[x+2] * 2**5/256)) for x in xrange(0, len(palette), 3)]
    f.write("const uint16_t palette[] PROGMEM = {" + ','.join(hex(x) for x in palette565) + "};\n\n")

def writePixel(pixelIndexColor, count):
    global rleBytes
    f.write(hex((pixelIndexColor << (8 - colorbits)) + count) + "," )
    rleBytes += 1

def writePixels(f):
    f.write("/* The first (MSB) " + str(colorbits) + "bits of each byte represent the color index, and the last " + str(8-colorbits) + " bits represent the number of pixels of that color */\n")
    f.write("const unsigned char pixels[] PROGMEM = {")
    (width, height) = image.size

    lastColors = []
    lastColor = -1
    lastColorCount = 0
    for y in xrange(height):
        for x in xrange(width):
            color = pix[x,y]
            if lastColorCount +1 < 2**(8 - colorbits) and (lastColorCount == 0 or color == lastColor):
                lastColorCount = lastColorCount + 1
            else:
                writePixel(lastColor, lastColorCount)
                lastColorCount = 1
            lastColor = color
        # To make the decoder easier, truncate at the end of a row
        # This adds about 5% to the total file size though
        writePixel(lastColor, lastColorCount)
        lastColorCount = 0
    f.write("};\n\n");


image = Image.open(sys.argv[1])
image = image.convert("P", palette=Image.ADAPTIVE, colors=2**colorbits)
#image.save("tmp.png")
pix = image.load()

f = open(sys.argv[2], 'w')
writeHeader(f)
writePalette(f, image)
writePixels(f)
writeFooter(f)
f.close()

print "Summary: Total Bytes: ", rleBytes

The result is that the 10 pictures take up 14kb. This doesn’t produce an optimal image, but it’s a decent starting point. This generates header files like:

/* A generated file for a run length encoded image of face1.png */

#ifndef PIXEL_VALUE_TO_COLOR_INDEX
#define PIXEL_VALUE_TO_COLOR_INDEX(x) ((x) >> 5)
#define PIXEL_VALUE_TO_RUN_LENGTH(x) ((x) & 0b11111)
#endif

namespace face1 {
/* The RGB565 value for color 0 is in palette[0] and so on */
const uint16_t palette[] PROGMEM = {0xef0b,0xf4a3,0x0,0xdb63,0xffff,0xa302,0xe54a,0x5181};

/* The first (MSB) 3 bits of each byte represent the color index, and the last 5 bits represent the number of pixels of that color */
const unsigned char pixels[] PROGMEM = {0x5f,0x56,0xe2...}
}

Which represents the image:

tmp

Which isn’t too bad for a first go. (This was generated by adding a image.save('tmp.png')" in the python code after converting the image).

Now we need an arduino sketch to pull this all in:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <TFT_ILI9163C.h>
#include "face1.h"
#include "face2.h"
#include "face3.h"
#include "face4.h"
#include "face5.h"
#include "face6.h"
#include "face7.h"
#include "face8.h"
#include "face9.h"
#include "face10.h"

const unsigned char *pixels[] = { face1::pixels, face2::pixels, face3::pixels, face4::pixels, face5::pixels, face6::pixels, face7::pixels, face8::pixels, face9::pixels, face10::pixels } ;
const unsigned char numpixels[] = { sizeof(face1::pixels), sizeof(face2::pixels), sizeof(face3::pixels), sizeof(face4::pixels), sizeof(face5::pixels), sizeof(face6::pixels), sizeof(face7::pixels), sizeof(face8::pixels), sizeof(face9::pixels), sizeof(face10::pixels) };
const uint16_t *palette[] = { face1::palette, face2::palette, face3::palette, face4::palette, face5::palette, face6::palette, face7::palette, face8::palette, face9::palette, face10::palette } ;


/*
 We are using 4 wire SPI here, so:
 MOSI:  11
 SCK:   13
 the rest of pin below:
 */
#define __CS 8
#define __DC 10
#define __RESET 9
TFT_ILI9163C tft = TFT_ILI9163C(__CS, __DC, __RESET);

const uint16_t width = 128;
const uint16_t height = 128;

void drawFace(int i);

void setup(void) {
  tft.begin();
}


void drawImage(const unsigned char *pixels, const unsigned char numPixels, const uint16_t *palette) {
  int16_t x = 0;
  int16_t y = 0;
  for( int i = 0; i < sizeof(face1::pixels); ++i) {
    unsigned char pixel = pgm_read_byte_near(pixels + i);
    /* The first (MSB) 3 of each byte represent the color index, and the last 5 bits represent the number of pixels of that color */
    unsigned char colorIndex = PIXEL_VALUE_TO_COLOR_INDEX(pixel);
    int16_t count = PIXEL_VALUE_TO_RUN_LENGTH(pixel);
    uint16_t color = pgm_read_word_near(palette + colorIndex);
    tft.drawFastHLine(x,y,count+1,color);
    x += count;
    if(x >= width) {
      x = 0;
      y+=1;
    }
  }
}

void loop() {
  for(int i = 1; i <= 10; ++i)
    drawFace(i);
}

void drawFace(int i) {
  drawImage(pixels[i-1], numpixels[i-1], palette[i-1]);
}

Pretty straight forward.

Using 4 bits per color uses up slightly too much space: 34,104 bytes (111%) of program storage.
Using 3 bits per color introduces slight artifcats, but presumably removable if we tweak the images, but it fits with plenty of room to space:

Sketch uses 20,796 bytes (67%) of program storage space. Maximum is 30,720 bytes.
Global variables use 134 bytes (6%) of dynamic memory, leaving 1,914 bytes for local variables. Maximum is 2,048 bytes.

The result is this:

Note that this is actually updating at maximum speed. There is room for improvement in the adafruit graphics code however, so I may have a go at improving this if I need faster updates. It might be nice to faster updates anyway in order to minimize battery use.

Android Phone App

I wanted to keep the Android app side of it as clean and simple as possible. There’s not much to document regarding the GUI side of things – it’s a pretty straightforward Android app.

The idea here is to connect via bluetooth to both the arduino and to a Healthband. It measures the heart rate and, more importantly, the very slightly changes in time between the heart pulses. By the knowing the users age, sex and weight, we can calculate the amount of stress that they are currently experiencing. We can then display that in the app, and send that information via Bluetooth Low Energy to our arduino heart badge.

Screenshot_2015-05-29-23-49-29

Screenshot_2015-05-29-23-51-15

It perhaps needs some tweaking – I’m not really that angry, honest!  Lots of polishing is still needed, but the barebones are starting to come together!

Advertisements

Nokia 6110 Part 1 – Electronics

Original Nokia 6110

This is a four part series:

The Nokia 6110 phone came with a snake game that was an instant success.  Many people bought the phone just to play the game.

I wanted to recreate this game, but have an AI playing it continually.

Final Result

This is the final result that I was aiming for:

Starting

I bought one of these phones from ebay, and took it apart.  It comprises of two boards – the top board controls the LCD and the buttons, and uses a ‘springy’ connector to connect to the bottom board.

Bottom Board

The bottom board contains all the phone electronics, including the power connector.

I don’t want to keep any of the phone electronics, so I took out my trusty saw and cut the middle part of the bottom board out, throwing it away and keeping the top most part and the bottom most part:

original_pcbCutting bottom boardPower Connector cut off

(Not shown here, but I also cut off the top part, purely for aesthetics, because the antenna pokes through the case).

Next, I soldered two wires to the power connector, using a multimeter to determine which wires to connect to.

Now that we have power, it’s time to move on to the top board, and get the LCD connected.

Top board

The LCD connects via a “zebra” connector to the board:

lcd

By finding and reading the PDF for this LCD display (pcd8544), we can work out which pins are which.  These pins map to the otherside of the board, connecting to the bottom board via the “springy connector” that I labelled in an image above.  Using a multimeter, I mapped out the pins.  The LED backlights are also helpfully connected here, via a transistor, so those were wired up too:

pin layout

The blue lines here indicate where I’ve shorted the connections to ground, and the red dotted lines is where I’ve shorted the connections to the power line.  In a future version I might leave the LED power separate, and power that separately, bypassing the arduino regulator.  The LEDs draw 100mA at 3.3V.

Here’s the final soldered product:

At this stage, I wrapped the board in salotape to provide a small amount of strength and to avoid any short circuits when placing other boards on top.

Arduino

In the first iteration of this, I used a 5V Arduino Nano running at 16Mhz, even though the display is supposed to accept only 3.3V logic and power.  I looked carefully at the display specifications, and it did appear that it should tolerate 5V.  However the result was that the display contrast was wrong.  So I switched to a 3.3V Arduino Pro Mini, running at 8 Mhz.

To make it possible to program the Arduino, I cut 6 female dupont leads and soldered them to the FTDI header.  This allows me to program the Arduino even when the phone is assembled.

Next I connected up the wires to the arduino pins as marked in the diagram (SCLK=pin2, SDIN=pin5, D/C=pin3, Res=pin4).  Optionally the backlight can be connected to pin6, but I hardwired it to power.

Regulator

In the first iteration of this, I then added an arduino and powered the arduino directly from the 7.9V input.  However the arduino’s has a linear regulator, meaning that it was producing (7.9V – 3.3V) * 100mA = 0.5 Watts of heat.  I thought that this would be tolerable, but it got a bit too warm for my liking inside the case.  So I added a switching regulator to convert 7.9V down to 3.6V, which I then fed to the arduino’s linear regulator.

Top half of the bottom board

Finally, I added the top half of the original board, simply so that the original antenna poked through.

Now the case can be screwed on, and we can start to program it by plugging in an FTDI usb connector:

Next up – Programming it!

3D Printer

On a whim, I decided to make a 3D printer.  I spent the weekend with my brother creating this, and I have to admit we did end up hacking it a lot.

The result wasn’t too bad:

I admit it’s very messy.

But on the first try, I had the temperature too high:

I did start work on an auto calibration system by just crafting a gcode file that would create triangles at various different settings, with a GUI program to let you say which one was best, and then automatically iterating on the best version.

But I got an offer from someone wanting to buy the reprap printer as-is, so I sold it.

I’m very excited to see how the future of 3D printing pans out.

Self Balancing Robot

This was my first electronics project – a simple self-balancing robot.

The wheels and chassis are from a toy truck.  Onto that I salotaped a breadboard with two Light-Dependant-Resistors, attached in series.  On one end I feed 5V, on the other end, ground.  This gives a voltage divider.  And in the middle I connect that to a resistor and a capacitor in parallel.  This gives me a Proportional Derivative signal (out of a PID controller).

This means that we can read off the current ratio of brightness that the two LDRs see, then add on to that signal the rate at which they are changing.  This helps prevent driving the motors too fast when we already moving rapidly towards the balancing point.

We then read this analog voltage on the Arduino, and use it to control the direction of the motors.

There is a huge amount of gear slop which is why it can only barely balance.