This is just a short post to show off some new Clixx.IO related components I've been working on over the past few weeks.

Board Designs

MCP23008 I2C Board

I recently received some PCBs that I'd sent off for fabrication a while ago. On the left you can see the MCP23008 board compared with the stripboard prototype I designed earlier and used in the keypad interface and alphanumeric LCD display controller.

LPC810 Controller Board

The image on the right shows the prototype and finished version of the LPC810 controller board I've been working on. I haven't written about this yet but you will here a fair bit about it in the future - it's the first of a series of SmartTabs that I'm working on using a range of different processors. David has already done some designs around the PIC12F and ATtiny processors - I'm working on the LPC810 (shown above) and a PIC16F based board.

Docking System

Modular Docks

I managed to resolve the problems I was having with the 3D printed cases for my external Clixx docks (here is the source for the ``` //---------------------------------------------------------------------------- // Casing for 3 port docks //---------------------------------------------------------------------------- RESOLUTION = 0.1;

PITCH = 2.54;

WALLWIDTH = 2; PCBWIDTH = 31; PCBHEIGHT = 50; PCBDEPTH = 1.4; SOCKET_DEPTH = 8.5;

CON1WIDTH = 20; CON2WIDTH = 14; CON2OFFSET = 15; CONDEPTH = 4;

TABWIDTH = 10.16; // Width of tabs in mm TABHEIGHT = 4; // Height of tab (circuit board depth) TAB_DEPTH = 8.89; // Depth of tabs in mm

TWINTABGAP = 10.16; // Gap between the two tabs on a TwinTab board

TWINTABHEADER = 6 * 2.54; // Width of the header for TwinTab boards SINGLETABHEADER = 3 * 2.54; // Width of a header for SingleTab boards HEADER_HEIGHT = 1 * 2.54; // Height of headers

/** Generate a shape that can be used to cut out space for the tab and header. * * x, y - specify the co-ordinates of the top left hand side of the header. * depth - how deep to make the object (on the z axis) */ module TwinTabCutout(x, y, slip = 0.5, depth = 2 * WALLWIDTH) { translate(v = [ x, slip + y, 0 ]) { union() { // Make the header translate(v = [ (((2 * TABWIDTH) + TWINTABGAP + (2 * slip)) - (TWINTABHEADER + (2 * slip))) / 2, 0, 0 ]) { cube(size = [ TWINTABHEADER + (2 * slip), HEADERHEIGHT + (2 * slip), depth ]); } // Make space for the tabs translate(v = [ 0, (TABHEIGHT + HEADERHEIGHT) / 2, 0 ]) { cube(size = [ (2 * TABWIDTH) + TWINTABGAP + (2 * slip), TAB_HEIGHT + (2 * slip), depth ]); } } } }

// Generate a rounded box with the given dimensions module roundedEdge(length, radius) { difference() { translate(v = [ radius, radius, 0 ]) { cube(size = [ radius * 2, radius * 2, length ], center = true); } cylinder(r = radius, h = length + 1, center = true, $fs = RESOLUTION); } }

module roundedCorner(radius) { difference() { cube(size = [ radius * 2, radius * 2, radius * 2 ]); sphere(r = radius, $fs = RESOLUTION); } }

module roundedBox(width, height, depth, radius) { difference() { // Start with the cube cube(size = [ width, height, depth ]); translate(v = [ width - radius, 0, depth - radius ]) { rotate(a = [ 90, 0, 0 ]) { roundedEdge(height * 3, radius); } } // Substract the edges translate(v = [ radius, 0, depth - radius ]) { rotate(a = [ 90, 0, 0 ]) { rotate(a = [ 0, 0, 90 ]) { roundedEdge(height * 3, radius); } } } translate(v = [ 0, radius, depth - radius ]) { rotate(a = [ 0, 90, 0 ]) { rotate(a = [ 0, 0, 180 ]) { roundedEdge(height * 3, radius); } } } translate(v = [ 0, height - radius, depth - radius ]) { rotate(a = [ 0, -90, 0 ]) { roundedEdge(height * 3, radius); } } translate(v = [ radius, radius, 0 ]) { rotate(a = [ 0, 0, 180 ]) { roundedEdge(height * 3, radius); } } translate(v = [ width - radius, radius, 0 ]) { rotate(a = [ 0, 0, -90 ]) { roundedEdge(height * 3, radius); } } translate(v = [ width - radius, height - radius, 0 ]) { roundedEdge(height * 3, radius); } translate(v = [ radius, height - radius, 0 ]) { rotate(a = [ 0, 0, 90 ]) { roundedEdge(height * 3, radius); } } // Substract the corners translate(v = [ width - radius, height - radius, depth - radius ]) { roundedCorner(radius); } translate(v = [ radius, height - radius, depth - radius ]) { rotate(a = [ 0, 0, 90 ]) { roundedCorner(radius); } } translate(v = [ radius, radius, depth - radius ]) { rotate(a = [ 0, 0, 180 ]) { roundedCorner(radius); } } translate(v = [ width - radius, radius, depth - radius ]) { rotate(a = [ 0, 0, 270 ]) { roundedCorner(radius); } } } }

module snapClip(stemwidth, stemheight, base, height, width) { // Start with an extruded triangle union() { translate(v = [ 0, width / 2, stemheight ]) { rotate(a = [ 90, -90, 0 ]) { linearextrude(height = width, center = true, convexity = 10, twist = 0) { polygon(points=[ [ 0, 0 ],[ height, 0 ],[ 0, base ] ], paths=[ [ 0, 1, 2 ] ]); } } } translate(v = [ -stemwidth, 0, 0 ]) { cube(size = [ stemwidth, width, stem_height + 0.1 ]); } } }

module dockStation() { difference() { // Create the basic box roundedBox( PCBWIDTH + (2 * WALLWIDTH), PCBHEIGHT + (6 * WALLWIDTH), PCBDEPTH + SOCKETDEPTH + (3 * WALLWIDTH), WALLWIDTH ); // Holes for the slots translate(v = [ WALLWIDTH - 0.25, 0, -WALLWIDTH ]) { TwinTabCutout(0, WALLWIDTH + (4.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); TwinTabCutout(0, WALLWIDTH + (11.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); TwinTabCutout(0, WALLWIDTH + (18.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); } // Knock out holes for the connectors translate(v = [ WALLWIDTH, -WALLWIDTH, -WALLWIDTH ]) { cube(size = [ PCBWIDTH, WALLWIDTH * 5, (WALLWIDTH * 3) + CONDEPTH ]); } translate(v = [ -WALLWIDTH, WALLWIDTH + CON2OFFSET, -WALLWIDTH ]) { cube(size = [ WALLWIDTH * 4, CON2WIDTH, (WALLWIDTH * 3) + CONDEPTH ]); } // Knock out room for the PCB and the inner shell translate(v = [ WALLWIDTH, WALLWIDTH, -WALLWIDTH ]) { cube(size = [ PCBWIDTH, PCBHEIGHT, PCBDEPTH + (4 * WALLWIDTH) ]); } translate(v = [ WALLWIDTH * 2, WALLWIDTH * 2, -WALLWIDTH ]) { cube(size = [ PCBWIDTH - (WALLWIDTH * 2), PCBHEIGHT + (3 * WALLWIDTH), PCBDEPTH + SOCKETDEPTH + (3 * WALLWIDTH) ]); } } }

rotate(a = [ 0, 180, 0 ]) { dockStation(); } ```

as well as the ``` //---------------------------------------------------------------------------- // Casing for 3 port docks //---------------------------------------------------------------------------- RESOLUTION = 0.1;

PITCH = 2.54;

WALLWIDTH = 2; PCBWIDTH = 31; PCBHEIGHT = 50; PCBDEPTH = 1.4; SOCKET_DEPTH = 8.5;

CON1WIDTH = 20; CON2WIDTH = 14; CON2OFFSET = 15; CONDEPTH = 4;

TABWIDTH = 10.16; // Width of tabs in mm TABHEIGHT = 4; // Height of tab (circuit board depth) TAB_DEPTH = 8.89; // Depth of tabs in mm

TWINTABGAP = 10.16; // Gap between the two tabs on a TwinTab board

TWINTABHEADER = 6 * 2.54; // Width of the header for TwinTab boards TWINTABSIDEHEADER = 4 * 2.54; // Width of the side header SINGLETABHEADER = 3 * 2.54; // Width of a header for SingleTab boards HEADERHEIGHT = 1 * 2.54; // Height of headers

/** Generate a shape that can be used to cut out space for the tab and header. * * x, y - specify the co-ordinates of the top left hand side of the header. * depth - how deep to make the object (on the z axis) */ module TwinTabCutout(x, y, slip = 0.5, depth = 2 * WALLWIDTH) { translate(v = [ x, slip + y, 0 ]) { union() { // Make the header translate(v = [ (((2 * TABWIDTH) + TWINTABGAP + (2 * slip)) - (TWINTABHEADER + (2 * slip))) / 2, 0, 0 ]) { cube(size = [ TWINTABHEADER + (2 * slip), HEADERHEIGHT + (2 * slip), depth ]); translate(v = [ 7.5 * PITCH, 0, 0 ]) { cube(size = [ TWINTABSIDEHEADER + (2 * slip), HEADERHEIGHT + (2 * slip), depth ]); } } // Make space for the tabs translate(v = [ 0, (TABHEIGHT + HEADERHEIGHT) / 2, 0 ]) { cube(size = [ (2 * TABWIDTH) + TWINTABGAP + (2 * slip), TAB_HEIGHT + (2 * slip), depth ]); } } } }

// Generate a rounded box with the given dimensions module roundedEdge(length, radius) { difference() { translate(v = [ radius, radius, 0 ]) { cube(size = [ radius * 2, radius * 2, length ], center = true); } cylinder(r = radius, h = length + 1, center = true, $fs = RESOLUTION); } }

module roundedCorner(radius) { difference() { cube(size = [ radius * 2, radius * 2, radius * 2 ]); sphere(r = radius, $fs = RESOLUTION); } }

module roundedBox(width, height, depth, radius) { difference() { // Start with the cube cube(size = [ width, height, depth ]); translate(v = [ width - radius, 0, depth - radius ]) { rotate(a = [ 90, 0, 0 ]) { roundedEdge(height * 3, radius); } } // Substract the edges translate(v = [ radius, 0, depth - radius ]) { rotate(a = [ 90, 0, 0 ]) { rotate(a = [ 0, 0, 90 ]) { roundedEdge(height * 3, radius); } } } translate(v = [ 0, radius, depth - radius ]) { rotate(a = [ 0, 90, 0 ]) { rotate(a = [ 0, 0, 180 ]) { roundedEdge(height * 3, radius); } } } translate(v = [ 0, height - radius, depth - radius ]) { rotate(a = [ 0, -90, 0 ]) { roundedEdge(height * 3, radius); } } translate(v = [ radius, radius, 0 ]) { rotate(a = [ 0, 0, 180 ]) { roundedEdge(height * 3, radius); } } translate(v = [ width - radius, radius, 0 ]) { rotate(a = [ 0, 0, -90 ]) { roundedEdge(height * 3, radius); } } translate(v = [ width - radius, height - radius, 0 ]) { roundedEdge(height * 3, radius); } translate(v = [ radius, height - radius, 0 ]) { rotate(a = [ 0, 0, 90 ]) { roundedEdge(height * 3, radius); } } // Substract the corners translate(v = [ width - radius, height - radius, depth - radius ]) { roundedCorner(radius); } translate(v = [ radius, height - radius, depth - radius ]) { rotate(a = [ 0, 0, 90 ]) { roundedCorner(radius); } } translate(v = [ radius, radius, depth - radius ]) { rotate(a = [ 0, 0, 180 ]) { roundedCorner(radius); } } translate(v = [ width - radius, radius, depth - radius ]) { rotate(a = [ 0, 0, 270 ]) { roundedCorner(radius); } } } }

module snapClip(stemwidth, stemheight, base, height, width) { // Start with an extruded triangle union() { translate(v = [ 0, width / 2, stemheight ]) { rotate(a = [ 90, -90, 0 ]) { linearextrude(height = width, center = true, convexity = 10, twist = 0) { polygon(points=[ [ 0, 0 ],[ height, 0 ],[ 0, base ] ], paths=[ [ 0, 1, 2 ] ]); } } } translate(v = [ -stemwidth, 0, 0 ]) { cube(size = [ stemwidth, width, stem_height + 0.1 ]); } } }

module dockStation() { difference() { // Create the basic box roundedBox( PCBWIDTH + (3 * PITCH) + (2 * WALLWIDTH), PCBHEIGHT + (6 * WALLWIDTH), PCBDEPTH + SOCKETDEPTH + (3 * WALLWIDTH), WALLWIDTH ); // Holes for the slots translate(v = [ WALLWIDTH - 0.25, 0, -WALLWIDTH ]) { TwinTabCutout(0, WALLWIDTH + (4.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); TwinTabCutout(0, WALLWIDTH + (11.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); TwinTabCutout(0, WALLWIDTH + (18.5 * PITCH), depth = PCBDEPTH + SOCKETDEPTH + (5 * WALLWIDTH)); } translate(v = [ 2.5 * PITCH, 0, 0 ]) { // Knock out holes for the connectors translate(v = [ WALLWIDTH, -WALLWIDTH, -WALLWIDTH ]) { cube(size = [ PCBWIDTH, WALLWIDTH * 5, (WALLWIDTH * 3) + CONDEPTH ]); } // Knock out room for the PCB and the inner shell translate(v = [ WALLWIDTH, WALLWIDTH, -WALLWIDTH ]) { cube(size = [ PCBWIDTH, PCBHEIGHT, PCBDEPTH + (4 * WALLWIDTH) ]); } translate(v = [ WALLWIDTH * 2, WALLWIDTH * 2, -WALLWIDTH ]) { cube(size = [ PCBWIDTH - (WALLWIDTH * 2), PCBHEIGHT + (3 * WALLWIDTH), PCBDEPTH + SOCKETDEPTH + (3 * WALLWIDTH) ]); } } } }

rotate(a = [ 0, 180, 0 ]) { dockStation(); } ``` ). I printed mine with a blue PLA (as you can see in the image to the left) and the end result looks very impressive.

I'm still waiting for the updated Babyduino boards to arrive so I've wired them up to the original design using the same pins that will be used on the new board - this will simplify migration in the future. It's not optimal but it's still very functional as you can see in the image to the right, you could easily wire them up to a standard Arduino without any problems.

All Wired Up

This particular example is wired to run off battery power using a power regulator board with battery voltage and current monitoring (I'll put up a post describing that board in the near future) - I'm using it to prototype the Race Timing System I briefly described in the last post about the Babyduino.

Next Steps

As you can see there have been big steps forward with the Clixx related components here at the lab - there is still a lot of work to do (and the posts on this site need to be caught up with what has already been done) but I seem to have reached a critical mass. The new work moves along quite rapidly now and builds on the parts that are already finished. It is now a lot easier to design and test new boards, build up prototypes for complete projects and reuse the components I already have.

Now that the most important parts of the infrastructure are in place I can spend a bit more time on project work rather than just focusing on individual boards - which is what I set out to do in the first place.