Changes to the design
The design and construction are described in the blog post A New Sandbot and the STLs and code can be downloaded from Github from the links in that article. The article and the STLs are not quite the same, though it's still easy enough to work out how to assemble it. I used the laser cutter at work to make the acrylic pieces (the first time I'd used it). The changes I made were:- different spacers for the motors. I had two motors of different heights and neither was quite right with the original spacer.
- made the "forearm" thicker. This is the grey piece with the magnet at the end, and it was very flimsy and wobbled whenever it moved.
- changed the magnet holder to take a round 12mm magnet instead of the original square one. I had some of these magnets from a previous project and they are also much easier to find.
- redesigned the lower arm locking piece to accept a 6mmx1mm magnet. This is used to trigger the endstop. The original design again used a hard-to-find square magnet.
The endstops in the original design used a board that can't now be purchased and required modification. I replaced it with some easy to obtain Hall effect sensors. They are not ideal as they trigger over a range if positions, though this can largely be fixed by adjusting the homing command, as described below. I designed new mounts for each of them.
Electronics and Software
Dobson uses a control board of his own design. There are two versions, one described in outline in the original blog post and a more recent one. I started by trying to use an off-the-shelf set up consisting of an Adafruit Huzzah32 and an Adafruit motor shield. The combination worked, though not very well, and I later replaced it with Dobson's Stepper Hat Trio board. (Another first: I haven't ever had custom PCBs made. At least, not since I was a hardware designer back in 1984). The Trio board is quite a good design, and there is an assembly video on YouTube. If you follow this video, note that one thing he forgets to say is that you have to put a jumper on J16 or the pullup resistors for the endstops will be floating rather than actually being pulled up, and won't work properly.
My experiments with the Adafruit parts worked OK, but were not great. The motor shield is not a modern stepper driver, and the motors run very noisily. I had to change Dobson's code to work with the Adafruit library (Github repo here). While I think his software is broadly good, doing this revealed a few things which were not ideal. Specifically:
- there is a Stepper class to abstract the details of the motor, but the code writes directly to the digital pins outside this class, making it tricky to figure out where the changes need to go.
- most of the motor control is done within an interrupt handler. As well as going against the general principle of doing as little work as possible in interrupt handlers, it's also not possible to call the Adafruit library from within the handler. ESP32 code requires all such code to have a special annotation, and so I would have had to change the Adafruit library and the libraries it depends on in the same way.
With the Stepper Hat Trio, things went more smoothly. I encountered a few issues. I suspect the published version of the code is not up to date with the board design.
The direction pins on the board are driven by a 74HC138 3-to-8 decoder. Different outputs are used for each stepper, but the effectively share the 3 pins that are the inputs. However, the code sets up the direction on a single pin, and does this some time before sending the step pulses. I fixed this by removing the 74HC138 and replacing it with a header that directly connected one of the 3 inputs to each of the direction pins for steppers 1 and 2. I am not using any of the other outputs of the decoder, so this worked fine.
The stepper drivers I used are TMC2208s. These can be controlled with SPI, and the board is designed to do this, but the code does not seem to support it yet. Instead, I added a couple of lines of code to pull the SDI/CFG1 and SCK/CFG2 pins high, so setting them for 1/16 microstepping. These pins are shared with the SPI interface, which in turn is used with the SD card on the Stepper Hat board; not a problem as I hadn't added the parts for the SD card. One final issue I found is that stepper 2 kept suddenly halting and turning the wrong way. This turned out to be because the configuration I had adapted was pulsing pin 14 to provide a WiFi status light, and I was using the same pin for the direction control of stepper 2.
As I mentioned, the Hall effect sensors trigger over quite a wide range, and so for homing, I changed the homing commands in the configuration so that they execute the following sequence: drive the motor one way until the endstop is not triggering; drive it the other way until the endstop triggers; then slowly drive it a fixed distance further, determined by experimentation. It's not super accurate but ends up with both arms centered fairly well at the home position.
For the record, here are my changes to the code to hold the extra two pins high (possibly unnecessary), and my configuration (with apologies for the horrible formatting).
- In MotionHelper.cpp, after the loop which calls configureAxis.
pinMode(33, OUTPUT);
pinMode(16, OUTPUT);
digitalWrite(33, 1);
digitalWrite(16, 1);
// Also set up auto standstill current reduction. Again, this conflicts with a SPI pin.
pinMode(5, OUTPUT);
digitalWrite(5, 0);
- Configuration
// ========================== DAHE config ===============================
"{"
" \"robotType\": \"SandTableMooseScara\","
" \"cmdsAtStart\": \"\","
" \"webui\":\"SandUI\","
" \"evaluators\":"
" {"
" \"thrContinue\": 0"
" }"
" ,"
" \"robotGeom\":"
" {"
" \"model\": \"SingleArmScara\","
" \"homing\":"
" {"
// Search one way until the endstop is off, search the other way until it is on; then advance for centering, determined empirically.
" \"homingSeq\": \"FR50;B10000n;#;B-10000N;#;FR1;B-120n;#;B=h;FR50;A10000n;#;A-10000N;#;FR1;A-450n;#;A=h;$\","
" \"maxHomingSecs\": 120"
" }"
" ,"
" \"blockDistanceMM\": 1,"
" \"allowOutOfBounds\": 0,"
" \"stepEnablePin\": \"21\","
" \"stepEnLev\": 0,"
" \"stepDisableSecs\": 10,"
" \"axis0\":"
" {"
" \"maxSpeed\": 50,"
" \"maxAcc\": 50,"
" \"maxRPM\": 30,"
" \"stepsPerRot\": 9600,"
// UnitsPerRot and things derived from it are only used in GeistBot, HockeyBot, MugBot and XYBot.
//" \"unitsPerRot\": 628.318,"
// maxVal is the arm length in mm.
" \"maxVal\": 100,"
" \"stepPin\": \"27\","
" \"dirnPin\": \"13\","
" \"endStop0\":"
" {"
" \"sensePin\": \"39\","
" \"actLvl\": 0,"
" \"inputType\": \"INPUT_PULLUP\""
" }"
" }"
" ,"
" \"axis1\":"
" {"
" \"maxSpeed\": 50,"
" \"maxAcc\": 50,"
" \"maxRPM\": 30,"
" \"stepsPerRot\": 9600,"
//" \"unitsPerRot\": 628.318,"
" \"maxVal\": 100,"
" \"stepPin\": \"12\","
" \"dirnPin\": \"14\","
" \"endStop0\":"
" {"
" \"sensePin\": \"36\","
" \"actLvl\": 0,"
" \"inputType\": \"INPUT_PULLUP\""
" }"
" }"
" }"
" ,"
" \"fileManager\":"
" {"
" \"spiffsEnabled\": 1,"
" \"spiffsFormatIfCorrupt\": 1,"
// Disable SD as we are holding the MOSI and SCK pins high for the microstepping config
" \"sdEnabled\": 0,"
// The next 4 lines are unused with sdEnabled = 0
" \"sdMOSI\": \"23\","
" \"sdMISO\": \"19\","
" \"sdCLK\": \"18\","
" \"sdCS\": \"4\""
" }"
" ,"
/* Disable wifiLed as we are using pin 14 for dir 1.
" \"wifiLed\":"
" {"
" \"hwPin\": \"14\","
" \"onLevel\": 1,"
" \"onMs\": 200,"
" \"shortOffMs\": 200,"
" \"longOffMs\": 750"
" }"
" ,"
*/
" \"ledStrip\":"
" {"
" \"ledPin\": \"25\","
" \"sensorPin\": \"-1\""
" }"
"}"
,