Longboard Ground Effect Lighting Case

Our Larval Engineer has been diligently procrastinating on her summer project to add ground effect lighting to her longboard. I’m hereby depriving her of the opportunity to learn enough OpenSCAD to build a case from scratch:

Longboard Ground Effect Lighting Case - exploded view

Longboard Ground Effect Lighting Case – exploded view

This is upside-down from its in-use position, but she’ll have it in this orientation on the bench. Four 10-32 screws clamp the whole affair together and hold it to a bottom aluminum plate with threads to suit; that plate also gets bolted between the longboard and the rear truck.

The general idea is that four 2 A·h lithium prismatic cells live in the bottom slice with their protection circuit, sandwiched between two aluminum plates that should protect them from all but catastrophic impact. The circuit board (which ought to be a PCB, but we’ll go with hand wiring for the first iteration) gets clamped in the recess between the two upper slices, above the upper aluminum plate. A polycarbonate sheet on top provides visibility for the Arduino blinky LED inside and shows off the circuitry to one and all.

I think a ridge on each long wall should suffice to hold the cells against the end wall; we don’t have the cells in hand to figure that out yet. She gets to add internal partitions, cable cutouts, and suchlike.

Oh. “Ground effect lighting” means ten RGB LED strips glued under the longboard deck. Her innovation is to make the LED color depend on the speed, which can range upward to scary-fast. It’s a simple matter of software, using a Hall effect sensor for input. This will look much better after dark, but she’s pretty much nocturnal anyway.

The OpenSCAD source code:

// Longboard Ground Effect Lighting Controller Case
// Ed Nisley KE4ZNU
// Karen Nisley KC2SYU
// July 2012

// Layout options

Layout = "Show";
 // Overall layout: Fit Show
 // Printing plates: Build1 .. Buildn (see bottom!)
 // Parts: BatteryLayer PCBLayer1 PCBLayer2
 // Shapes: CaseShell PCBEnvelope

ShowGap = 5; // spacing between parts in Show layout

//-----
// Extrusion parameters must match reality!

ThreadThick = 0.25;
ThreadWidth = 2.0 * ThreadThick;

HoleWindage = 0.2;

//-- Handy stuff

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

Protrusion = 0.1; // make holes end cleanly

inch = 25.4;

Tap10_32 = 0.159 * inch;
Clear10_32 = 0.190 * inch;
Head10_32 = 0.373 * inch;
Head10_32Thick = 0.110 * inch;
Nut10_32Dia = 0.433 * inch;
Nut10_32Thick = 0.130 * inch;
Washer10_32OD = 0.381 * inch;
Washer10_32ID = 0.204 * inch;

//----------------------
// Dimensions

CellWidth = 50.0; // Lithium-ion cell dimensions
CellLength = 60.0;
CellThick = 6.0;
CellClearance = 1.5; // on all sides
CellTabClearance = 10.0; // for connections

BatteryCount = 4; // cells in the battery
BatteryHeight = BatteryCount*CellThick + 2*CellClearance;

PCMWidth = 16.0; // Battery protection board
PCMLength = 50.0;
PCMThick = 4.0; // at terminal end of cells

PillarOD = Washer10_32OD + 2*0.5; // screw pillar diameter
PillarOffset = (PillarOD/2) / sqrt(2.0); // distance to case inside corner

WallThick = 6.0; // case wall thickness

PinOD = 1.4; // alignment pin size

CaseOALength = CellLength + CellClearance + CellTabClearance + PCMThick + 2*WallThick;
CaseInsideLength = CaseOALength - 2*WallThick;
echo("Box Length outside: ",CaseOALength);
echo(" inside: ",CaseInsideLength);

CaseOAWidth = CellWidth + 2*CellClearance + 2*WallThick;
CaseInsideWidth = CaseOAWidth - 2*WallThick;
echo("Box Width outside: ",CaseOAWidth);
echo(" inside: ",CaseInsideWidth);

CaseOAHeight = BatteryCount * CellThick + CellClearance;

PCBThick = 1.0; // PCB thickness
PCBMargin = 3.0; // clamping margin around PCB edge
PartHeight = 10.0; // height of components above PCB
WiringThick = 4.0; // wiring below PCB

echo("PCB thickness:",PCBThick);
echo(" clamp margin: ",PCBMargin);
echo(" wiring: ",WiringThick);
echo(" components: ",PartHeight);

PCBLayer1Thick = IntegerMultiple(WiringThick + PCBThick/2,ThreadThick);
PCBLayer2Thick = IntegerMultiple(PartHeight + PCBThick/2,ThreadThick);

echo("Battery compartment height: ",BatteryHeight);
echo("PCB Layer 1 height: ",PCBLayer1Thick);
echo("PCB Layer 2 height: ",PCBLayer2Thick);

PlateThick = 1/16 * inch; // aluminum mount / armor plates

echo("Total height: ",2*PlateThick + BatteryHeight + PCBLayer1Thick + PCBLayer2Thick);

//----------------------
// Useful routines

module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes

Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);

FixDia = Dia / cos(180/Sides);

cylinder(r=(FixDia + HoleWindage)/2,
 h=Height,
 $fn=Sides);
}

module ShowPegGrid(Space = 10.0,Size = 1.0) {

Range = floor(50 / Space);

for (x=[-Range:Range])
 for (y=[-Range:Range])
 translate([x*Space,y*Space,Size/2])
 %cube(Size,center=true);

}
//-------------------
// Shapes

module CaseShell(h=1.0) {

difference() {
 union() {
 translate([0,0,h/2])
 cube([CaseOALength,CaseOAWidth,h],center=true);

for (x=[-1,1])
 for (y=[-1,1])
 translate([x*(PillarOffset + CaseInsideLength/2),
 y*(PillarOffset + CaseInsideWidth/2),
 h/2])
 cylinder(r=PillarOD/2,h,center=true,$fn=4*6);
 }

for (x=[-1,1]) // screw holes on corners
 for (y=[-1,1])
 translate([x*(PillarOffset + CaseInsideLength/2),
 y*(PillarOffset + CaseInsideWidth/2),
 -Protrusion])
 PolyCyl(Clear10_32,(h + 2*Protrusion),8);

for (x=[-1,1]) // alignment pins in width walls
 translate([x*(CaseOALength - WallThick)/2,0,-Protrusion])
 rotate(45)
 PolyCyl(PinOD,(h + 2*Protrusion));
 for (y=[-1,1]) // alignment pins in length walls
 translate([0,y*(CaseOAWidth - WallThick)/2,-Protrusion])
 rotate(45)
 PolyCyl(PinOD,(h + 2*Protrusion));

}
}

module BatteryLayer() {

difference() {
 CaseShell(BatteryHeight);

translate([0,0,BatteryHeight/2])
 cube([CaseOALength - 2*WallThick,
 CaseOAWidth - 2*WallThick,
 BatteryHeight + 2*Protrusion],
 center=true);
 }
}

module PCBEnvelope() {

union() {
 translate([0,0,WiringThick + PCBThick + PartHeight/2])
 cube([CaseInsideLength - 2*PCBMargin,
 CaseInsideWidth - 2*PCBMargin,
 PartHeight + 2*Protrusion],
 center=true);

translate([0,0,WiringThick + PCBThick/2])
 cube([CaseInsideLength,CaseInsideWidth,PCBThick],center=true);

translate([0,0,WiringThick/2])
 cube([CaseInsideLength - 2*PCBMargin,
 CaseInsideWidth - 2*PCBMargin,
 WiringThick + 2*Protrusion],
 center=true);
 }
}

module PCBLayer1() {

difference() {
 CaseShell(PCBLayer1Thick);
 PCBEnvelope();
 }

}

module PCBLayer2() {

difference() {
 CaseShell(PCBLayer2Thick);
 translate([0,0,-(WiringThick + PCBThick/2)])
 PCBEnvelope();
 }

}

module Aluminum() {
 translate([0,0,PlateThick/2])
 cube([1.1*CaseOALength,1.1*CaseOAWidth,PlateThick - Protrusion],center=true);
}

//-------------------
// Build things...

ShowPegGrid();

if ("Battery" == Layout)
 Battery();

if ("CaseShell" == Layout)
 CaseShell(CaseOAHeight);

if ("BatteryLayer" == Layout)
 BatteryLayer();

if ("PCBEnvelope" == Layout)
 PCBEnvelope();

if ("PCBLayer1" == Layout)
 PCBLayer1();

if ("PCBLayer2" == Layout)
 PCBLayer2();

if ("Fit" == Layout) {
 BatteryLayer();
 translate([0,0,BatteryHeight + PlateThick])
 color("Green") PCBLayer1();
 translate([0,0,BatteryHeight + PlateThick + PCBLayer1Thick])
 color("Cyan") PCBLayer2();
}

if ("Show" == Layout) {
 BatteryLayer();
 translate([0,0,BatteryHeight + PlateThick + ShowGap])
 color("Green") PCBLayer1();
 translate([0,0,BatteryHeight + PlateThick + PCBLayer1Thick + 2*ShowGap])
 color("Cyan") PCBLayer2();
}

if ("Build1" == Layout)
 rotate(90) BatteryLayer();

if ("Build2" == Layout)
 rotate(90) PCBLayer1();

if ("Build3" == Layout)
 translate([0,0,PCBLayer2Thick])
 rotate([0,180,90])
 PCBLayer2();

  1. #1 by madbodger on 2012-08-21 - 08:10

    aluminum plate with threads to suit

    You’re anchoring screws in a high-vibration, high-impact environment with the scant few threads’ engagement you’ll get in an aluminum plate? Seems to me they’d tear out after a little while. However, I can’t think of a good way to put ordinary hex nuts
    on the ends, as that would require recesses in the longboard to accommodate them. Hmmm.

    • #2 by Ed on 2012-08-21 - 08:34

      Seems to me they’d tear out after a little while.

      We used 10-24 all-thread rods anchored with JB Weld, as I didn’t have any 2-1/2 inch 10-32 screws. She tapped a test coupon, clamped it in the vise, and gave the (un-glued) all-thread a mighty yanking, all to no avail. I don’t like it, but it will survive longer than anything else I can think of and it’s as good as it’ll get.

      The outer end of the all-thread gets nylon locking nuts over spring washers to keep the case stack in compression.

      that would require recesses in the longboard

      Which was, shall we say, vetoed early in the design process… [wince]

      A thicker plate moves the truck too far from the longboard; there are complex issues of balance and responsiveness and riding technique that I do not profess to understand.

      a high-vibration, high-impact environment

      I’m not entirely pleased with the overall design, but the battery’s as well-armored as I can make it within the other constraints.

      The big fear: a wire shaking loose from a cell or the battery protection board, shorting a cell outside the polyfuse and protection circuitry, ending with a catastrophic lithium cell failure inside an ABS plastic case. We’ll put a dab of flexy acrylic adhesive on those joints, pack that part of the case with foam to immobilize the wires, cross our fingers and eyes, and hope for the best.

      It is a way complex project and she’s learned a lot more than she expected… [grin]

  2. #3 by Bill Rutiser on 2012-08-21 - 12:20

    I just learned of this. http://joostn.github.com/OpenJsCad/
    Perhaps someone will find it useful.

    • #4 by Ed on 2012-08-21 - 21:06

      I vaguely recall a mention of that on the OpenSCAD mailing list a while back; I liked the notion of not having a bunch of local dependencies for the installation. So many languages, so little time … [grin]

  1. PL605060 Cells: First Charge « The Smell of Molten Projects in the Morning