Pages

Showing posts with label Video. Show all posts
Showing posts with label Video. Show all posts

Friday, 28 July 2017

Wi-Fi Enabled bed lamp - Project to teach the kids

In today's world where school is all theory with very little reality to pin it to in our mental model, we decided to try and give some kids as many different experiences as we could as early as possible. Once they get to the theories taught in the classroom they will hopefully have some mental model to connect it with.

This is one of those projects.

The Wi-Fi enabled Bedlamp


4 Kids aged between 7 and 13 and their parents each built their own Bed side lamp. We started off with the woodwork. We encouraged them to do as much of the work themselves, even with the larger machinery (With good and careful adult supervision).
Some kids and dads while doing the woodwork.
The Circuit we built was relatively simple and consisted mostly of just an ESP01 (which has a total of two GPIO's), a relay, a linear voltage regulator (for the 3.3V supply) and a couple of passives, all soldered to some veroboard.

Functional Diagram showing main components
The button was connected to allow for in place programming. With Tx and Rx connected, if you powered the circuit on while holding down the button, the ESP8266 would boot into firmware mode, otherwise it would start-up normally.
Circuit diagram
We printed the image below out and everyone placed their pack of components into the vera board and soldered the bits themselves.
Vera/Strip board component layout
Mom and daughter sorting and placing components on their boards before soldering them.
Mom helping her daughter. Neither have ever built electronics before.
Below are some images of a finished lamp with a perspex/plexiglass bottom to show off the work.
Perspex/Plexiglass bottom cover to see the hard work.

We made use of a 5V USB charger, which was stripped of it's case and had the 5V rail tapped off to power our circuit. This provided a nice opportunity to integrate the original USB charger into the side of the lamp, where your phone will probably be during the night anyway, ;-)
USB charger on the side.
When the lamp is initially turned on in a new network, the lamp comes up as a WiFi access point. Connecting to it opens a web interface allowing you to search, configure and connect to your local Wi-Fi network. Once that is done, your Wi-Fi router should provide the lamp with its own IP address (You can easily find the new IP address by looking on your router configuration page.
Mobile phone UI when connecting to the lamp via Wi-Fi
We wired the relay up to be off while GPIO2 (Also connected to the LED on the module) was on so that it would shine through the clear plastic round bar used to press the power button. This made the on switch glow blue while the light is off. This makes it nice and easy to find the light switch at night.
Light Switch glows when the light is off.
The video below is a really short view of the basic operation of the finished lamp.



As always, I would love to hear your thoughts in the comments below.

Bill of Material

  • LM1117-3,3 3.3V Liear Voltage Regulator
  • 10µF capacitor
  • 100µF Capacitor
  • Momentary push tactile button
  • Mains rated relay with 5V actuator
  • 4 x 3K3Ω Resistor
  • 1N4001 Diode
  • BCM337 NPN transistor
  • ESP-01s ESP8266 module
  • Cheap 5V 1AMP USB charger

Firmware Source Code

/*
 * Copyright 2017 Gineer RnD
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Relay control using the ESP8266 WiFi chip

// Import required libraries
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

//Room Name
const String RoomName = "Switch";

//Response from Client
String request = "";

// The port to listen for incoming TCP connections 
#define LISTEN_PORT           80

// set pin numbers:
const int buttonPin = 0;    // the number of the pushbutton pin
const int relayPin = 2;      // the number of the SWITCH pin

int relayState = LOW;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

// Create an instance of the server
WiFiServer server(LISTEN_PORT);
WiFiClient client;

void setup(void)
{  
   // Start Serial
   Serial.begin(115200);
   delay(10);
   Serial.println();
   Serial.println();
   Serial.println();
   Serial.println();

   //initialise WiFi Manager
   WiFiManager wifiManager;

   //first parameter is name of access point, second is the password
   wifiManager.autoConnect("GineerLamp", "secureme");

   pinMode(buttonPin, INPUT);
   pinMode(relayPin, OUTPUT);

   // set initial SWITCH state
   digitalWrite(relayPin, relayState);
  
   Serial.println("");
   Serial.println("WiFi connected");
  
   // Start the server
   server.begin();
   Serial.println("Server started");
  
   Serial.println("You can connect to this Switch at this URL:");
   Serial.print("http://");
   // Print the IP address
   Serial.print(WiFi.localIP());
   Serial.println("/");
  
}

void loop() {
  request = "";
  
  // Handle REST calls
  WiFiClient client = server.available();
  if (client) {
     Serial.println("User connected.");
     while(!client.available()){
          delay(1);
     }
     Serial.print("Request Received:");
     request = client.readStringUntil('\r\n');
     Serial.println(request);
     client.flush();
  }

    //process the request
    if (request.indexOf("/SWITCH=ON") != -1) {
       relayState = HIGH;
    }
    if (request.indexOf("/SWITCH=OFF") != -1) {
       relayState = LOW;
    }

   // read the state of the switch into a local variable:
   int reading = digitalRead(buttonPin);

   // If the switch changed, due to noise or pressing:
   if (reading != lastButtonState) {
     // reset the debouncing timer
     lastDebounceTime = millis();
   }

   if ((millis() - lastDebounceTime) > debounceDelay) {
     // whatever the reading is at, it's been there for longer
     // than the debounce delay, so take it as the actual current state:

     // if the button state has changed:
     if (reading != buttonState) {
       buttonState = reading;

       // only toggle the SWITCH if the new button state is HIGH
       if (buttonState == HIGH) {
         relayState = !relayState;
       }
     }
   }

   digitalWrite(relayPin, relayState);

   // save the reading.  Next time through the loop,
   // it'll be the lastButtonState:
   lastButtonState = reading;

  if (client) {
      client.println("HTTP/1.1 200 OK");
      client.println("Content-Type: text/html; charset=UTF-8");
      client.println("");
      client.print("<!DOCTYPE html><html><head><title>");
      client.print(RoomName);
      client.print(": Gineer.Home.SmartSwicth</title><style>body {background-color: black;color: white;text-align: center;}#switchSlider {display: inline-block;left: 28px;position: relative;border: 4px solid gray;width: 40px;height: 120px;vertical-align: central;}#switchToggle {display: inline-block;left: -30px;position: relative;border: 4px solid gray;width: 60px;height: 20px;vertical-align: central;}#switchSlider.off {background-color: silver;}#switchToggle.off {top: -20px;background-color: silver;}#switchSlider.on {background-color: yellow;}#switchToggle.on {top: -80px;background-color: yellow;}</style></head><body><h1>");
      client.print(RoomName);
      client.print("</h1><a href=\"/SWITCH=");
      if(relayState == HIGH)
      {
         client.print("OFF");
      }
      else
      {
         client.print("ON");
      }
      client.print("\" border=\"0\"><div class=\"");
      if(relayState == HIGH)
      {
         client.print("on");
      }
      else
      {
         client.print("Off");
      }
      client.print("\" id=\"switchSlider\"></div><div class=\"");
      if(relayState == HIGH)
      {
         client.print("on");
      }
      else
      {
         client.print("Off");
      }
      client.print("\" id=\"switchToggle\"></div></a><br /><br /><a href=\"/\">Refresh</a> <br /><br />Brought to you by <a href=\"http://www.gineer.co.za/\">Gineer R&D</a><p style=\"font-size: x-small;\">Rest: http://");
      client.print(WiFi.localIP());
      client.print("/SWITCH=ON or http://");
      client.print(WiFi.localIP());
      client.print("/SWITCH=OFF</p></body></html>");
      Serial.println("htmlsent");
   }
}

Friday, 22 July 2016

Lab supply progress - Rectifier board redesign

I've finally made some progress with the power supply. After re-spinning a board and twice setting my diodes on fire, I now have a working first stage.

In the video below I give you a quick run through of my work and also show the inrush current limiting board and the regulator stage.


As I've mentioned before, this power supply will also be modular, but not only in the sense that each stage will be a separate board, but even at the board level parts are optional. This allows for you to build a bare bones analogue PSU through to a fully connected lab grade power supply.

Leave comments below. :-)



Wednesday, 7 January 2015

Microsoft - Intel Galileo - Upgrade to Gen 2?

In this video I answer a Youtube comment on a previous video "Microsoft - Intel Galileo" from John Ryder and discuss the differences between the Intel Galileo Gen 1 and Gen 2 development boards and talk about some example projects.
 
Let me know what projects you guys decide on to help you learn.

Monday, 24 November 2014

3D Delta Printer Magnetic Ball Joints Upgrade

In this video I show how I upgrade the magnetic ball joints I implemented on my Delta 3D printer to make them more stable and reliable. I then also show how I printed a 90 degree bracket which I made available and shared on Thingiverse.


Thingiverse - http://www.thingiverse.com/thing:436118
3D Modeling for 3D Printing Playlist - http://www.youtube.com/playlist?list=PLSO20OpG15P8Li0RPiHRwwYHfhVIqSMmj

Saturday, 11 October 2014

Microsoft - Intel Galileo: Firmware Upgrade

In the video below I go through the process of upgrading the downloading the dedicated Arduino IDE for the Intel Galileo board and then upgrade the firmware.

 
 
I've also seen that there are a collection of new sample applications out to test at: https://ms-iot.github.io/content/SampleApps.htm
 
Windows on Fridges looks interesting.
 


Tuesday, 7 October 2014

CremaBot: Bug resolved and Rev2 PCB's ordered

I posted the video below yesterday after finally resolving a bug a found a couple of days before planning to complete the project (Isn't that always the case?)



This is actually the first time I have ever needed a second spin of the PCB I've designed. (Can't say that anymore).

I've just ordered and paid for the boards to be manufactured by OSHPark.com.

For some reason the silk screen does not display correctly on the site when ordering, but once the boards arrived last time in was flawless.

I was very impressed with the previous boards I received from them. This board is actually quite a bit smaller because I was also able to simplify the board and therefore get more density of components. The previous version was also double side loaded but this one is roughly evenly loaded on both side.

Sunday, 24 August 2014

3D Modelling for 3D Printing #3

In this video we finish off the OpenScad Model and start with creating the same model in SketchUp to show the comparison and some of the the required tips and trick.


The final object can be found on thingiverse.com


Below is the code which you should get to by the end of the video if you were following along.

OpenScad
/*
Created by David Taylor as part of a video series
Video are available at http://bit.ly/GineerTube
*/

include <libraries/MCAD/boxes.scad>

hw=40;
d=20;
t=5;
hr=3;
cr=2.5;

difference()
{
 union()
 {
  translate([(hw/2)-(t/2),0,0])
  {
   //cube([hw,d,t], center=true);
   roundedBox([hw,d,t],cr,true);
  }
  translate([0,0,(hw/2)-(t/2)])
  {
   rotate([0,90,0])
   {
    //cube([hw,d,t], center=true);
    roundedBox([hw,d,t],cr,true);
   }
  }
  translate([((hw/2)*sqrt(2))/2,0,((hw/2)*sqrt(2))/2])
  {
   rotate([0,45,0])
   {
    cube([hw,d,t], center=true);
   }
  }
  cube([t,d,t],center=true);
 }
 translate([(hw/4),0,0])
 {
  cylinder(h=(t*2), r=hr, $fn=100, center=true);
 }
 translate([(hw/4)*3,0,0])
 {
  cylinder(h=(t*2), r=hr, $fn=100, center=true);
 }
 rotate([0,-90,0])
 {
  translate([(hw/4),0,0])
  {
   cylinder(h=(t*2), r=hr, $fn=100, center=true);
  }
  translate([(hw/4)*3,0,0])
  {
   cylinder(h=(t*2), r=hr, $fn=100, center=true);
  }
 }
 translate([(hw/4),0,(hw/2)+(t/2)+0.1])
 {
  cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
 }
 translate([(hw/2)+(t/2)+0.1,0,(hw/4)])
 {
  rotate([0,90,0])
  {
   cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
  }
 }
 translate([(hw/4)*3,0,(hw/2)+(t/2)])
 {
  cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
 }
 translate([(hw/2)+(t/2),0,(hw/4)*3])
 {
  rotate([0,90,0])
  {
   cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
  }
 }
 translate([(hw/4)+(hw/2),0,(hw/4)+(hw/2)])
 {
  cube([hw,(hr*4),hw], center=true);
 }
 translate([(hw)-sqrt(pow(hw/8,2)-pow(t/2,2)),0,(t)])
 {
  rotate([0,20,0])
  {
   cube([(hw/4),d,t],center=true);
  }
 }
 translate([(t),0,(hw)-sqrt(pow(hw/8,2)-pow(t/2,2))])
 {
  rotate([0,-20,0])
  {
   cube([t,d,(hw/4)],center=true);
  }
 }
}

Thursday, 21 August 2014

3D Modeling for 3D Printing #2

In this video we continue working on the bracket we started in the first video and cut the screw holes by using difference and union as well as introducing libraries. This vide continues on from the first video in this series which you can find at: http://youtu.be/rUwoMPB4Lv8


Below is the code which you should get to by the end of the video if you were following along.

OpenScad
include <libraries/MCAD/boxes.scad>

hw=40;
d=20;
t=6;
hr=3;
cr=2.5;

difference()
{
 union()
 {
  translate([(hw/2)-(t/2),0,0])
  {
   //cube([hw,d,t], center=true);
   roundedBox([hw,d,t],cr,true);
  }
  translate([0,0,(hw/2)-(t/2)])
  {
   rotate([0,90,0])
   {
    //cube([hw,d,t], center=true);
    roundedBox([hw,d,t],cr,true);
   }
  }
  translate([((hw/2)*sqrt(2))/2,0,((hw/2)*sqrt(2))/2])
  {
   rotate([0,45,0])
   {
    cube([hw,d,t], center=true);
   }
  }
  cube([t,d,t],center=true);
 }
 translate([(hw/4),0,0])
 {
  cylinder(h=(t*2), r=hr, $fn=100, center=true);
 }
 translate([(hw/4)*3,0,0])
 {
  cylinder(h=(t*2), r=hr, $fn=100, center=true);
 }
 rotate([0,-90,0])
 {
  translate([(hw/4),0,0])
  {
   cylinder(h=(t*2), r=hr, $fn=100, center=true);
  }
  translate([(hw/4)*3,0,0])
  {
   cylinder(h=(t*2), r=hr, $fn=100, center=true);
  }
 }
 translate([(hw/4),0,(hw/2)+(t/2)+0.1])
 {
  cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
 }
 translate([(hw/2)+(t/2)+0.1,0,(hw/4)])
 {
  rotate([0,90,0])
  {
   cylinder(h=(hw), r=(hr*2), $fn=100, center=true);
  }
 }
}

3D Modeling for 3D Printing #1

In this video I will take you through a bit of my own learning on how to use OpenScad which I am planning to make into a series where I show the benefits of using both OpenScad and higher level modelling tools like SketchUp to model different aspects such as components and full systems.


Below is the finished code for this video for those that don't want to go through the trouble of typing it all in.

OpenScad
hw=40;
d=20;
t=6;
hr=3;

translate([(hw/2)-(t/2),0,0])
{
 cube([hw,d,t], center=true);
}
translate([0,0,(hw/2)-(t/2)])
{
 rotate([0,90,0])
 {
  cube([hw,d,t],center=true);
 }
}
translate([((hw/2)*sqrt(2))/2,0,((hw/2)*sqrt(2))/2])
{
 rotate([0,45,0])
 {
  cube([hw,d,t], center=true);
 }
}

Sunday, 17 August 2014

Microsoft - Intel Galileo IoT Dev Board Unboxing

In this video I unbox, setup and have a first look at the Intel Galileo development board Microsoft sent me. This board, updated to allow windows to boot, is unreleased at this stage.

 
More Info:


Thursday, 17 July 2014

CremaBot: Jura FX50 Display Hack - The Theory

posted 17 Jul 2014 11:46 by David Taylor

In this video I take you through the theory of how I plan to hack into the display of the Jura Impressa FX50 Classic automatic coffee machine to allow me to display what ever is being shown on the 1 line x 10 character dot matrix display, on the web app in real time.

Easy Buble free Kapton Tape

posted 17 Jul 2014 11:41 by David Taylor

In this video I show you an easy way to stick Kapton tape onto a print surface such as aluminium. In the video I also show off the first version of the Printer plate I made available on Thingiverse.com.

PCB Heatbed MK2a Plate (3 hole mounting) (4 hole mounting)

Friday, 4 July 2014

CremaBot: Schematic Update

posted 23 Jun 2014 12:49 by David Taylor   [ updated 25 Jun 2014 22:48 ]

While being in Cape Town on Business, I decided to make use of some free time and recorded a quick YouTube Video to provide an update on progress with CremaBot. Below is a Walkthrough of the Schematic:


Since I recorded the video above, I've actually completed the PCB layout and ordered it from OSHPark. I've never used them before, so I'll let you know what I think of the quality and speed of delivery all the way to South Africa.

More updates coming soon.

Video Update

posted 5 Jun 2014 23:50 by David Taylor



Delta 3D Printer Videos

posted 2 May 2014 13:15 by David Taylor

Here are two quick videos I created for a talk I presented on my printer.

This one is a quick overview animation from Sketchup. Note that in these videos it still shows the first version of my Magnetic Joints which I have recently improved.


This version show my Delta 3D Printer in action.

All systems go...

posted 13 Feb 2014 12:12 by David Taylor

I had 20, 3mm aluminium plates cut last week based on the two designs I created and uploaded to thingiverse.com. I had 2 plates cut for the 3 post mounted MK2a board, for my 3D Delta Printer, and 18 of the plates for the 4 post mounted boards. The 4 post plates were destined for friends at H4H, my local hackerspace, where most people have Morgan printers, requiring the 4 post mounting system.


After wasting 2 pieces of Koptan (always thought this was Capton tape, why does everyone pronounce it like that?) I figured I'd use a trick my sister, who works in advertising, where she often has to stick vinyl signs to glass and other stuff, taught me.

I dripped a couple of drops, really just a small amount, of dishwashing liquid onto the plate, then wet it with water and gently mixed it with my fingers until the plate was evenly wet and slippery from the soap. I lay the Koptan tape onto the wet surface, which allows for really easy bubble removal, using a credit card, until the water dries up, and the tape takes over.

Here is the finished product installed on the MK2a heat bed on my 3D Delta Printer:

With all the bits finally in place I figured it was time to get some filament moving through my extruder. I had set all the values in DEFAULT_AXIS_STEPS_PER_UNIT to the same value, 167, including the extruder. Turns out the extruder needs 954 steps to push the filament 1 mm, thanks to the slightly customised Reprap pro mini extruder I now have. (A review of this coming soon). Anyway, with it all set up and ready to go, I decided to see if I could get some molten plastic squirting out of my hot-end for the first time.


And so was boren: Worm 1 and Worm 2
@Gineer Worm1 and worm2?
— Quentin Harley (@QuentinHarley) February 12, 2014


I am having slight problems with the balls not being held tightly enough to the trucks, but theoretically, I should be ready to print my first actual object soon. Just need to recalibrate with the new plates installed.

Whish me luck ;-)

Saturday, 14 June 2014

TriRot – First flight Tests

posted 18 Oct 2011 02:26 by David Taylor

Over the last couple of weeks I’ve been trying to get TriRot airborne… and it’s been interesting.
My initial worry was that TriRot may be too heavy, so I trimmed off all the excess and brought the weight down from 553grams to 410grams. This included getting a smaller battery. I even turned the motors around to get rid of the prop adapters in favour of the built in prop savers. The vireo below shows why I now hate prop savers ;-)
TriRot - First Flight Test
I then spent the next week building arms that could hold my motors the other way around so that I could fit prop adapters and also support bigger props. And then I had success. TriRot lifted quite easily, just to crash about 2 seconds later… See the next Video.


TriRot - First Flight Test ... Again
Broken Props, and one arm
The problem now is that I to fix the brazing on the broken arm, I’ll have to take the motors completely out again as I will be heating them to about 600 degrees Celsius if I don’t. The plan at the moment is to fix it temporarily with cable ties or glue and work on getting TriRot to fly first. Once everything is stable, I’ll braze the mounting plate back onto the arm.
I think I know what caused the crash too. I used the z axis output from the IMU to auto correct the yaw using the servo angle. I did get some early comments saying that this was not a good idea, but I tried it anyway. Watching the video seemed to show that it was a Z-axis spin that prevented TriRot from correcting its hover, so I’ll update the code before the next test.
I’ve ordered new props last night, so hopefully they will arrive soon.
Ps. Thanks to everyone who have made various suggestions so far. Keep them coming. I need them, because as I mentioned right in the beginning, this is my first attempt at flying anything [remote controlled].

Pitch Mixing (Video)

posted 23 Sep 2011 01:55 by David Taylor

I recorded a quick video this morning showing me testing TriRot’s Pitch Mixing routines. I zip-tied a pivot beam to the chassis to limit movements to the Pitch axis only and also commented out the mixing code for Roll and Yaw.


In the video you can also see TriRot going through its full pre-flight self-check. Flashing the LED’s 5 times after the motors spin up for the first time means all is good and we are ready to go. I temporarily set channel 5 on my remote to be a kill switch. When this switch is toggled it either sends command to the motors and servo, or not. This is very handy for this part of the testing, where I’m not quite sure what TriRot will do as soon as it lifts off.
Below is a graph based on the data I captured during this test:
·         The top blue line is both the left and right motor speeds.
·         The Green line is the throttle
·         The orange line shows the rear motor speed.
·         The red line shows the input from the Received. It shows that I did not control TriRot from the Transmitter and that it is self-stabilising. (Notice that it is biased a bit forward in this graph and not on zero, because the pivot is a bit too far back so there is more weight in the front.)
·         The blue line in the bottom shows the Yaw input from the IMU.
I have done exactly the same testing to sort out the Roll axis. Tonight (possibly) I will be testing the Yaw axis. I think this is going to be a bit more complicated because I need to be able to override the IMU Yaw over time; otherwise TriRot will always try to turn back into the direction it started up in. But, when I’m not applying any Yaw input it should hold in that direction.
Watch this space.

IMU Test - Video

posted 19 Jul 2011 04:55 by David Taylor

So now that I have delved into the depths of 2’s compliment and also made sure I was using my incoming data in the correct order for the BitConverter, the Complementary filter from NuclearProjects.com, my IMU is working perfectly.

TriRot - IMU Test


In the video I am using the Altitude indicator that Jesse wrote to test it with. (He even made the full source code available on CodeProject).
He has even implemented some calculations that now allows TriRot to track the z-Axis as well for Yaw control. Although this implementation is only based on Gyro’s (ITG-3200) and Accelerometers (ADXL345) ,which could drift over time, it is holding rock solid in tests so far.
The C# code that makes the above possible:
  1: using System;
  2: using Microsoft.SPOT;
  3: using Tri_Rot_Version_1;
  4: using System.Threading;
  5: using GHIElectronics.NETMF.System;
  6:
  7: namespace IMUComplementaryFilter
  8: {
  9:     public class Program
 10:     {
 11:
 12:         private static Int32 gXZeroValue = 0;
 13:         private static Int32 gYZeroValue = 0;
 14:         private static Int32 gZZeroValue = 0;
 15:
 16:         private static Int32 aXZeroValue = 0;
 17:         private static Int32 aYZeroValue = 0;
 18:         private static Int32 aZZeroValue = 0;
 19:         private const Double Rad2Deg  = 57.2957795; // 1 radian = 57.2957795 degrees
 20:         private const Double Deg2Rad = 0.0174532925; // 0.0174532925 rads = 1 deg
 21:
 22:
 23:         public static void Main()
 24:         {
 25:             Debug.EnableGCMessages(false);
 26:             int i = 0;
 27:             Int16[] g;
 28:             Int16[] a;
 29:
 30:             using (Gyro = new ITG3200I2C())
 31:             {
 32:                 Gyro.StartUp();
 33:             }
 34:
 35:             using (Acc = new ADXL345I2C())
 36:             {
 37:                 Acc.StartUp();
 38:             }
 39:             Thread.Sleep(100);
 40:
 41:             Int16 gXTemp = 0;
 42:             Int16 gYTemp = 0;
 43:             Int16 gZTemp = 0;
 44:
 45:             Int16 aXTemp = 0;
 46:             Int16 aYTemp = 0;
 47:             Int16 aZTemp = 0;
 48:
 49:             while (i < 100)
 50:             {
 51:                 g = getGyroData();
 52:                 a = getAccData();
 53:                 Thread.Sleep(20);
 54:                 i++;
 55:             }
 56:             i = 0;
 57:             while(i < 100)
 58:             {
 59:                 g = getGyroData();
 60:                 a = getAccData();
 71:
 72:                 gXTemp += g[0];
 73:                 gYTemp += g[1];
 74:                 gZTemp += g[2];
 75:
 76:                 aXTemp += a[0];
 77:                 aYTemp += a[1];
 78:                 aZTemp += a[2];
 79:
 80:                 Thread.Sleep(20);
 81:                 i++;
 82:             }
 83:             gXZeroValue = (Int16)(gXTemp / 100F);
 84:             gYZeroValue = (Int16)(gYTemp / 100F);
 85:             gZZeroValue = (Int16)(gZTemp / 100F);
 86:                               
 87:             aXZeroValue = (Int16)(aXTemp / 100F);
 88:             aYZeroValue = (Int16)(aYTemp / 100F);
 89:             aZZeroValue = (Int16)(aZTemp / 100F);
 90:
 91:             Double GyroRateX = 0.0;
 92:             Double GyroRateY = 0.0;
 93:             Double GyroAngleX = 0.0;
 94:             Double GyroAngleY = 0.0;
 95:             Double GyroAngleZ = 0.0;
 96:
 97:             Double AccelX = 0.0;
 98:             Double AccelY = 0.0;
 99:             Double AccelZ = 0.0;
100:             Double AccelAngleX = 0.0;
101:             Double AccelAngleY = 0.0;
102:             Double AccelAngleZ = 0.0;
103:             Double Roll = 0.0;
104:             Double Pitch = 0.0;
105:             Double Yaw = 0.0;
106:
107:             Int16 minValx = -4095;
108:             Int16 maxValx = 4095;
109:             Int16 minValy = -4095;
110:             Int16 maxValy = 4095;
111:             Int16 minValz = -4095;
112:             Int16 maxValz = 4095;
113:
114:             long currentTime;
115:             Single interval;
116:             long lastTime = DateTime.Now.Ticks;
117:
118:             long TicksPerMillisecond = TimeSpan.TicksPerMillisecond;
119:             Single GyroAngleZ_dt = 0;
120:
121:             Telemetry telemetry = new Telemetry();
122:
123:
124:             while (true)
125:             {
126:                 currentTime = DateTime.Now.Ticks;
127:                 interval = ((currentTime - lastTime) / TicksPerMillisecond)/1000F; //in milliseconds
128:                 lastTime = currentTime;
129:
130:                 g = getGyroData();
131:                 a = getAccData();
132:
133:                 GyroRateX = -1.0 * interval * (g[0] - gXZeroValue) / 14.375F;
134:                 GyroRateY = interval * (g[1] - gYZeroValue) / 14.375F;
135:
136:                 GyroAngleZ_dt = interval * (g[2] - gZZeroValue) / 14.375F;
137:
138:                 GyroAngleZ += -1.0 * GyroAngleZ_dt * (1 / (MathEx.Cos(Deg2Rad * Roll))); // convert Roll angle to Rads, find sin to use as scaler for Yaw
139:
140:                 if (GyroAngleZ < 0) GyroAngleZ += 360;    // Keep within range of 0- 360 deg
141:                 if (GyroAngleZ >= 360) GyroAngleZ -= 360;
142:                 //----------------------------------------------------------------
143:
144:                 //convert read values to degrees -90 to 90 - Needed for atan2
145:                 Single xAng = map(a[0], minValx, maxValx, -90, 90);
146:                 Single yAng = map(a[1], minValy, maxValy, -90, 90);
147:                 Single zAng = map(a[2], minValz, maxValz, -90, 90);
148:
149:                 //Caculate 360deg values like so: atan2(-yAng, -zAng)
150:                 //atan2 outputs the value of -π to π (radians)
151:                 //We are then converting the radians to degrees
152:                 AccelAngleX = Rad2Deg * (MathEx.Atan2(-xAng, -zAng) + MathEx.PI);
153:                 AccelAngleY = Rad2Deg * (MathEx.Atan2(-yAng, -zAng) + MathEx.PI);
154:
155:                 // Keep angles between +-180deg
156:                 if (AccelAngleX > 180) AccelAngleX = AccelAngleX - 360;
157:
158:                 if (AccelAngleY <= 180) AccelAngleY = -1.0 * AccelAngleY;
159:                 if (AccelAngleY > 180) AccelAngleY = 360 - AccelAngleY;
160:
161:                 // Final values...
162:                 Roll = (0.98) * (Roll + GyroRateX) + (0.02) * (AccelAngleX);
163:                 Pitch = (0.98) * (Pitch + GyroRateY) + (0.02) * (AccelAngleY);
164:                 Yaw = GyroAngleZ;
165:
166:                 //Debug.Print(Roll.ToString() + "," + Pitch.ToString() + "," + Yaw.ToString());
167:                 telemetry.SendData(new TelemetryData(0, 0, 0, 0, (Single)Roll, (Single)Pitch, (Single)Yaw));
168:                 Thread.Sleep(50);
169:             }
170:         }
171:
172:         private static Single map(Single x, Single in_min, Single in_max, Single out_min, Single out_max)
173:         {
174:             return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
175:         }
176:         
177:         private static ITG3200I2C Gyro;
178:         private static ADXL345I2C Acc;
179:
180:         private static Int16[] getGyroData()
181:         {
182:             Int16[] data;
183:             using (Gyro = new ITG3200I2C())
184:             {
185:                 data = Gyro.AdcR();
186:             }
187:             return data;
188:         }
189:
190:         private static Int16[] getAccData()
191:         {
192:             Int16[] data;
193:             using (Acc = new ADXL345I2C())
194:             {
195:                 data = Acc.AdcR();
196:             }
197:             return data;
198:         }
199:
200:     }
201: }

I have now also finally updated my PartsList after noticing that I had never added the Gyro or the Accelerometer to the it.
Next steps would be to buy the Transmitter and Receiver pair and a PPM Encoder for interfacing into TriRot's Interface board.