Saturday, June 15, 2013

Linear axis movement

After MF was over, I got to thinking about linear axis control and how it might have been used for the clip, instead of the rotating mechanism.

I returned to the printer carriage assembly I'd had earlier and finally got the nerve to pull out its DC motor and try to put a stepper motor in its place.  I partially succeeded.  The original DC motor had a nylon, single-flange timing pulley mounted to the motor, and the spindle was very small, like 1mm or 2mm diameter.  I had no other motors that would come close to that size.  The Teco motor that I wanted to use, because of its 1.8 degree accuracy, has a 5mm shaft, and there no easy or inexpensive way to step it down.  I also couldn't use the aluminum timing belt pulley that came with it.  It would have been great to have done that, because the belt pitch matched, but the belt width was slightly too wide for the aluminum timing pulley, and the pulley itself was of such a large diameter that it would have caused too much tension on the old belt and would have been misaligned.

Instead, I was able to use the cheap 7.5-degree paddle stepper that I'd gotten at Halted early on.  It had a timing pulley already built onto it (and impossible to remove) so I wouldn't have to get one, and wouldn't try to put the old nylon pulley onto it.  While it was less than ideal to have only 48 steps per full rotation, I wanted the experience of mounting my own motor to the assembly.  Its pulley had a 2mm pitch, so every other tooth was aligned with the original belt, and the pulley diameter was quite similar to the original.

I tried to mount the paddle motor, drilled holes, and then found I'd drilled them too big.  Learned lesson.

I found a different way to mount the motor, drilled smaller holes, and then very carefullly widened them with a round file until a mounting screw could fit into it.  Two mounting screws then self-tapped into the sheet metal and held the motor firmly.

I then added contact switches on either side to make sure the stepper didn't errantly run the carriage off the end or, really, to reduce harm to the belt and motor.

Once this was in place, I could reconnect it to the same stepper motor controls I had for the WBMD, and add some commands for running clockwise and counter-clockwise for some number of steps.  What I wanted to control:
- minimum step delay.  Going lower than this can cause you to exceed the max pulses per second (PPS) of a motor, and cause it to stall
- number of steps to move
- direction
Also, it's not entirely useful to state "clockwise" and "counterclockwise" as terms here.  That all assumes a particular orientation of the motor.  I suppose a good convention would be to assume that the direction is interpreted with the motor facing up.  That would only get in trouble for motors where the shaft runs out both sides of the motor, and that's fairly rare.

The shift register code to drive the motor assumed two pairs of wires driving motor coils in an A-B and C-D pairing.  I use a bit definition for the values 1001, 1010, 0110, 0101 and then cycle around again to the start to move a full step.  So to drive the motor I have a "where am I now" state in the range of 0..3, and then index into a four-byte array that contains the values 0x09, 0x0a, x06, and 0x05.  Going forward means increasing the state by one, and going backwards means decreasing the state by one, and in all cases mod by 4 to stay within the 0..3 range.  Then push the byte value into the upper or lower 4 bits of either shift register and send the clock signal to push the bits to the motors atomically.

I had purchased some SainSmart L298N Dual H Bridge drivers from e-Bay.  In part, I'd gotten these in anticipation of driving higher amp motors than could be handled with an L293NDE or other similar drivers.  These worked out great.  The only gripes: you have to be careful about lead length going into the screw terminals / cinches, else you can get dodgy results; and if you want to release the Enable lines, you need a second 4x1 wire housing, or you need a 4x2 to control all four motor lines plus the two enables.  I did it with two 4x1 housings, and have to keep an eye on them, else they get loose.

I found that keeping the motors enabled caused the motor drivers and motors themselves to heat up over time, so adding explicit enable and disable commands to the input tab/module was a good idea.  But more generally, it seemed that I was bumping up against the next big challenge: avoiding burning out the motors.

In this picture, you can see the two enable pins (white wires) are now set up so I can enable/disable the motor as desired.  The four wires in the middle (green, yellow, white, blue) are the motor logic control lines, and those connect to shift register outputs.  In this example, you can see I had a pair of yellows for OUTA and OUTB, and a pair of greens for OUTC and OUTD, so when this was taken, I was only doing full-stepping.

At the same time, I started learning about microstepping.  The site at stepperworld.com has a nice explanation of it.  I had to be careful in reading that site, though, because its wiring descriptions are based on 1A/2A/1B/2B configurations, where I think in terms of 1A/1B/2A/2B, so the bit positioning they have isn't correct for my wiring arrangement.

In my first foray into microstepping, I had the wrong wiring order.  That will stop you right there, especially in wave drive mode, because reduced torque, combined with misdirection of the motor is a recipe for quiet stalling.  In full stepping, you just need to make sure the pairs of wires are properly determined, so it doesn't matter if the A/B wires are in a B/A order, nor if C/D is in a D/C order.  For half-stepping, you have to get them in the correct sequence.

After some experimentation, and again much thanks to stepperworld.com, I figured out the proper order of wiring for my motors.

Getting half-stepping working also meant that I could accelerate to a faster pulse rate than I had been using before, so I changed the code to recognize a step delay max, a step delay min, and a number of steps that the motor goes through in order to accelerate to the minimum step delay.

The acceleration code is based on cosine values and some number of acceleration steps.  I now run with 64 acceleration steps in Frankenplotter, but used far fewer earlier in the game.  If, for example, I were to have to traverse 200 steps, I'd accelerate in the first 64, decelerate in the last 64, and go at minimum step delay in the steps in-between.  That method fails if the total traversal is less than 128 steps, so in those cases, I halve the number of steps to move, and use those values as the number of accel and decel steps, possibly going one step in the middle if the total step count is odd.

The cosine value table allows me to slowly move from max delay to min delay, and back again on deceleration.  Cosine runs in the range +1 to -1 in radians 0..pi, so I normalize that to a +1..0 range, multiply by the max-min delay range, and add that back onto the min delay to get a value.  All the cosine values can be precomputed (they get re-computed if the input module receives a new value for max/min/n_accel) so I don't have to rebuild them with each vector.  Also, the structure allows each axis to have its own acceleration count and delay range.


Friday, June 14, 2013

Clip position movement and position detection

The WBMD "clip" ended up being a rotating disc design.  It also had to move with high precision, because the proboscis (the thing that would push a tube into another tube to fill the water balloon mounted on the clip) needed very precise alignment.  And, we didn't have time for fancier solutions like image detection/comparison.

I wanted to model something that would work for this.  Several things led up to this:
1.  The kids' earlier Maker Faire project used cat toy lasers and optical sensors for detecting presence
2.  I'd taken apart an H-P printer, and found within it a carriage assembly that was driven by a DC motor, not a stepper, and its position detection was based on a board and a strip of plastic.  The board had the curious words "bar code" on it.  And upon inspecting the strip of plastic under magnification and seeing that it ran through an optosensor, I figured they'd effectively turn an optical mouse upside-down and were using it for positional feedback!  Very clever, and too clever for me to hack apart and use.  But it gave me ideas.
3.  I'd gotten a freebie laptop DVD drive, and had taken it apart to see how it worked.  Inside, I found a cool little board that they use for controlling everything.
What made this fun was that I could ignore all the (nice) electronic components, and just use that part that clips onto an old DVD or CD, giving me a free mount point and bearing.  I liked the idea of using a CD, because it has reflective material between its layers of plastic, and that would block the laser.

To model the clip, I stuck an old DVD+R onto the drive board.  I then kept it in a fixed position, and then clamped a fine-point Sharpie so that it would draw on the DVD.  Then, I just rotated the DVD around to give me one circle on the DVD at a fixed radius.

I then drilled holes into the DVD at various points.  As long as the hole was on the line, and as long as the hole was big enough for the cat toy laser to shine through to an optical resistor, I would be in business.

To rotate the DVD (i.e., simulate rotation of the clip), I ended up with a sloppy but functional solution that involved putting a rubber band on my stepper motor's timing pulley, and putting tension between the DVD's edge and the rubber band.  In doing that, it made it so that every step of the motor would result in a fractional turn of the CD.  It wasn't clear if this would be a reasonable approach for a full-scale build, but seemed promising.

Then, all it took was mounting a laser above (adding tape to keep the switch turned on) and an optical resistor underneath.  I added code that would keep stepping the motor until a dark-light-dark transition had occurred, and calibrated for the actual resistance values seen.  The code would spin the CD around until it'd seen the dark-light, start counting steps, look for the light-dark, and then back off half the number of steps seen.  It was a nice, crude, and pretty precise solution.

Merit badges:
- Disassemble a printer
- Disassemble a laptop DVD drive
- Extra credit: find the pins that make the DVD drive's motor spin, even if you don't have the soldering chops to wire it up for real
- Make a motor move something other than just the motor
- Build a position detection system
- Use a cat toy in some other way than what it was designed for (merit badge only available to those who are not cats)
- Generate an annotated video

While I don't have all the skills needed for it, I still want to try to wire directly to the pins of the BH6546KV chip that's on that drive board and make the motor move.  The pins I want are wired in pairs (three pairs, each effectively solder-bridged already) and so maybe, even though its super tiny, I can wire onto pairs of pins and thereby gain control without damaging things.

Arduino serial I/O and the Big Bug

As the main program came together, there were a number of conditions that could arise that would cause the program to stop.  For example, you wouldn't want to fill the balloon unless the dumper was up, and you wouldn't want to move the clip into position unless the proboscis was raised.

Chris and I talked about main Arduino loop() behavior, too.  He had decided it was intentional that we not allow parallel functions to be occurring.  I feel like I'm doing a poor job explaining, so let's lay out a few models.

Model 1: suppose the main loop() were being called rapidly.  Each time we'd go through the loop, the purpose would be to check status on everything, and act accordingly.  In this way, you might have one balloon moving into position for filling, while the last one was just out of the dumper and on its way down the chute to the launcher.

Model 2: instead of expecting the main loop() to be called rapidly, we'd take full control of the CPU.  So, in one call to loop(), we'd check state of the proboscis, raise it, move the clip, lower the proboscis, fill the balloon, etc., until someone hit the big red button and launched the balloon, and the launch arm had returned to position.  Only after all that had happened would we return from loop() and release control of the CPU to the caller.

The path Chris chose was model 2.  We'd hold onto control.  What that demanded was that we have some mechanism for getting everything started.  To do that, I looked into the Serial.begin / Serial.available / Serial.read functions, and added an "input" tab to the code.  Along with it, there were several state variables added to globals.h.  The main input function was to check to see if any new data had been sent in via the Arduino Serial Monitor.  If the "go" command was seen, it would set the "running" flag to true, and return.  In this way, the loop() could check for input and return control if the running state remained in a "stopped" state.  More generally, this allowed the input function to process a whole bunch of commands for setting and querying state variables before the "go" was seen.  Eventually, this would allow us to manage important aspects of the WBMD like how long to run the spinner, how long to delay between spins, and how many times to spin.  (By the way, we'd given up on the ultrasonic sensor approach, so a timed spin was in the design by this point.)

The input code had two flavors: <name>=<value> and <name>.   The former allowed for setting state variables or running commands a number of times, and the latter allowed for simple, one-word command processing.

The combination of having a set of commands and error conditions gave rise to having a large number of hardcoded strings in the system, the lion's share for emitting user-friendly and error messages.

I didn't know it at the time, but what this led to was the Big Bug.  As the program became more complex, the number of error messages, and more importantly the byte size of the error messages, became larger and larger.  Eventually, I'd compile the program and run it, and it would start skipping recognition of error messages, or reboot the system completely.  It was somewhat unpredictable, in the sense that on any given compilation, it might show up.  On the other hand, it was annoyingly repeatable.  Once the program was in a messed up state, it stayed that way.

I did find that the common solution was to reduce the message string sizes, and the problem would go away.  But being a fan of providing explicit error messages with helpful suggestions on next steps, I didn't want to trim the messages down too much.

This got me thinking about whether I really wanted all the computational horsepower programmed on the Arduino, or if we'd be better off having the main code written in Java, and having a pre-defined protocol between the computer and the Arduino sketch.  But more on that later when we talk about the Frankenplotter.

It took a while, but eventually I stumbled upon http://arduino.cc/en/Tutorial/Memory.  It turns out the Big Bug was caused by my long messages and other variables eating up what little SRAM is available!  The page thankfully provided ways to use the PROGMEM qualifier to force storage in flash (program) memory rather than SRAM.  That meant pulling all messages out to a msg tab (proper programming technique should have required that in the first place, though normally it'd be done for proper internationalization, not this memory selection purpose) and providing a function for retrieving messages by ID number.  After that big chore was complete, I haven't seen a Big Bug recurrence again.

Modular Arduino coding

The WBMD code was getting to be more complex, and it would have been great to have used object-oriented programming techniques, but I didn't have the C++ tools for doing it.  Instead, I resorted to old-school C programming.

To build Arduino code in a more sensible manner, you can use "tabs".  Each tab is really its own .ino file, and each appears to be compiled as part of the whole.  But, there are special tabs.  If a tab is a ".h" file, it can be included by others, and isn't part of the full compilation unless included.  To include it in one of the .ino files, you use a
#include "name.h"
pre-compiler directive.  Use quotation marks instead of "<" and ">" path-search delimiters.

I chose to build up the code starting with a "globals.h" file.  It's prefixed with the usual guards:
#ifndef GLOBALS
#define GLOBALS
put stuff here
#endif

I then chose to put all the pin assignment information into that file.  That was a little tricky for the WBMD set-up because there were also pin names to be reserved for all 16 of the shift registers' outputs.  I called those Q1A..H and Q2A..H, where 1 and 2 represent which shift register is in use, and A..H are the output pins (corresponding to one of the names used in datasheets I had read).  Putting all the assignments in one place was my visual cue not to assign a pin multiple meanings.

For other "objects" in the world, like the spinner or dumper or proboscis, I'd set up a tab of the same name, and give each one a <name>_setup() function.  Then, the main tab, the one that has the real "setup" and "loop" functions would call all the <name>_setup() functions from its setup() function.  Beyond that, it was up to each tab to declare its own behavior.  Generally speaking I followed a name_function() naming convention for functions.  Example: dumper_dump_balloon() or dumper_return_to_fill_position().  This is part of my normal programming set of rules: name functions very specifically.

Each object also had its own set of feedback choices, so there might be boolean functions named dumper_is_down() or dumper_is_up(), and those might query a digital input pin that's tied to a contact switch.  That would allow the skeleton of the main program to be fleshed out while the actual functionality behind the functions was hardcoded.

WBMD pump, spinner, and dumper - DC motor control

The next project was to figure out DC motors.  Jake had one I could borrow, but I wanted more to mess with.  I had gotten one earlier at Halted, and also pulled one from an old printer.

The WBMD called for having a pump to fill a water balloon, a spinner that would just spin the water balloon around a zillion times until it self-tied (proof of concept was completed by the boys by the time I got to this point) and a dumper that would release the balloon to a delivery chute.  For all this, I figured I'd need bidirectional and speed-controlled DC motor circuitry.

As it turns out, there are several example circuits online that explain how to control a DC motor in two directions, but none that make it very clear how to do that in combination with speed control while also maintaining proper isolation.

My first step was to figure out the pump motor.  Jake had a big lead acid battery and a bilge pump for the job.  It had been tested to see if it could have enough power to fill a balloon, and it did.  He had rigged up some tubing to allow sufficient hold by the balloon (with no human intervention to keep it held on) while pumping.

It was a this point that I fried my first transistor.  I tried to set up an opto-isolated circuit such that the Arduino would PWM the input to an opto-isolator, and the opto-isolator would allow current to flow to the pump.  But I hadn't accounted for the fact that the pump took 6 amps to run, sometimes peaking at nearly 10 amps!  Poor little transistor.  So it goes.

Then, I found that by running the power across a transistor that could take that kind of amperage, and using PWM, I ended up in a position where I didn't have sufficient amperage to pump the water balloon anyway.  The circuit would survive, but the pump wasn't generating enough water pressure to fill the balloon.

Meanwhile, Jake found a geared DC motor that would allow for the dumper to run without speed control.  And, I ordered up a SainSmart 4-channel relay that could take plenty of voltage and amperage.

So the design changed.  The pump would be run through a relay at full pressure -- no need for speed control nor directional control.  The spinner could use speed control but did not require directional control.  And the dumper was geared, so I could do directional control but didn't need speed control.  And what that meant was that the pump and spinner could be handled by a relay, and the dumper could be managed by a simple H-bridge.

Pump with lead-acid battery and relay in initial tests:


Merit badges
- Fry a transistor
- Measure amperage (always start with highest first, wire in series!)
- Learn about transistor current limits and voltage drops
- Use an optoisolator
- Learn how to handle a relay that closes on a LOW signal
- Use an Arduino to control a DC motor via a relay
- Use an Arduino to control DC motor direction

Ultrasonic proximity sensor

The WBMD called for a means by which we could tell the balloon was full, and the boys wanted to use a proximity sensor of some sort for it.  Little luck getting the right kind from Halted.  They had had some, but were sold out, though the guy there was really nice in suggesting that he'd let me borrow one from his lab o' tricks.

In lieu of having a fancy IR proximity sensor, I found and purchased a couple of ultrasonic proximity sensors over the web. 


(From the top of image to bottom, pins on the sensor read: ground, echo, trigger, Vcc)



After getting it wired up, coding for it required use of the NewPing library.  But after playing with it and monitoring its results, I came away unconvinced that it would meet our needs.  All too often, it would generate a false zero, and you couldn't tell if that was a zero caused by things getting too close, or if it was a zero generated by having no response to the echo (timeout reached waiting for echo).  The backup plan was to change the physical design to have a reflective back wall within the configured max ping distance, to try to avoid the false zeroes.


Along the way, I had hoped to figure out how to repurpose a Lego Mindstorms ultrasonic sensor, but after reading the specs on i2c.  And, now that I'm writing this blog, I'm wondering why I didn't find and consider this link earlier:
http://forum.arduino.cc/index.php?topic=62899.0

Merit badges:
- Wire up a proximity sensor
- Download, install, and use an Arduino library
- Learn about how i2c devices work

Stepper motors

Shortly after messing with LEDs, I started into motors.  Partly, I felt we'd need to know this for the Maker Faire project, and partly I wanted to work with steppers, because I'd done this in college but hadn't really understood them all that well then, and wanted to try again.

Project 1 was to run a stepper motor.  For this, I built up two H bridges, one based on the SN754410NE chip, and then the other on the L293DNE.  Both are referenced at http://arduino.cc/en/Reference/StepperBipolarCircuit

I'd gotten some kinda cheesy steppers and some good steppers at Halted.  The nice one is a Teco DSH40EX24A.  5.1V, 1A, 1.8-degree steps, hefty.


The cheesy one was a paddle stepper with a brass pinion on it, and it only did 7.5-degree steps.


Once I had the bridge and the wiring figured out, I needed a power supply.  I had read online (fortunately, I did the reading first) that you should not run a motor directly from the Arduino due to the current drawn by the motor.  Driving that much current from the Arduino could cause damage to the pin.  So instead, I got an old Seasonic ATX motherboard power supply working, and used its power to drive the motors.  Still, that would require a common ground wire between the systems, so much time was spent learning about circuit isolation.

I followed the Stepper and AccelStepper code in the Arduino libraries.  They were fun, but I didn't stay with them, opting instead to write my own control code.  The Stepper code makes non-atomic calls to individual pins, and that means that at some transition points in the code, you end up with three pins on instead of two, when doing full steps.  It's brief, but the purist in me says that you should just have two on, the other two off, and that's it.  The AccelStepper code was good, but by the time I started using it, I needed feedback control (i.e., hit hard limit = do not step any more).  So I rolled my own code that would set up the on/off bit values for each of the four control wires, and pushed the values over to the shift register, and then used the shift registers' atomic function for transferring data to the control wires.  And with that, I had full steps working.

Merit badges:
- Figure out the wiring of a stepper motor
- Build your own H-bridge
- Figure out how to trick an ATX motherboard power supply into thinking it's enabled so that it turns on.
- Use the Stepper library- Use the AccelStepper library
- Write code to drive multiple bits of a shift register at once, while preserving its original values in the other unaffected bits.
- Learn how optosensors work for circuit isolation

At this point in the process, I didn't know much of anything about chopping circuits, power resistors, current measurement, or microstepping (even half-stepping).