Improved Duet3d Fusion 360 post processor for Ooznest Ox

I recently upgraded my ooznest OX cnc machine from the original xPro USB controller (GRBL based) to the Duet3d (marlin based?)

Ooznest typically use this on their newer workbee machines and they provide a post processor in the downloads for that machine. However, they also ship all of their machines for use with trim routers which you manually switch on/off and don’t have any RPM control. Whereas I run my OX with a chinese 2.2KW water cooled spindle run from a VFD.

So I took the liberty to create a new version of the post processor that outputs spindle start, RPM, and spindle stop commands in the relevant places. I also have my processor drive the machine home at the end of every file. Note the spindle doesn’t shutdown until after the homing operation. I thought about shutting it down first. But honestly in the past I’ve accidentally homed it through things like work holding clamps and at least with the spindle on it just cuts through them ;-)

Obviously that will only work if you have also configured your duet to sent spindle rpm commands to your vfd, you can see how I set up mine in the wiring video here:

You milage may vary - and I make no guarantees as to suitability etc. basically don’t blame me if you use it and something weird happens. However I’m using it and figured I’d share my version

enjoy!


To use the below code - safe it into a file with a .cps extension (mine is called “Ox - Duet.cps” and save it into: C:\Users<youruser>\AppData\Roaming\Autodesk\Fusion 360 CAM\Posts

Then when you go to do post processing in fusion, select setup and choose “use personal post library”

you should then be able to select it from the drop down.



/**
  Copyright (C) 2012-2017 by Autodesk, Inc.
  All rights reserved.

  WorkBee - Duet post processor configuration.

  $Revision: 42115 bdeb2e221ae970b5318768fc88f8111865513bf5 $
  $Date: 2018-09-06 14:16:13 $
  
  FORKID 
*/

description = "Ox - Duet";
vendor = "Ooznest";
vendorUrl = "https://ooznest.co.uk/";
legal = "Copyright (C) 2012-2017 by Autodesk, Inc.";
certificationLevel = 2;
minimumRevision = 24000;

longDescription = "Generic milling post for Ox CNC Machine with Duet3d wifi.";

extension = "nc";
setCodePage("ascii");

capabilities = CAPABILITY_MILLING;
tolerance = spatial(0.002, MM);

minimumChordLength = spatial(0.25, MM);
minimumCircularRadius = spatial(0.01, MM);
maximumCircularRadius = spatial(1000, MM);
minimumCircularSweep = toRad(0.01);
maximumCircularSweep = toRad(180);
allowHelicalMoves = true;
allowedCircularPlanes = undefined; // allow all circular planes


// user-defined properties
properties = {
  writeMachine: true, // write machine
  writeTools: true, // writes the tools
  useG28: true, // disable to avoid G28 output for safe machine retracts - when disabled you must manually ensure safe retracts
  showSequenceNumbers: false, // show sequence numbers
  sequenceNumberStart: 10, // first sequence number
  sequenceNumberIncrement: 1, // increment for sequence numbers
  separateWordsWithSpace: true // specifies that the words should be separated with a white space
};

// user-defined property definitions
propertyDefinitions = {
  writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"},
  writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"},
  useG28: {title:"G28 Safe retracts", description:"Disable to avoid G28 output for safe machine retracts. When disabled, you must manually ensure safe retracts.", type:"boolean"},
  showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"},
  sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"},
  sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"},
  separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}
};

var gFormat = createFormat({prefix:"G", decimals:0});
var mFormat = createFormat({prefix:"M", decimals:0});

var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4), trim:false});
var feedFormat = createFormat({decimals:(unit == MM ? 1 : 2)});
var toolFormat = createFormat({decimals:0});
var rpmFormat = createFormat({decimals:0});
var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-1000
var taperFormat = createFormat({decimals:1, scale:DEG});

var xOutput = createVariable({prefix:"X", force:true}, xyzFormat);
var yOutput = createVariable({prefix:"Y", force:true}, xyzFormat);
var zOutput = createVariable({prefix:"Z", force:true}, xyzFormat);
var feedOutput = createVariable({prefix:"F", force:true}, feedFormat);

var sOutput = createVariable({prefix:"S", force:true}, rpmFormat);

// circular output
var iOutput = createReferenceVariable({prefix:"I", force:true}, xyzFormat);
var jOutput = createReferenceVariable({prefix:"J", force:true}, xyzFormat);
var kOutput = createReferenceVariable({prefix:"K", force:true}, xyzFormat);

var gMotionModal = createModal({force:true}, gFormat); // modal group 1 // G0-G3, ...
var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19
var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91
var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94
var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21

// collected state
var sequenceNumber;
var currentWorkOffset;

/**
  Writes the specified block.
*/
function writeBlock() {
  if (properties.showSequenceNumbers) {
    writeWords2("N" + sequenceNumber, arguments);
    sequenceNumber += properties.sequenceNumberIncrement;
  } else {
    writeWords(arguments);
  }
}

function formatComment(text) {
  return "(" + String(text).replace(/[()]/g, "") + ")";
}

/**
  Output a comment.
*/
function writeComment(text) {
  writeln(formatComment(text));
}

function onOpen() {
  if (!properties.separateWordsWithSpace) {
    setWordSeparator("");
  }

  sequenceNumber = properties.sequenceNumberStart;

  if (programName) {
    writeComment(programName);
  }
  if (programComment) {
    writeComment(programComment);
  }

  // dump machine configuration
  var vendor = machineConfiguration.getVendor();
  var model = machineConfiguration.getModel();
  var description = machineConfiguration.getDescription();

  if (properties.writeMachine && (vendor || model || description)) {
    writeComment(localize("Machine"));
    if (vendor) {
      writeComment("  " + localize("vendor") + ": " + vendor);
    }
    if (model) {
      writeComment("  " + localize("model") + ": " + model);
    }
    if (description) {
      writeComment("  " + localize("description") + ": "  + description);
    }
  }

  switch (unit) {
  case IN:
    error(localize("Please select millimeters as unit when post processing. Inch mode is not recommended by the BoXZY team."));
    return;
    // writeBlock(gUnitModal.format(20));
    // break;
  case MM:
    writeBlock(gUnitModal.format(21));
    break;
  }
  // dump tool information
  if (properties.writeTools) {
    var zRanges = {};
    if (is3D()) {
      var numberOfSections = getNumberOfSections();
      for (var i = 0; i < numberOfSections; ++i) {
        var section = getSection(i);
        var zRange = section.getGlobalZRange();
        var tool = section.getTool();
        if (zRanges[tool.number]) {
          zRanges[tool.number].expandToRange(zRange);
        } else {
          zRanges[tool.number] = zRange;
        }
      }
    }

    var tools = getToolTable();
    if (tools.getNumberOfTools() > 0) {
      for (var i = 0; i < tools.getNumberOfTools(); ++i) {
        var tool = tools.getTool(i);
        var comment = "T" + toolFormat.format(tool.number) + "  " +
          "D=" + xyzFormat.format(tool.diameter) + " " +
          localize("CR") + "=" + xyzFormat.format(tool.cornerRadius);
        if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) {
          comment += " " + localize("TAPER") + "=" + taperFormat.format(tool.taperAngle) + localize("deg");
        }
        if (zRanges[tool.number]) {
          comment += " - " + localize("ZMIN") + "=" + xyzFormat.format(zRanges[tool.number].getMinimum());
        }
        comment += " - " + getToolTypeName(tool.type);
        writeComment(comment);
      }
    }
  }

  // absolute coordinates
  writeBlock(gAbsIncModal.format(90));

  forceXYZ();
}

function onComment(message) {
  writeComment(message);
}

/** Force output of X, Y, and Z. */
function forceXYZ() {
  xOutput.reset();
  yOutput.reset();
  zOutput.reset();
}

/** Force output of X, Y, Z, and F on next output. */
function forceAny() {
  forceXYZ();
  feedOutput.reset();
}

var deviceOn = false;

function setDeviceMode(enable) {
  if (enable != deviceOn) {
    deviceOn = enable;
    if (enable) {
      switch (tool.type) {

      default:
        writeComment("TURN ON CUTTING");
        writeBlock("S1000"); // DEVICE ON
        // writeBlock(mFormat.format(15)); // CUT ON
      }
    } else {
      switch (tool.type) {

      default:
        writeComment("TURN OFF CUTTING");
        writeBlock("S0"); // DEVICE OFF - could add delay here
        // writeBlock(mFormat.format(16)); // CUT OFF
      }
    }
  }
}


function onSection() {
  var insertToolCall = isFirstSection() ||
    currentSection.getForceToolChange && currentSection.getForceToolChange() ||
    (tool.number != getPreviousSection().getTool().number);
  var retracted = false; // specifies that the tool has been retracted to the safe plane

  writeln("");
  
  if (hasParameter("operation-comment")) {
    var comment = getParameter("operation-comment");
    if (comment) {
      writeComment(comment);
    }
  }

  
  // spindle speed
  if (insertToolCall ||
      isFirstSection() ||
      (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) ||
      (tool.clockwise != getPreviousSection().getTool().clockwise)) {
    if (spindleSpeed < 1) {
      error(localize("Spindle speed out of range."));
    }
    if (spindleSpeed > 99999) {
      warning(localize("Spindle speed exceeds maximum value."));
    }
    //switch on spindle via I/O ping
    writeBlock(mFormat.format(42), "P1 S1");
    writeBlock(
      //set spindle rpm
       mFormat.format(tool.clockwise ? 3 : 4), sOutput.format(spindleSpeed)
       
    );
    // dwell for 4 seconds for spindle ramp up
    writeBlock(gFormat.format(4),"P10000");
  }
  
  forceXYZ();

  { // pure 3D
    var remaining = currentSection.workPlane;
    if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) {
      error(localize("Tool orientation is not supported."));
      return;
    }
    setRotation(remaining);
  }

  // coolant not supported

  forceAny();

  var initialPosition = getFramePosition(currentSection.getInitialPosition());
  if (!retracted) {
    if (getCurrentPosition().z < initialPosition.z) {
      writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z));
    }
  }

  if (retracted || isFirstSection() || true) { // we pull up Z above if we start lower
    gMotionModal.reset();

    writeBlock(
      gAbsIncModal.format(90),
      gMotionModal.format(0),
      xOutput.format(initialPosition.x),
      yOutput.format(initialPosition.y),
      zOutput.format(initialPosition.z)
    );
  } else {
    writeBlock(
      gAbsIncModal.format(90),
      gMotionModal.format(0),
      xOutput.format(initialPosition.x),
      yOutput.format(initialPosition.y)
    );
  }
}

function onDwell(seconds) {
  if (seconds > 99999.999) {
    warning(localize("Dwelling time is out of range."));
  }
  seconds = clamp(0.001, seconds, 99999.999);
  writeBlock(gFormat.format(4), "P" + secFormat.format(seconds));
}

function onSpindleSpeed(spindleSpeed) {
  writeBlock(mFormat.format(tool.clockwise ? 3 : 4),sOutput.format(spindleSpeed));
}


var pendingRadiusCompensation = -1;

function onRadiusCompensation() {
  pendingRadiusCompensation = radiusCompensation;
}

function onRapid(_x, _y, _z) {
  var x = xOutput.format(_x);
  var y = yOutput.format(_y);
  var z = zOutput.format(_z);
  if (x || y || z) {
    if (pendingRadiusCompensation >= 0) {
      error(localize("Radius compensation mode cannot be changed at rapid traversal."));
      return;
    }
    writeBlock(gMotionModal.format(0), x, y, z);
    feedOutput.reset();
  }
}

function onLinear(_x, _y, _z, feed) {
  // at least one axis is required
  if (pendingRadiusCompensation >= 0) {
    // ensure that we end at desired position when compensation is turned off
    xOutput.reset();
    yOutput.reset();
  }
  var x = xOutput.format(_x);
  var y = yOutput.format(_y);
  var z = zOutput.format(_z);
  var f = feedOutput.format(feed);
  if (x || y || z) {
    if (pendingRadiusCompensation >= 0) {
      error(localize("Radius compensation mode is not supported."));
      return;
    } else {
      writeBlock(gMotionModal.format(1), x, y, z, f);
    }
  } else if (f) {
    if (getNextRecord().isMotion()) { // try not to output feed without motion
      feedOutput.reset(); // force feed on next line
    } else {
      writeBlock(gMotionModal.format(1), f);
    }
  }
}

function onRapid5D(_x, _y, _z, _a, _b, _c) {
  error(localize("Multi-axis motion is not supported."));
}

function onLinear5D(_x, _y, _z, _a, _b, _c, feed) {
  error(localize("Multi-axis motion is not supported."));
}

function onCircular(clockwise, cx, cy, cz, x, y, z, feed) {
  // one of X/Y and I/J are required and likewise
  
  if (pendingRadiusCompensation >= 0) {
    error(localize("Radius compensation cannot be activated/deactivated for a circular move."));
    return;
  }

  var start = getCurrentPosition();

  if (isFullCircle()) {
    if (isHelical()) {
      linearize(tolerance);
      return;
    }
    switch (getCircularPlane()) {
    case PLANE_XY:
      writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), feedOutput.format(feed));
      break;
    /*
    case PLANE_ZX:
      writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
      break;
    case PLANE_YZ:
      writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), yOutput.format(y), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
      break;
    */
    default:
      linearize(tolerance);
    }
  } else {
    switch (getCircularPlane()) {
    case PLANE_XY:
      writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), feedOutput.format(feed));
      break;
    /*
    case PLANE_ZX:
      writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
      break;
    case PLANE_YZ:
      writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
      break;
    */
    default:
      linearize(tolerance);
    }
  }
}

var mapCommand = {
  COMMAND_STOP:0,
  COMMAND_END:2,
  COMMAND_SPINDLE_CLOCKWISE:3,
  COMMAND_SPINDLE_COUNTERCLOCKWISE:4,
  COMMAND_STOP_SPINDLE:5
};

function onCommand(command) {
  switch (command) {
  case COMMAND_START_SPINDLE:
    onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE);
    return;
  case COMMAND_LOCK_MULTI_AXIS:
    return;
  case COMMAND_UNLOCK_MULTI_AXIS:
    return;
  case COMMAND_BREAK_CONTROL:
    return;
  case COMMAND_TOOL_MEASURE:
    return;
  }

  var stringId = getCommandStringId(command);
  var mcode = mapCommand[stringId];
  if (mcode != undefined) {
    writeBlock(mFormat.format(mcode));
  } else {
    onUnsupportedCommand(command);
  }
}

/** Output block to do safe retract and/or move to home position. */
function writeRetract() {
  // initialize routine
  var _xyzMoved = new Array(false, false, false);
  var _useG28 = properties.useG28; // can be either true or false

  // check syntax of call
  if (arguments.length == 0) {
    error(localize("No axis specified for writeRetract()."));
    return;
  }
  for (var i = 0; i < arguments.length; ++i) {
    if ((arguments[i] < 0) || (arguments[i] > 2)) {
      error(localize("Bad axis specified for writeRetract()."));
      return;
    }
    if (_xyzMoved[arguments[i]]) {
      error(localize("Cannot retract the same axis twice in one line"));
      return;
    }
    _xyzMoved[arguments[i]] = true;
  }
  
  // special conditions
  // none

  // define home positions
  var _xHome;
  var _yHome;
  var _zHome;
  if (_useG28) {
    _xHome = 0;
    _yHome = 0;
    _zHome = 0;
  } else {
    _xHome = machineConfiguration.hasHomePositionX() ? machineConfiguration.getHomePositionX() : 0;
    _yHome = machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : 0;
    _zHome = machineConfiguration.getRetractPlane();
  }

  // format home positions
  var words = []; // store all retracted axes in an array
  for (var i = 0; i < arguments.length; ++i) {
    // define the axes to move
    switch (arguments[i]) {
    case X:
      if (machineConfiguration.hasHomePositionX() || _useG28) {
        words.push("X" + xyzFormat.format(_xHome));
      }
      break;
    case Y:
      if (machineConfiguration.hasHomePositionX() || _useG28) {
        words.push("Y" + xyzFormat.format(_yHome));
      }
      break;
    case Z:
      if (_useG28) {
        words.push("Z" + xyzFormat.format(_zHome));
        retracted = true;
      }
      break;
    }
  }

  // output move to home
  if (words.length > 0) {
    if (_useG28) {
      gAbsIncModal.reset();
      writeBlock(gFormat.format(28), gAbsIncModal.format(91), words);
      writeBlock(gAbsIncModal.format(90));
    } else {
      gMotionModal.reset();
      writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), words);
    }

    // force any axes that move to home on next block
    if (_xyzMoved[0]) {
      xOutput.reset();
    }
    if (_xyzMoved[1]) {
      yOutput.reset();
    }
    if (_xyzMoved[2]) {
      zOutput.reset();
    }
  }
}

function onSectionEnd() {
  forceAny();
}

function onClose() {
  
  writeRetract(Z);

  writeRetract(X, Y);

  onImpliedCommand(COMMAND_END);
  onImpliedCommand(COMMAND_STOP_SPINDLE);
  writeBlock(mFormat.format(tool.clockwise ? 3 : 4),sOutput.format(0));
  writeBlock(mFormat.format(5)); // stop program, spindle stop, coolant off
  writeBlock(mFormat.format(42), "P1 S0");
  
}

cnc router, project, Casting

Sagrada Familia Knight

IMG_2755.JPG

I recently went to Barcelona and visited the Sagrada Familia there. It's an awesome building and if you have the chance I recommend a visit. The pictures really do not do it justice.

There are hundreds of statues, but one of the cool ones that caught my eye was this one of a knight

IMG_2139.JPG

Whilst the Sagrada Familia is a Antoni Gaudi design, much of the construction has occured since his death based on his ideas and designs. The Passion Facade was actually designed by Josep Maria Subirachs who was working from what he thought Gaudi's intention had been.

His original sketch for this knight is sold in various forms in the gift shop, I bought a t-shirt with it on. The sketch looks like this:

horse-guard.jpg

So I decided to make my own min version of the statue, by starting from the sketch and sticking with a fairly cubist design.

I used Fusion360 to first draw out the lines and arcs from the sketch that I wanted, then connecting them to create discrete panels that could be extruded to different heights and angles to create my own model

This STL is available in our  shop

This STL is available in our shop

Once I had the model, I created a few CAM paths to cnc it out from some high density model board. Which takes detail really well and it quite easy to machine. I wound up doing a face operation to flatten the stock, then a 3d adaptive clear with 1mm clearance from the model to remove the bulk of the material. Then a parallel pass with 0.3mm stepover using a 3mm ballnose bit. And finally with a 1.5mm flat bit I did another parallel operation but with rest machinging switched on, so it only cut in places that the previous 3mm bit could not reach. This helped pull out some of the finer details and sharp internal corners.

IMG_2657.JPG

The above is what it looked like after the machining. There was still quite a bit to do with the file to clean up the edges, I sanded all the faces to try to remove any tooling marks, and filled all the details in.

Once I was happy with this master, I cast it in silicone. Basically I stuck it down to a sheet of mdf, then built some little walls around it to form a shape, then poured on the silicone rubber and left it to set.

Once that had cured I was left with this chunky mold

IMG_20171029_092040.jpg

Now that I have a mold I can easily produce many copies of the model. My first attempt I used a concrete mix. However that did not work well. The agregate in the mix was too large, I didn't really mix it right, the whole thing just was note well suited to something at this scale.

Instead I bought some Jesmonite. This stuff is pretty amazing. it comes as a powder and fluid, it mixes up fairly fluid and pours in the mold quite nicely. It sets up in about 30-40 minutes, but continues to harden over a few days I believe. By default it comes out quite white and stone like which is really nice. It can also be coloured with pigments.

Again I needed to file some to clean up anywhere it spilled over the mold, but generally the castings came out great. The only issue I had was with the sword/lance type thing the Knight has, it's quite a thin section and it was very prone to snapping whilst I tried to de-mold.

To resolve this I found some heavy gauge copper wire from some house electrical wire (the kind that is actually quite stiff used to wire up plug sockets). a length of this stipped of it's inuslation dropped into the length of the sword and overlapping into the head of the horse seemed to work perfectly to provide a little extra support here, and I've not broken a single one that I did this way.

Lastly I made up a quick oak mount to put the model on. My idea was that this would work well as a book-end on a shelf in my study. So I made up a simple bracket for that, and shaped a piece of oak to form a 'terrain' beneath the horses hooves. Which you can see at the top.

If you'd like to make your own knight model, the STL is available for sale here

As always I also made a video of my build

 

I actually made two videos, the first one I made was a lot longer and I spent more time talking about the steps, that longer version is here:

cnc router, Design, project

Custom pi Zero case

Now available from our shop! custom pi Zero cases.

Yes you can buy off the shelf cases for your pizero. and that's fine. But if you're building a project and looking for a professional finish, it's great to have a case customised to your project or business.

The case is a snug fit for the latest version of the pi Zero and includes access to the sd card slot

Pi Zero sitting snugly in a custom cast case.

Pi Zero sitting snugly in a custom cast case.

Made from high grade J-Cast 70D hardness plastic, these cases can be drilled, and etched to custom requirements. 

Since we're making them to order they can be made in just about any colour you require.

Part of the customisation is to etch your logo or project name right into the case.

The cases are sealed with 4 torx screws from the bottom leaving the top surface clear for your sticker or design.

the maker geek branded pi0 case.

the maker geek branded pi0 case.

 

Need something more custom? or for a different kind of board? feel free to contact us with your particular requirements and we will see what we can do to help.

utility

Watch stand - evolving desings

Some time ago I decided to make myself a watch stand. I don't have a bed side clock, and since I take my watch off to sleep it seemed it could do double duty and be the 'clock' on my bedside table.

I made this:

I was pretty happy with it at the time, and I've used it for a couple of years. However over time the failings in this design has become more and more annoying.

The first issue is that the round that the watch is wrapped around is not quite big enough. I miscalculated at the time and so the watch hangs quite loosely on it.

The second, and much more serious issues, is that when the watch is in place the whole thing is front heavy. It is very prone to tipping over. Particularly when I'm putting the watch in place. I must have had this fall over and off my bedside dozens and dozens of times.

So I finally decided to make a new one. Attempting to fix the problems of the first, and also to come up with a different design. I could have just made the same thing again, but with better balance and a larger center. However I thought it would be more interesting to design something actually different.

That thinking actually lead me to make two new watch stands, trying out two different designs and two different materials. One in wood and one in metal.

These are the designs I came up with

The wooden one is a fairly traditional and simple design. A central block that the watch closes tightly around with a peg through it that rests in a cradle. This is nice because I can lift the central block free of the cradle to add or remove my watch. Then set it back down. The weight is central and the whole thing is nice and stable. It also holds the watch face right where I want it.

The metal one by contrast is adjustable, the central 'hub' has two nuts welded into it, and the front and rear plates screw in and out to adjust to the size of the watch. This design is in some ways more complicated, but in others more basic. The structure of the stand is essentially the watch itself. The metal 'skeleton' simply adds rigidity that holds the watch in place.

A few weeks on and I haven't really decided which I prefer. I mostly alternate between them. I think the wooden option has a nicer tactile feel. But there is something about the bare bones metal one which I enjoy the look of.

Below I have videos of me making each one. Which do you prefer?

 

 

welding, project, household

Custom fire guard

upload.jpg

Last year we installed a log burning stove in our living room. It's awesome and I'm actually looking forward to the cold weather as a reason to use it again.

upload.jpg

 

This year we will be playing host to my family including my nieces and nephew. So the question of how we ensure that an exuberant child doesn't accidentally lay a hand on the hot outer surface.

The answer of course is a fireguard and that sounds like an excuse to fire up the welder!

Design

For design I looked to our lights for inspiration. They set a tone for the room and I wanted to echo the pattern in the fire guard.

upload.jpg

materials

9 meters of mild steel flat bar. 25mmx3mm

21 meters of 6mm mild steel round bar

 

Tools

Mig welder (mine is a Clarke 150en)

Metal cutting chop saw (mine is the evolution rage)

Or just use a hack saw.

 For this project I actually created an instructable which you can find here

http://www.instructables.com/id/Art-Deco-Fireguard-for-Log-Burner/ 

 

I also made a video of my build process