syntax-highlighter

Showing posts with label Arduino. Show all posts
Showing posts with label Arduino. Show all posts

Sunday, March 24, 2013

Setting up the WiFly RN-XV with a Teensy 3.0

A recent order from Sparkfun arrived, including a 3.3V Serial LCD and a Roving Networks RN-XV WiFly module.  The RN-XV module is intended to be a drop-in replacement for an XBee, except that it operates over WiFi.  At about $35, it is just about the cheapest way to make your project wireless enabled.

The module is 3.3V, meaning some form of level shifting is needed with a 5V system like an Arduino.  You can use this module with an Arduino via an XBee shield pretty easily.  However it is even easier to use with the 3.3V Teensy 3.0 ARM board, provided you have a breakout for small-pitch XBee module footprint.  The Teensy is also nice for this application because it has multiple serial ports, so you don't need to use the SoftwareSerial library, or program the board, then disconnect to use the wireless.

Setting everything up was pretty easy once I knew what to do, but this post summarizes the process should I ever need to do it again.

The setup that I am using is shown below:


Only four connections are needed once you're set up, 3.3V, GND and two data connections.  DOUT from the Teensy3.0 Serial3 connects to DIN of the RN-XV and DIN from the Teensy to DOUT to the RN-XV.  This makes the module operate as just a serial port, making it pretty easy to interface with. The remaining orange wire connects the Serial LCD display, more on this later.

To get started, I found it was easiest to set the RN-XV in ad-hoc mode. This can be done by connecting pin 8 to 3.3V and will cause the module to create its own wireless network.  When this happens you will see the status LEDs blinking green, orange and red; they're doing it, but you can't really see in this picture.  Note the additional green wire to 3.3V connected to the 8th pin.


You can then look for the network. On a Mac it's pretty easy, it just shows up in the list of networks in the status bar:


The WiFly shows up towards the bottom as WiFly-GSX-a8 or something similar.  If you connect to this network, you can then telnet to the module using the IP address: 169.254.1.1, port 2000.  The module should then respond with a *HELLO* string, at which point you type $$$ to enter command mode.  Command mode allows you to set up the module for your network.


When the module is ready, it will respond with the CMD message to indicate that you're in command mode.  To set up your network you can issue the commands:


set wlan phrase (password);
set lan ssid (your network name);
save
reboot

You can also issue commands to assign a static IP address to the module, but I didn't do this.  For more information, see this excellent introduction http://www.tinkerfailure.com/2012/02/setting-up-the-wifly-rn-xv/

I found that sometimes the module would respond with a confirmation and sometimes would not. I repeated the process a few times in the hopes that some combination would stick.  After this process, remove the power and and connection from pin 8 to 3.3V.  This will cause the device to try to connect to your wireless network.

You should now be able to telnet to the device, but this time with your computer and it connected to your normal WiFi network rather than the ad-hoc network that the device creates.  However first you need to find the IP address of the module.  To do this, I went into my router configuration page:


Conveniently the WiFly module had an entry: 192.168.1.106. Depending on your router, you should be able to set up a specific IP address for the router to assign to the module based on the MAC address.  However my POS router does not allow this.

I could then telnet to the module's IP address, again using port 2000.  This module responds with the same *HELLO* prompt, indicating that everything was successful and the module is on the network and communicating.

With the connections above the Teensy should now see the module as just another serial port.  To test this, I attached the Serial LCD and uploaded the following code to the Teensy:

#include<stdio.h>

void setup(){
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial3.begin(9600);
}

void write_lines( const char *L0, const char *L1 ){
  
  Serial2.write( 0xFE );
  Serial2.write( 0x01 );
  delay(10);
  Serial2.write( 0xFE );
  Serial2.write( 128 );
  delay(10);
  Serial2.print( L0 );
  Serial2.write( 0xFE );
  Serial2.write( 192 );
  delay(10);
  Serial2.print( L1 );
}


void loop(){
  if( Serial3.available() ){
    char L0[17];
    char L1[17];
    int pos = 0;

    L0[0] = '\0';
    L1[0] = '\0';

    while( Serial3.available() ){
      char c = Serial3.read();
      if( c == '\n' ){
        pos = 0;
        Serial.print('\n');
      } else if( c == '\r' ){
        
      } else {
        if( pos < 16 ){
          L0[pos] = c;
          pos++;
          L0[pos] = '\0';
        } else if( pos < 32 ){
          L1[pos-16] = c; 
          pos++;  
          L1[pos-16] = '\0'; 
        }
        Serial.print( (char)c );
      }
    }
    write_lines( L0, L1 );
  }
  delay(100);
}

My LCD is a 2x16 character display.  The code above just polls for available data on the third serial port and, when a newline is encountered, prints it out onto the display.  Lo and behold, after the following session:

Jamess-MacBook-Pro:~ jgregson$ telnet 192.168.1.106 2000
Trying 192.168.1.106...
Connected to 192.168.1.106.
Escape character is '^]'.
*HELLO*
This is James

The result on the display is below:


Hooray! An utterly useless internet thingy!




Sunday, February 10, 2013

Prototype 5-Axis CNC Board

Since I'm working on some CNC firmware, I thought I needed a test platform.  I didn't want to rip-apart the CNC so I decided to build a new 5-axis board using the new Pololu stepper drivers. These are nice little cheap boards and their advantage over the ubiquitous A4988 drivers is that they don't need cooling up to 1.5A per phase.  This makes things way easier, since those tiny little heatsinks for the A4988 chips are scattered all over my apartment, double-sided tape all linted up so they won't stick, just waiting to be stepped on.  They hurt even more than stepping on a Lego block, little buggers.

Anyway, I figured I should just go to five axes, which would allow me to control the 3D printer eventually (1X, 1Y, 2Z, 1E).  So I built a protoboard test board:


I laid out the board and soldered it up.  It was pretty easy except for having to drill a few extra holes in the power-rails to get the spacing I wanted.  After electrically testing everything I decided not to blow all 5 drivers simultaneously on a meticulously copied wiring error, opting instead for only two.

The MCU controlling the whole mess is an Arduino Uno.  I have the step pins on PortD and the direction pins on PortB, which allows fast bitwise operations to be used for stepping.  While picking up the stuff for the prototype board I also bought a proto-screw-shield, wanting a more resilient mounting option for the Uno:





I have the early version of my CNC firmware loaded onto the Arduino.  After firing up the serial terminal, I had the thing running!



Not super crazy, but still rewarding nonetheless.

CNC Firmware running on Arduino Uno

In an earlier post I described some prototype CNC firmware that I had gotten running on the Teensy3.0.  The Teensy3.0 is a 32-bit ARM board and is pretty nice, but I wanted to see if the code would also run on an Arduino Uno.  This is a much more restrictive platform; only 2k of ram and 32k of program space.  However, after a bit of porting of the hardware layer, it compiles and seem to run no problem.  Here's a screen-capture from a (brief) session, the pulse-ticks stuff is debugging the motion control module.


Note that this is running the full GCode interpreter, with spec-compliant command sorting and an 8-move lookahead buffer.  The step timer is running off a 2KHz Timer1 interrupt.  I'm hoping to increase this to 8+ KHz, at least on other platforms, which should make smoother motion control possible, but at the moment I'm just happy that my ANSI C + POSIX code that I've developed on on a 64bit quad core machine with 16Gb ram ports easily to an 8bit system with 2k or ram and only interrupts after changing less than 50 hardware-specific lines of code.

I've written the code this way for just this reason; by keeping all the hardware specific stuff isolated in a hardware abstraction layer I'm hoping to make a code base that stands the test of time, from small embedded platforms to boards like the Raspberry Pi, to full-blown PCs.  So far so good, it's running on an 8bit AVR, a 32 bit embedded ARM and a full X86_64 PC.

Wednesday, January 30, 2013

Start of CNC Firmware Running on the Teensy 3.0

I've been intermittently working on some CNC firmware with the goal of getting it to run on everything from an Arduino to a standard PC.  It's a fun project because it's very resource constrained but involves a number of different parts, like GCode parsing, asynchcronous programming, portability and motion control for non-Cartesian machines.

In the interests of portability I've been writing the various components in plain ANSI C (except the comments, I like my double slashes!).  By doing this I expect the code to be portable across a wide variety of platforms, from AVRs to the Propoeller child to ARM to PCs, with minimal effort.

My hopes are that the firmware will conform to the NIST GCode specification as closely as is manageable.  That said, some sacrifices will have to made as the spec calls for around 20k of addressable space for expressions, which exceeds the total RAM of Arduinos and even the Teensy 3.0.  However I hope to get mostly spec-compliant in terms of order of operations, expressions (if not the addressable space) and features.  Where possible I am also allowing the specific capabilities to be determined by preprocessor macros.

I've done a bit of work on the GCode interpreter, getting some test code together that parses GCode expressions (this led to my Mathematical Expression Parser in C) as well as an arbitrary dimension DDA implementation.  To date these have been just simple tests, nothing actually running on real hardware.  I've also done some early tests on doing feedrate optimization and

However today I made some good progress on actually getting some real firmware started.  I got my early GCode interpreter stripped down and abstracted out all the hardware-specific stuff into a hardware layer.  I then wrote the hardware abstraction layer for the Teensy 3.0 version of the Arduino environment and fired up the code, using the following string to test with:


N1229.0(This is a comment)G01x110.0   y330.0 M7 F20.0(MSG: another comment)

And it worked!


The screenshot above shows the GCode interpreter code loaded up in the Arduino environment, the Teensy loader application that programs the Teensy and the serial output of the parser. Note that this is not just reading the commands linearly, it is fully parsing the input, splitting it into distinct commands, sorting the commands by operation precedence (as described in the NIST spec) and finally calling back to the Arduino sketch with each command.

Parsing the string and calling back takes about 250 us (the remaining time taken is up by serial communications) so more than fast enough to fill up a lookahead buffer.  Total RAM used is about 5k, so this won't run on an Uno but does run handily on the Teensy 3.0. Ditto goes for the flash at 33Kb currently. This is pretty hefty compared to other firmware, but I think portability and cleanness makes up for that.

The next steps will be to get the kinematic/motion control stuff fleshed out.  I think I should be able to use by DDA implementation running with the Teensy timers pretty easily.  Then it's just tweaking and adding features...

Thursday, January 10, 2013

Periodic Interrupt Timers on the Teensy 3.0 (Freescale MK20DX128)

I recently ordered a Teensy 3.0 and today it finally arrived!  It definitely is teensy.  Anyway, after soldering on headers and popping it in a breadboard, I got it running with the blink example.  It took some time, but my issue was downloading directly from the Teensy loader page (which does not support Teensy3.0) rather than from the PJRC Teensy forums. To be fair, the Teensy loader page does have a notice at the top about this, but I missed it.

After getting the software, I was able to write a standard Arduino sketch to blink the LED connected to pin 13.  The code is below:

void setup(){
   pinMode(13, OUTPUT);
}

void loop(){
   digitalWrite( 13, HIGH );
   delay( 100 );
   digitalRead( 13, LOW );
   delay( 100 )
}

It's pretty nice that the Teensy3 works like a regular Arduino, but with more pins, at a higher clock rate, in 32 bits and with heaps of extra peripherals.  However my end goal is to use the board as a CNC controller for my ongoing firmware project.  This will make extensive use of timed interrupts to control steppers, so I thought I'd try to get timers working.  Of course, the Teensy3 is not a Atmel uC, so everything changes at this point and, with the help of this forum post to get started, I had to dive into the manual (available from here) for the Freescale MK20DX128 that the Teensy3 is based upon.

According to the forum post, the timer to use is one of the (4?) Periodic Interrupt Timers (PITs).  As expected, these have a number of control registers. Registers listed with an [N], e.g. PIT_LDVAL[N] should have an appropriate timer index substituted, like PIT_LDVAL2.  Here are the registers:
  • SIM_SCGC6 - Enables/disables clock used by PIT timers, not exactly clear on the details, set to SIM_SCGC6_PIT in the forum post example.
  • PIT_MCR - Enables and disables the PIT timers. Writing zero enables the timers and writing 1 disables them.
  • PIT_LDVAL[N] - Sets the timer count value.  Apparently the timer runs at 50MHz, so toggling timer 2 every second should set PIT_LDVAL2 to 0x2fa080 (hex for 50,000,000).  Visually, this appears to be around a second.
  • PIT_TCTRL[N] - Bit zero (TEN in the manual) enables (set to 1) or disables (set to zero) the timer. Bit one (TIE in the manual) enables (set to 1) or disables (set to zero) interrupts that can be generated by the timer.
  • PIT_TFLG[N] - Flag to indicate timer waiting.  Set to one to start timer and at the end of every called interrupt routine, otherwise interrupts will stop. 
Finally, interupts must be enabled. Again I'm not clear on the details, but calling NVIC_ENABLE_IRQ( IRC_PIT_CH[N] ) results in the interupt "void pit[N]_isr(void){}" being called.  Although it seems like the chip should have four timers, I only succeeded in getting timers 0, 1, and 2 working properly with interrupts, testing with index 3 gave a linker error in the Arduino software.

Anyway, here's the code for my tests:

#define TIE 0x2
#define TEN 0x1

void pit0_isr(void){
  digitalWrite( 13, !digitalRead(13) );
  PIT_TFLG0 = 1; 
}

void setup(){
  pinMode(13,OUTPUT);
  SIM_SCGC6 |= SIM_SCGC6_PIT;
  PIT_MCR = 0x00;
  NVIC_ENABLE_IRQ(IRQ_PIT_CH0);
  PIT_LDVAL0 = 0x2faf080;
  PIT_TCTRL0 = TIE;
  PIT_TCTRL0 |= TEN;
  PIT_TFLG0 |= 1;
}

void loop(){
  delay(2000);
}

Hope this helps someone get up to speed, and perhaps serves as a reference for me later on.

Saturday, November 3, 2012

Low Cost CNC Part VIII - Built-in electronics

In Part VII, I finally got the CNC cutting, but it was still a bit rough.  Cables ran everywhere, it was powered from a wall-wart and the electronics were sitting next to it on the table.  You can see the setup below:



Since finding the big C-channel piece that makes up the base of the machine, I've always planned to build the electronics into the base to give a nice, compact and clean machine.  I'd been waiting for a power-supply to arrive before doing this, but last week it came so I got to work.

The electronics are currently mounted to a piece of plywood that hooks on a key at one end and has bolt holes to bolt to some 3D printed plastic standoffs.  It fits into the base pretty nicely.


The plywood is a bit of an old shipping box; it had my address on it, hence the black tape.  Undoing two nuts on the right hand side allows the plywood to come loose:


Running approximately left-to-right, you can see the Arduino running GRBL, the three stepper driver carrier boards, a solid-state AC relay for spindle control as well as the power-supply and finally the E-stop button.  All the mains power is wired with 14 gauge household wiring connected with Marretts.  The E-stop button cuts all mains power to the power-supply and relay, thus rapidly immobilizing the machine.  The Arduino is unaffected by the E-stop, being powered from USB.  In this way, the E-stop also functions as an optional operator stop, allowing you to de-energize the machine, reposition it manually or change tools and then start it back up, knowing that it won't starting moving or cutting with your hands in there. 



Looking from the back, the USB socket for the Arduino is accessible, as is the scavenged spindle socket and power cable.  In a subsequent revision I plan to replace these with panel-mount components, but I couldn't locate them locally last week.


It's still a bit ghetto, but quite functional and self-contained at this point.  I intend to finalize the electronics layout and then make a sheet-metal replacement for the plywood with front and back panels and all panel-mount components.  But for the moment, it's working pretty well and I can easily pick it up and move it without rewiring the whole thing.


The other, straightforward, addition was cable drags on all the axes.  This has really helped to clean up the machine, giving me something that looks more like an actual tool than before.  Hopefully I will be able to start using it for projects soon.



Sunday, September 16, 2012

Low Cost CNC Part V - A Redesign

I've been working on a homemade CNC now for quite some time.  My goal for the project was to produce something modular, where shaft-mounts, motor mounts and bearings were entirely separate parts using a standardized mounting pattern.  This would allow the machines to be put together like Legos and has a lot of advantages, like the ability to mix and match drive options, e.g. in the image below, one axis is screw-driver while the other is belt-driven.



I still think this concept has merit, however the part designs that I ended up using made for bulky and not particularly stiff machines. In the photo above, the top of the XY table is close to six inches from the base plate and the machine itself has considerable give.  Even worse, I didn't build enough slop into the designs to accommodate tolerances for manually built mounting plates, so it was actually quite difficult to get all the pieces to play nice.

I've since redesigned the machine to use more compact mounts, merged the shaft and motor supports into a single axis-end part and moved to half-inch shafting.  The switch to larger shafting results in a much stiffer machine, but unfortunately does require bushing style bearings.  Using the combined axis ends simplifies alignment, but unfortunately precludes belt drives.  I've also switched to Nema 17 steppers from Nema 23s, which leads to a more compact overall machine.  Surprisingly they don't seem to have much affect on the overall machine speed.  This makes for a much cleaner design:


Like the old parts, the new parts are 3D printed, but this time on my recently acquired RepRap.  The meshes were parametrically generated using my Python contructive solid geometry library, which is becoming quite usable.  This serves for prototypes, but final parts could be machines from plastic or aluminum.  The fixed-end for the leadscrew uses the same axis-end as the motor side, but with some bearing plates and Nylin nuts to take the axial loads:


After switching to acetal linear bushings, the bearings mounts can be made much more compact, reducing the height of the XY table from about six inches to a shade over 3.  These seem to run nicely on plain hardware store shafting, although hardened precision linear shafting would obviously be stiffer and smoother than cold-rolled bar stock.


Overall the machine is looking much cleaner.  I've decided to go with a knee-mill style machine rather than a gantry arrangement.  This means an XY table horizontally with a third axis mounted vertically. I bought a massive piece of aluminum channel to serve as the base of the machine, 10"x3"x20".  This provides a sturdy mount, and the controller and power-supply can be mounted to the underside.  Here's the thing midway through the build process yesterday:


And here's the finished-except-for-limit-switches XY table mounted to the channel.  The green duct tape is just there to stop it from gouging up my coffee table when I move it.  I will also probably replace the top cheese-plate with a thicker piece of flat-bar that's had all the holes drilled and tapped; I can't tell you how much I'm looking forward to doing that.


The mount for the Z-axis will be attached at the near-end of the machine.  I still have to design and build that mount, as well as print out the parts for the third axis.

After assembly, I simply had to try it out, missing limit switches or not.  I hooked it up to my 3-axis controller board with the Pololu A4988 carrier board carriers. The Arduino is flashed with GRBL, and wired to the carrier boards, which are in turn wired to the steppers.  I need to find some extra heatsinks for the X and Y axes, along with some thermal tape or epoxy, but they seem to run cool enough for testing anyway.


Here is it running, the first time I've gotten one of these CNC projects using actual GCode.  The leadscrew for the Y-axis is a bit loose in the fixed-end bearings, causing the knocking sounds, I have to look into the alignment, but otherwise it's working pretty well.  Speed is about one inch per second.


Next up will be designing a mount for the Z-axis.  I'm quite happy with the current set of printed parts and will probably continue to use them.

Friday, August 10, 2012

Optical Tomography Setup

As noted in a previous post, my Stochastic Tomography paper was accepted to SIGGRAPH 2012.  Last Tuesday I was in Los Angeles for the conference to present the paper, including present the synopsis in the conference 'fast-forward' to an appallingly large audience.  The photo below shows the seating but during the actual event it was standing room only. 


A bit nerve-wracking to say the least.  However it went well and after presenting my main talk to a MUCH smaller crowd, I'd like to post some photos of the setup that we used for the paper.  I should point out that this project actually did not contribute much to the capture setup, this was previously in place from work done by Borislav Trifonov, Michael Krimerman, Brad Atcheson, Derek Bradley and a slew of others.  My work on this paper focused on the algorithms primarily, but I thought people might be interested in a quick overview of the tomography capture apparatus.

The goal of the paper was to build 3D animated models of mixing liquids from multiview video.  To accomplish this, we used an array of roughly 16 Hi-Def Sony camcorders arranged in a semi-circle around our capture volume to record video streams of the two fluids mixing.


You can see the cameras in the photo above, all focused on the capture volume which is inside the glass cylinder.  Each of these records a video of the mixing process, producing 16 streams of video that look more or less like the photo shown below:


You can see one of the cameras peeking out at the right side of the frame.  The cameras are controlled by an Arduino based box that talks to each camcorder using the SONY LANC protocol.  This is an unpublished protocol used by SONY editing consoles, however it has been reverse-engineered by others to allow people to control SONY equipment.  We implemented this protocol on an Arduino, which allows us to start all the cameras recording, turn them on and off as arrays, switch them between photo and video mode and so on.  Unfortunately we can't easily set the exposure levels, transfer files to and from the device, instead we have to painstakingly do this by hand through the on-camera menus, which is error-prone and time-consuming.

The two fluids we use are water, for the clear liquid, and Fluoroscein-Sodium fluorescing dye for the mixing liquid.  This fluorescent dye is available in a powder that is soluble in water, which allows us to perform several types of capture.  The image above is dye powder dropped onto the surface of the water, this mixes with the water and is slightly denser, forming the Rayleigh-Taylor mixing process you see in that shot.  We can also pre-mix the dye powder and simple pour or inject it into the domain, this was the process used for the following two captures that were used in the paper.



This shows an unstable vortex propaging downwards, leaving a complex wake.  I recommend watching in high-def (720p or 1080p).  The next is alcohol mixed with the dye powder, injected into the cylinder from the bottom.  Since alcohol is less dense than water, it rises under buoyancy, mixing as it goes.



In the video above you can see a laminar to turbulent transition as well as lots of complex eddies that form as part of the mixing process.

The captures are illuminated with a set of white LED concert strobe panels.  These panels serve two purposes. First they let us get LOTS of light into the scene in a controlled fashion.  Second we actually use a strobed illumination at about 30Hz to optically synchronize the cameras and remove rolling-shutted shear effects.

All captures start in darkness so we can tell the time offset from the start of the video to the first frame where there is significant illumination.  In fact we can do better than alignment to a single frame, since with the rolling shutter used by these cameras, we can actually determine the first scanline that is exposed.  Using a 30Hz illumination pattern, we can also determine the exposure setting of the camera by looking for the last scanline before the light goes off again.

We then have a rolling shutter compensation program that scans through each video and reassembles a new video from the exposed and dark scanlines.  The result is a set of videos that are optically synchronized and that have minimal shearing introduced.

This gives us a set of input data, however we also need to perform some geometric calibration of the scene in order to know from what angle each video was recorded and to be able to obtain the ray inside of the capture volume that corresponds to every observed pixel.

To do this, we use an optical calibration library called CalTag that detects self-identifying marker patterns similar to QR codes in images.  We print a calibration target on overheard transparencies and mount this pattern to a 3D printed calibration jig that is placed in the glass cylinder.


This jig fits tightly in the cylinder and is registered with a set of detents that fit into recesses in a registration plate that is glued to the inside of the capture cylinder.  The marker pattern that you see in the photo above is also registered to a set of registration tabs.  We have a calibrated pattern on the front of the target as shown above, but also on the back.

When a camera takes an image of this jig after placing it into the capture domain (filled with water), an image similar to the following is obtained, although generally with far less blur due to condensation.


CalTag can then give us the corners of the marker patterns, which can be interpolated to associate with every image pixel, the corresponding 3D point on the calibration target that it 'sees'.  We then rotate the target 180 degrees to face away from the camera and take a picture of an identical and carefully aligned target on the back side of the jig, giving another 3D point for each pixel.  Connecting the points gives a ray in 3D space inside the cylinder, without having to account for any optical interactions between the interior liquid and cylinder.

We do this for every camera, by mounting the capture domain on a rotation stage, which is again controlled by an Arduino.  An automated calibration procedure rotates the stage and triggers each camera to image the front calibration plane, then rotates an additional 180 degrees to repeat the process.  The whole mess is controlled by a python script using pySerial, including the strobes, the rotation stage and the embedded camera controller.

This gives us the needed calibration data to express our scene as a tomographic inverse problem.  Here we look for the scene content that would reproduce the measurements (videos) we obtained, given a physical model for the scene.  In this capture case, the scene is simply emissivity adding up along a ray-path, so we get a linear inverse problem, that we solve using our new Stochastic Tomography algorithm.  The result is volumetric 3D fields that you can animate, inspect and slice through and re-render however you like, as seen below in the submission video.



Stochastic Tomography and its Applications in 3D Imaging of Mixing Fluids from al. et Hullin et al. on Vimeo.

Sunday, July 15, 2012

Sample Code for atmega328p Serial Communication

Nothing special, just a bit of sample code for serial communications with the Atmega328p.  It took a bit of hunting to find code that worked properly on the 328.  The code is taken from https://sites.google.com/site/qeewiki/books/avr-guide/usart.  It worked for me with the Arduino serial console.



#include<avr/io.h>

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU/(USART_BAUDRATE*16UL)))-1)

int main(void){
 char recieved_byte;
 
 UCSR0B |= (1<<RXEN0)  | (1<<TXEN0);
 UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);
 UBRR0H  = (BAUD_PRESCALE >> 8);
 UBRR0L  = BAUD_PRESCALE;
 
    for(;;){
  // wait until a byte is ready to read
  while( ( UCSR0A & ( 1 << RXC0 ) ) == 0 ){}

  // grab the byte from the serial port
  recieved_byte = UDR0;
  
  // wait until the port is ready to be written to
  while( ( UCSR0A & ( 1 << UDRE0 ) ) == 0 ){}

  // write the byte to the serial port
  UDR0 = recieved_byte;
    }
    return 0;   /* never reached */
}

Makefile settings for this code were the following, uses a 16MHz external crystal:

DEVICE     = atmega328p
CLOCK      = 16000000
PROGRAMMER = -c dragon_isp -P usb
OBJECTS    = main.o
FUSES      = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m

Sunday, April 8, 2012

Low Cost CNC Machine Part III - 1 1/2 Half-Finished Translation Stages

A continuation of Parts I and II....

I went on a bit of a tear today, building another four of the CNC shaft mounts, plus printing two hex-nut mounts and another motor mount.  As a result I have one nearly completed stage that just needs end-stops added, plus another that's about halfway done and still needs bearing mounts and end-stops.

The newly created hex-nut mounts follow the trend of the shaft and bearing mounts, except that the frame size of the Keling NEMA23s that I'm using don't match that well with shaft centers at 1" height, so the threaded rod ends up a few millimeters above the linear shafts.  Consequently the nut has to be moved a little closer to the carriage to take up the difference.



Nothing really novel here, but having everything mount on standard hole-spacings with standard hardware really makes the design and assembly process easier.

With the hex-nuts in hand, I can now test the stages to see how they perform.  The assembled stage is shown below, with the half-finished stage partially assembled on the carriage.  The extra pillow-block bearings were the ones I'd bought from VXB, I will probably not actually end up using them.  Note that I'm really short on 2" 1/4-20 machine screws, so every mount only has one screw on the clamp side. 


I also tested out a simple anti-backlash nut idea.  It involves backing an additional nut onto a stack of rubber washers.  This is pretty cheap, with adjustable preload and surprisingly enough, seems to stay in place just by friction, although I think a proper mount should probably be designed.  A closeup is below:


For testing I'm using the standard Pololu A4988 drivers, driven by an Arduino Uno.  After some mishaps a few months ago, I fried two of my drivers so I'll have to pick up some new ones. In the photo below you can see the Arduino connected to the 4-axis driver board I've kludged together, connected to the stepper in bipolar parallel, which should give about 180 oz-in of torque.


Early tests showed that even unloaded, it was important to control acceleration of the motors to make sure they didn't skip steps.  I wrote a simple sketch that ramps the step rate up from a low rate where the motors can slam from forward to reverse without stuttering up to a much higher rate, about 2500 steps/sec with no microstepping.  The code is below:

// step pin is #3, direction is pin #2
void setup(){
 Serial.begin(115200); 
 pinMode(2, OUTPUT);
 pinMode(3, OUTPUT);
}

inline void advance_steps( int dir, int steps ){
  int increment  = 4;
  int delay_init = 1000;        // 1/2 initial step delay, in us
  int delay_final = 200;        // 1/2 final step delay, in us
  int delay_curr = delay_init;  // current delay
  
  // compute the end of the ramp up and the start of the ramp down
  int ramp_up   = min( steps/2, (delay_init-delay_final)/increment );
  int ramp_down = max( steps/2, steps-(delay_init-delay_final)/increment );
  
  // set the direction
  digitalWrite( 2, dir );
  
  // start stepping
  int i=0; // step counter
  
  // ramp up
  for( ; i<ramp_up; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );  
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );
    delay_curr-=increment;
  }
  
  // constant feed-rate
  for( ; i<ramp_down; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );  
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );    
  }
  
  // ramp down
  for( ; i<steps; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );
    delay_curr+=increment;
  }
}

void loop(){
  // move carriage one way
  advance_steps( LOW, 15000 );
  
  // then reverse
  advance_steps( HIGH, 15000 );
}

So I tried it out, and it worked! Moves the carriage back and forth like all-get-out.


Pretty cool to see something you've taken from initial idea to built prototype actually perform.  Doesn't hit the 100 mm/s feedrate that I'd like, but I didn't really expect that from the 1/4-20 rod.  With a proper leadscrew it should be no problem to get to that rate.  Yes, that is a T-Pain microphone on the chair in the background.

Next I wanted to see how loaded down the motors were.  To test this I added 16 pounds of freeweights.  It still ran smooth as silk, and after 15 minutes, the Pololu driver was only slightly warm.

I'm really pleased with how this turned out.  The stage is actually pretty quiet, much quieter than I'd expected.  On top of that, it seems to have power to spare even from a 12V supply.  Since the Pololu drivers are rated for up to 35V, I think I should be able to run it at 24V and get even faster feedrates. I haven't checked the accuracy yet, but if the 1/4-20 rod was perfect (Ha!) and there was no backlash (Ha!) then it would have a resolution of 6.4 um/step.  It might be wishful thinking, but I'm hoping to get it to within a few thousands of an inch (0.025-0.05mm).

Next steps are to finish the z-axis and install endstops.  Then I'll make the components for the y-axis and start thinking about a frame.