Cracking the cube

I can’t take any credit for this.

Earlier this summer, I built a Rubik’s cube solver from a Lego Mindstorms set. I simply followed the plans for the Mindcuber design. It seemed to work quite well, but my Rubik’s cube had one face which was much stiffer to rotate than others, and the power available from the Lego motors was insufficient to reliably turn it. So it’s been sitting in my office for a while doing nothing. Today, however, Rob lent me a speed cube he bought from Amazon. It’s designed to take as little torque as possible to rotate, and has nicely chamfered edges so it doesn’t jam if it’s slightly out of alignment when you try to turn it. In the picture, the speed cube is the one on the left.

rubiks_cubes

The cube-solver robot loves it. I’ve posted a video of it on the YouTubes at http://youtu.be/iDMrePmmNA0 .

The whole thing is a supremely pointless exercise, but it is rather hypnotic to watch.

…and we’re back!

As a close follower of my blog, you will doubtless have noticed that it has been off the air the last few days. I didn’t. Rob drew my attention to its non-existence. It turns out that there were hosting troubles, and to cut a dull story short I’ve had to move the blog to a different hosting provider.

It’s more or less fixed now, but still has a few niggles to sort out. Sorry for any inconvenience.

Creating 3D models is hard

3D printing is easy. You can buy a 3D printer off the shelf for a few hundred of your Earth pounds, download patterns from thingiverse and be printing solid stuff straight away. Creating your own 3D models is harder, and it seems to me that this is now the bottleneck (at least, if you don’t want to spend a lot of money). There are several options. You can scan a real item, if you have a 3D scanner, and then use really awkward tools to manipulate the mesh. You can persuade powerful and confusing tools for animation and modelling like Blender to create models. You can use infuriatingly limited free versions of commercial tools like Sketchup. Or if you have really deep pockets, you can buy a proper CAD system.

I don’t like any of the above. I’m a programmer, and have a mechanical engineering background. I want to create precise, preferably parametric, models of components which I can then print out and fit together with other components to make machines. Up till now, I’ve been using OpenSCAD, and it’s been pretty good. OpenSCAD allows you to define shapes precisely using numbers, and combine them exactly (wrong word, I know) to create useful things. I love it. But it’s always had one major flaw (and a few little ones, but that’s another story). Building a model in OpenSCAD is like writing a program: it supports functions and loops, but it doesn’t support variables. At least not variable variables. You can assign a value to a variable and use it later, but that value cannot change. This is a bit of a pain, and it limits what you can do. Now there is an alternative, and that makes me happy.

The new kid on the block

OpenJSCAD is an entirely browser-based (yes, you read that right) editor and renderer for 3D models. It uses a language quite similar to OpenSCAD (indeed, it can import OpenSCAD files), but more powerful and a bit like C. And it supports proper variables. Hurrah! Here’s a screenshot of a little something I knocked up in it:

screenshot_4

Try doing that in OpenSCAD. I did, and the spiral ramp was not easy. Here’s an assembly of a few of them, printed out and stacked together. A ball bearing runs around them very nicely:

ball chute

Try OpenJSCAD. If you don’t like it, I’ll give you your money back.

Here’s my .jscad file which created the ball chute. You can simply cut the text and paste it into OpenJSCAD if you want to try it.

// title: Spiral ball bearing chute
// author: Peter Robinson
// license: None.  Free for all.
// description: A stackable spiral chute for ball bearings.

var radius = 50;
var slotwidth=6;
var wallthickness=1;


function main(params) {

    var a=2;
    return union(
        section(),
        section().translate([0,0,20]).rotateZ(90),
        section().translate([0,0,40]).rotateZ(180),
        section().translate([0,0,60]).rotateZ(270)
        );

}

function section(){
    return union(
        ramp(1,11,radius-4,radius-4,4),
        ramp(6,11,radius-3,radius-3,1),
        ramp(1,11,radius+3,radius+3,6),
        hub()
        );

}

function hub(){
    return  difference(
        union(
            CSG.cylinder({
            start:[0,0,0],
            end:[0,0,10],
            radius: 10,
            resolution:36
            }),
            CSG.cylinder({
            start:[0,0,10],
            end:[0,0,15],
            radiusStart: 8,
            radiusEnd: 7,
            resolution:36
            }),
            spoke().rotateZ(-27.5),
            spoke().rotateZ(-27.5-45).scale([1,1,0.5])
        ),
        union(
            CSG.cylinder({
            start:[0,0,-1],
            end:[0,0,17],
            radius: 6,
            resolution:36
            }),
            CSG.cylinder({
            start:[0,0,-0.5],
            end:[0,0,5.5],
            radiusStart: 7.9,
            radiusEnd: 6.9,
            resolution:36
            })
        )

    );
}

function spoke(){
    return CSG.cube()
        .scale([radius/2-slotwidth/2,1,5])
        .translate([-radius/2,0,5]);
}

function spiral(){
    var hex = CSG.Polygon.createFromPoints([
            [0,slotwidth/2, 0],
            [0,-slotwidth/2, 0],
            [0,-slotwidth/2, radius],
            [0,slotwidth/2, radius]
    ]).setColor(
        [0, 0.8, 0]
    );
    var angle = 5;
    return hex.solidFromSlices({
        numslices: 365 / angle,
        callback: function(t, slice) {
            var coef = 1 - t * 0.8;
            return this.
            translate([0 , radius, t*10]).
            rotateZ(angle * slice);
        }
    });

}

function ramp(slotwidth,climb,innerradius_start,innerradius_end,basethickness){
    var hex = CSG.Polygon.createFromPoints([
            [0,0, 0],
            [0,slotwidth, 0],
            [0,slotwidth, 1],
            [0,0, 1]
    ]).setColor(
        [0, 0.8, 0]
    );

    var base = CSG.Polygon.createFromPoints([
            [0,0, 0],
            [0,slotwidth, 0],
            [0,slotwidth, 1],
            [0,0, 1]
    ]).setColor(
        [0, 0.8, 0]
    );
    var angle = 5;
    var radiuschange = innerradius_end-innerradius_start

    var bottom = base.solidFromSlices({
        numslices: 95 / angle,
        callback: function(t, slice) {
            return this.
            translate([0 , innerradius_start +t*radiuschange, 0]).
            scale([1,1,basethickness]).
            rotateZ(angle * slice);
        }
    });

    var spiral = 
        hex.solidFromSlices({
        numslices: 95 / angle,
        callback: function(t, slice) {
            return this.
            translate([0 , innerradius_start +t*radiuschange, 0]).
            scale([1,1,climb*(0.1+t*0.9)]).
            rotateZ(angle * slice);
        }
    });


    return union(
        bottom,
        spiral.translate([0,0,basethickness])
    );

}

More tiny WiFi success

As I have posted before, I have been playing with the super-cheap WI07C WiFi module based on the ESP8266 chip. I’ve now had sufficient success with it that I can publish a post on a working project. This simple setup uses an Arduino nano to read temperatures from to 18BS20 sensors, formats the data as JSON and then sends it over WiFi to a server on my home network. It’s cheap and simple. Here’s the fritzing diagram:

WI07C_bb

You may notice the Adafruit level shifter board in there too. That’s because the digital IO from the arduino is 5V, but the WiFi module needs 3.3V. The Adafruit module is a dead easy way of joining the two. The temperature sensors use a three-wire protocol which allows you to connect many in parallel and address each one individually. there’s a software library which takes care of this.

Here’s the Arduino sketch. It uses Miles Burton’s temperature control library to read from the sensors, so you’ll need to download that. NB: this code is not a shining exaple of style or completeness. It’s a quick hack to get something working. It does no error checking or reconnection if there are problems. You may find yourself pressing the reset button a lot.

You might also note that the hardware serial port is required for the WiFi module, which needs 115200 baud. This means that you can’t upload a new sketch to the arduino while it’s connected to the WiFi module. I just whip out the wires to the TXD and RXD pins on the arduino while I’m uploading, and all is well.

The sketch tries to join the WiFi network, and then tries to establish a simple TCP connection to a server IP address and port of your choice. Once the connection is established, it checks the temperature sensors every 10 seconds or so and sends the temperatures to the server in JSON format, thus: {"temp":[22.63,22.81]}. That’s all it does. You’ll need a TCP server listening on your chosen IP address and port, of course. It seems to work quite reliably for me.

#include <SoftwareSerial.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define SSID        "MyHomeSSID"
#define PASS        "MyPassword"
#define TARGET_IP   "192.168.1.xx"
#define TARGET_PORT 5000
#define TEMPERATURE_PIN 9

SoftwareSerial dbgSerial(10,11); // RX,TX
OneWire wire(TEMPERATURE_PIN);
DallasTemperature sensors(&wire);

void setup()
{
   // WiFi module needs fast serial, so must use the hardware port which is also used for
   // uploading sketches
   Serial.begin(115200);
   Serial.setTimeout(5000);

   // For debugging, we therefore need a software serial port.  This can be much slower.
   dbgSerial.begin(9600);
   dbgSerial.println("Starting");

  delay(1000);

  // Connect to the wirelsess network
  dbgSerial.println("Joining network...");
  Serial.print("AT+CWJAP="");
  Serial.print(SSID);  
  Serial.print("","");
  Serial.print(PASS);
  Serial.println(""");
  receive();

  // Just check that the WiFi module is joined to the network
  dbgSerial.println("Check connection...");
  Serial.println("AT+CWJAP?");
  receive();

  dbgSerial.println("Initialising sensors...");
  sensors.begin();


  dbgSerial.println("Connecting to server...");
  Serial.print("AT+CIPSTART="TCP","");
  Serial.print(TARGET_IP);
  Serial.print("",");
  Serial.print(TARGET_PORT);
  receive();
  delay(5000);

  dbgSerial.println("Ready to rumble!");
}

int incomingByte=0;
bool echoLocal = true;

// Get the data from the WiFi module and send it to the debug serial port
void receive(){
  delay(500);
  while (Serial.available() >0) {
    incomingByte = Serial.read();
    dbgSerial.write(incomingByte);
  }
  dbgSerial.println();
}

char temp1[10];
char temp2[10];

void loop()
{

  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  dbgSerial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  dbgSerial.println("DONE");

  dtostrf(sensors.getTempCByIndex(0),1,2,temp1);
  dtostrf(sensors.getTempCByIndex(1),1,2,temp2);  

  String json="{"temp":[" + String(temp1) + "," + String(temp2) + "]}";
  dbgSerial.print("Sending ");
  dbgSerial.println(json);

  // Send the data to the WiFi module
  Serial.print("AT+CIPSEND=");
  Serial.println(json.length());
  delay(500);
  Serial.print(json);
  receive();


  delay(10000);
}

This is a work in progress. I’ll be updating it soon. But for now, I’m very pleased with the simplicity of the WiFi modules, and even more pleased with their low cost (I am a Yorkshireman, after all).

Wifi success, for a change!

WI07C module featuring esp8266 chipset

I’m actually having success in connecting the super-cheap WI07C wifi module to my home network, so that an arduino can send data anywhere.

WI07C module featuring esp8266 chipset
WI07C module featuring esp8266 chipset

I can now reliably establish a TCP connection and send data back and forth. It works rather well, with some limitations. It helps that I came across some slightly better documentation, sourced from this esp8266.com forum post which lists a few more AT commands and some more illuminating explanations.

AT+CIPMODE

AT+CIPMODE=1 or 0 (1 is default) sets the data receiving mode of the socket. If it’s 0, received data is simply sent to the serial port. If it’s 1, data gets “+IPD,c,n,” prepended to it, where c is the channel number and n is the number of bytes received. c is omitted if you are in single-channel mode. If you have multiple connections going in, this mode is logical. If you have only one, it may be simpler to use the transparent mode (AT+CIPMODE=0). But see AT+CIPSEND below, because this is affected by AT+CIPMODE!

AT+CIPSEND

The usual documentation about this is a bit pants. Here’s how it works. When you issue an AT+CIPSEND command, you normally tell it the channel you want to send it over (the chip can keep four IP connections going at once) and the number of bytes you want to send (call it n). The module then responds with a “>” character. It will then take the next n bytes you give it and send them over the TCP link. Then it responds with “SEND OK” and returns to normal, waiting for your next command. There are three variants of the AT+CIPSEND command, however:

AT+CIPSEND=channel,length is used if you have multiple connections open (AT+CIPMUX=1)
AT+CIPSEND=length is used for a single channel (if you have previously issued AT+CIPMUX=0 to tell it that you will only use one connection at a time)
AT+CIPSEND can be used without any parameters if you are using a single channel and transparent data mode (AT+CIPMODE=0). Note that there is no channel or length specified in this variant. After you issue the command, it will simply send every byte you throw at it directly to the receiving socket. This includes AT+ commands. Note that I have as yet found no way of exiting this mode other than resetting the module.

Also note that if you are in transparent data mode (AT+CIPMODE=0) and you try to use the variant of AT+CIPSEND which uses a length value, it won’t just fail or ignore it, it will actually reboot the module.

Lesser known AT commands not in other documentation

AT+GMR

AT+GMR retrieves the firmware ID of the module. There are at least two versions in the wild.

AT+CIPSTO

AT+CIPSTO=60 Sets the socket timeout period to 60 seconds
AT+CIPSTO? gets the current timeout value
You might want to change this value because if the server closes the socket, and your timeout value is too high, there appears to be nothing you can do from the client end other than do a hardware reset. I’m thinking it’s probably going to be necessary to tie the CH_PD line of the module to an output from the arduino, as I’m bound to want to reset from software at some point.

It’s good to make some progress. I’m going to look at the using the module as a server next.

WiFi for less than a fiver

Last week, the excellent hackaday ran a story about a new WiFi module (the Wi07c) based on the ESP8266 chip. It is eminently suitable for attaching to an arduino or similar, but it only costs $5. As I’ve been whining for some time about the high cost of WiFi shields for arduino, this piqued my interest. I bought a couple from the electrodragon store and have now had a bit of time to play with them. It seems that for once I’m a little ahead of the game – other people seem to be waiting a long time to receive theirs. The documentation is a little sparse, but enough to be going on with. There are three main sources, mostly community-supported: electrodragon, a hackaday.io project and nurdspace. The device is serial-driven, using a limited set of AT commands which will feel familiar to anyone who has worked with GPRS modems.

I’ve connected one of the modules to an arduino, and done some testing. I’ve also driven it directly from a serial terminal on my PC. These are my findings so far:

  1. The module runs at 3.3V, not 5V. The 3.3V available from an arduino can’t provide enough current to power it (though it may seem to). The WiFi module won’t even respond to commands without a separate power supply. Powering it from one of these works fine.
  2. In the version of the module I have, you must connect the CH_PD pin to +3.3V. Don’t be put off by the suggestion in the diagrams on Nurdspace.com that the four pins in the middle of the connector are not connected to anything. CH_PD is the pin next to the TXD pin.
  3. The AT commands mostly work. AT+CWLAP briskly returns a list of visible access points. AT+CWJAP connects to my home router (or claims to). Beyond that, things get flaky. Actually setting up a TCP connection to a running server does not seem to work. Nor does sending UDP. both allow me to get to the point of sending data, but then simply respond ‘busy’ to any further command or data until the device is rebooted (by unplugging it from the power supply). I’m not convinced that the device is ever genuinely connected to my router: the router doesn’t show it in the list of active clients.

I’ll keep experimenting with it, but I’m frustrated. It seems so close to being functional, but isn’t quite there yet. It feels like the firmware is missing some useful commands and diagnostic information. Or maybe it’s the documentation. Perhaps if my Chinese was better…

Lathe parts online

Some people (honestly, more than one) have asked for the source files for the parts for the 3D printed lathe. I’ve posted them under the OpenSCAD tab. There’s a direct link here. Don’t forget the Youtube video if you want to see it in action.

It’s not my fault!

It’s probably not my fault, at least.

If you’ve been following my blog (and why wouldn’t you?), you’ll know that I have had some issues with my 3D printer printing things skewed. It got particularly annoying a day or two ago when a five-hour print ended up unusable. It’s about time I got this problem sorted out. Annoyingly, it seems that every time I do a test print, the problem goes away. Indeed, a test print after the last failure was absolutely fine. Perhaps it is time to apply logic, and isolate the causal factor. My test prints tend to be small (so that they are quick and cheap), but I notice (or perhaps only care about) the problem on large objects which take a long time to print. This morning, I printed a set of test objects of increasing size, to see if the problem was size related. The objects I used as test pieces were simple hollow square-section blocks, connected by a thin strip. The model looked like this:

test_pieces

I printed each of the pieces individually, smallest first. The results were very interesting:

size-based distortion 2

size-based distortion 1

As you can see, the first three parts printed fine (I’m not worried about the finish quality, just the geometrical straightness). Only the largest one had any skew at all. The really weird thing is that the skew was pretty constant over the entire width. To me, this strongly suggests a printer firmware issue. If the hardware were misaligned, or a drive were slipping, I would expect to see this on all the prints. A structural problem would surely show up gradually as the parts grew bigger, or possibly show up more the further from the centre the print head moved. In fact, the skew at the centre is the same as the skew at the ends of the part, and there is no skew at all on the second-largest component.

So if the problem is not the hardware (that’s why it’s not my fault 🙂 ), what is it? It could be the slicer, but this seems unlikely because I have used both Slic3r and Cura. Printing gcode files from either can have the problem. This seems to leave only one culprit: the printer firmware. I’m thinking that there is some rounding error, or some motor step-counter in the firmware which is overrunning or accumulating a consistent error on long print head movements. As the print head moves back and forth along the x-axis, this error fails to get reset between z-slices. Each layer is slightly offset from the one before. With smaller parts, this error simply does not occur. This is speculation at the moment.

I’m currently using Marlin firmware. My install is at least a year old, so my first step will probably be to upgrade to the latest version. This might be a bit of a pain, because I made a few customisations when I installed the last version, to suit my printer. If that doesn’t sort the issue out, I’ll have to delve into the source code. At least Marlin is open source, so I can fiddle with it.

Disappointment is…

… finding your five-hour print is faulty

Hmmm. Yesterday I replaced the heater in the printer’s hot end, vastly improving printing. Today I replaced a faulty power switch, reducing the chance of the printer cutting out mid-print. There seemed to be no reason not to make a start printing some of the bigger components I’ve been working on. Here’s the first one:

large thing 01

Don’t worry what it is. All will be revealed eventually. it’s a substantial bit of plastic, and demonstrates why I wanted to build a 3D printer in the first place. How else could I make components like that without a proper machine shop? I still want a proper machine shop, by the way, but I love the fact that I can make lightweight, stiff components straight from CAD models. I mean, just look at it.

large thing 02

So, you might think all is well. This component took five hours to print, and the printer didn’t miss a beat. What it did do, however, is print the whole thing skewed. It’s not immediately obvious, but look at this:

disappointment 01

and this:

disappointment 02

Makes you want to cry, doesn’t it? Both of those sides should be vertical, in case you didn’t realise. In fact the whole thing has been printed with a slant on it. I’ve had this problem before, but I thought I’d cured it by increasing the tension in the drive strings (the fault being that one or more was slipping). Clearly I hadn’t. Back to the drawing board I go.

Always remember: that which does not kill us makes us really annoyed.