Thursday, January 28, 2016

Making 3D Parts in VCarve, a 2D Editor

In my quest to get certified to use the MultiCam CNC router table at the local Makerspace, I need to create some kind of part that requires use of at least a couple different types of end mills, plus do various cuts such as pockets, profiles, V-carves, engraving, and so on.

First, a bit about the Makerspace movement, in case you haven’t heard: Makerspaces (or Hackerspaces) are places set up for community good that allow people to share knowledge, resources, and tools to help further DIY activities such as programming, electronics, 3D printing, machining, or other activities where people make something.  Makerspaces come in various flavors: some are set up as startup incubators, others as for-profit centers where paid employees build things for people, and still others where members mostly come to work on something completely different from work.  The tools one would find at a makerspace were traditionally owned by companies or by individuals who have spent a long time honing their craft; as such, they are left for very few people to use either in someone’s garage or during working hours.  Through the power of crowdsourcing money from makerspace members through dues and/or fundraising drives, makerspaces can afford tools to share with the community (that is, anyone willing to become a member) and offer training around getting the most out of these tools, not to mention proper use and safety.  People who live in small apartments or are otherwise constrained for space or don’t have thousands of dollars for tools now have access to all sorts of tools that would be impractical for them to own outright.  This attracts talent and people with ideas who often form groups that can do much more than any one individual can on their own, though there are still lots of individual projects happening at makerspaces as well.

Tabling VCarve for the moment


Our MultiCam 3000 CNC router table, like most other CNC milling machines, 3D printers, and other such devices, requires information sent to it in the form of G-Code.  This flavor of code specifies things like feed rate, spindle speed, and tool position to the machine so it will mill out (or extrude plastic or etc.) in the location you want at the time it needs to be there, hopefully without breaking the tool from too much stress or damaging the machine.  The toolchain we use at the Makerspace to produce the required G-code for the table mill involves use of a program called VCarve.  It is a nice program that allows you to design your part and produce G-code to run on the machine to make the part.

VCarve is great for designing fairly simple projects.  This can take you a long way, because what is “simple” in the world of milling can often yield astonishingly detailed and fantastic results, usually by use of the actual operation called “V Carve” (which of course the program VCarve can help you do).  Even a pinball playfield could count as a simple part using this metric.  However, the part I want to make for my test is essentially a replica of the classic Nintendo game controller for the NES, which involves several contoured buttons.  Look closely at the controller, and you will see that the A and B buttons are slightly concave so as to cradle your fingertip nicely.  The D-pad has directional arrows imprinted into the plastic and a hemisphere carved out of the center, not to mention each direction bends slightly upward out of the center to give you nice tactile feedback for exactly where to press to move in that direction.  After trying hard to make these types of 3D cuts (which VCarve doesn’t support inherently) using ramps and other effects, I temporarily gave up on VCarve.

Ok, so what else is there to make the 3D shape?


Previous versions of VCarve prior to 8.0 don’t support any 3D objects whatsoever.  Luckily, my Makerspace has VCarve Pro V8 available for us to use.  With its license, I am able to upload one STL file or bitmap image for it to translate into G-code.  I created the contoured buttons using Blender in three simple steps:
  • Use negative Boolean operations to subtract a large sphere from a small cylinder to create the slight contour of the A & B buttons (then import this button into VCarve and add two instances of it)
  • Use transformations to slightly elevate desired edges of basic cubes to make the contoured D-pad shape
  • Use transformations on cubes to create arrows, and then negative Boolean operations to subtract the arrows from the D-pad shape
While on the topic of Blender, here are two other quick hints about it:
  • When doing a negative Boolean operation, the negative shape does not immediately disappear (unlike other 3D rendering environments I’ve worked with).  You have to move the negative shape out of the way in order to see the imprint it made into the positive shape.  Otherwise, you’ll think the negative Boolean operation is not working, attempt to upgrade Blender to the latest version, and find that doesn’t even help either.
    • When exporting to STL format, you need to have all the desired elements of your object selected while in Object Mode.  Otherwise, Blender won’t export anything in your STL file and VCarve will complain that your STL file is invalid because it contains nothing.

    Bringing It All Back In


    [Edited 6/3/16]

    Once you have made the STL files with Blender, import them into VCarve by going to “Model” -> “Import Component / 3D Model".  Remember that with certain license grades, you might only be able to import one STL file or bitmap per part.   It's not hard to get around this:

    • If all you need are objects imported from Blender (or whatever 3D program you use), then just merge all the objects into a single STL file.
    • Otherwise, make n files to describe the same part, but only import one STL or bitmap into each file.  Place the imported 3D object in the desired location on the part by itself.  Generate the needed G-code outputs for each file, and then either merge the G-code files by hand or just have the machine run all the G-code files sequentially, possibly without even changing the tool.
    Anyway, once you import an STL file, VCarve will give you several options regarding how to treat it.  The most interesting ones to me are:
    • Model Size: Allows you to scale your model if you used the wrong units in Blender or want to make a last-minute adjustment.
    • Zero Plane Position in Model Top: This specifies where the origin is placed with respect to your 3D model.  You can put the zero plane at the top of your model so that all drill depths are negative with respect to the zero plane (just make sure to un-check the checkbox to discard anything below the zero plane).  You can also put it at the bottom of your model, or anywhere in between.
    Once you confirm your selections by clicking OK, it should switch you right away to the Modeling tab.  Double-click on the item you just imported in the Component Tree.  Now, pay close attention to two options:
    • Shape Height: This gives you another opportunity to scale the Z-height of your 3D model in case you think it needs to be altered.  Experimenting with this before your "final cut" could help you optimize the "look and feel" of your 3D part.
    • Base Height: This sets how far from the base of the material the part will be milled from.  This is where your Zero Plane position comes into play.  If your zero plane position is at the top of the model, you can leave the Base Height at zero and the top of the model will be flush with the top of the material.  If your zero plane position is at the bottom of the model, then the Base Height must be set to (Material Height - Model Height) in order for the top of the model to be flush with the top of the material.  Otherwise, the model will be cut too low or too high, causing the mill to do extra work or to not cut everything you expected it to.
    For instance, I imported the NES buttons with the zero plane at the very bottom of the model.  With a material height of 19.7 mm and a model height of 12 mm, I had to specify the base height as 7.7 mm in order to make the edge of the buttons exactly flush with the top of the material so that no extra material would be cut away and that none of the details would be chopped off by being above the top of the material.  Now if you want shallower buttons (or a shorter part in general), feel free to set the base height to something less.  In retrospect, though, I probably could have imported them with the zero plane at the top, un-checked the checkbox, and then not worried about any math other than not setting the shape height more than 19.7 mm.

    With your 3D object in the right place with its desired specifications, you can now treat it like it’s another vector object.  Since I merged a lot of things together into one STL file, I first made vectors describing the outlines of where the 3D parts should go in order to line up the 3D object exactly as I wanted it.  You can either use these same vectors in your pockets and profiles if you need to cut out/around the 3D parts, or you can delete these and create new vectors from the actual 3D model if needed.  This way, you are guaranteed that the vectors will truly follow the 3D part.  How to do this is mentioned below.

    IMPORTANT SAFETY NOTE: Before making a 3D cut, slow down the feed rate on the machine's control panel because it will plunge to cut the deepest part first and will not do any pocketing beforehand.  The first time I attempted to cut a 3D part, the tool plunged so deep and moved around so forcefully that, despite the table vacuum being run, the part still moved around the table quite a bit.  It seemed dangerous and could have resulted in a broken tool.

    It is important to adjust the feed rate on the machine itself if you can, and not in the G-code.  If you set the feed rate too low in the G-code, it might seem tolerable while you're watching the deep cuts get made, but watching the machine cut the shallower sections of the part will be very tedious and boring.

    Two things you might want to do with your imported 3D object:
    • To actually mill the shape you imported, you need to select the 3D object in the drawing tab and then select the “3D Finishing Toolpath" operation in the Toolbox at right.  Remember to mind your feed speed as discussed above so that the tool doesn't just shake around your material and mess up the cut, or (worse yet) break itself.
    • You might want to profile this shape (i.e. cut material around it so it exists by itself) as well.  If so, select it, create a vector of its outline by going to the “Modeling” tab at left, selecting the desired instance of your 3D model, and clicking the “Create vector boundary from selected components” button under “3D Model Tools".  Then, with that vector selected, select the “Profile” operation in the Toolbox at right.  Finally, you can describe all the desired parameters of your Profile operation as usual.  Remember to do Profile last; otherwise, you would be cutting into a piece that is now mostly disconnected from other material and it could possibly come loose.

    Notice: These instructions are based on my observations and are not a substitute for proper education on the VCarve program or using a milling machine.  I cannot be held liable for any damage to materials, tools, or personal injury.  It is always up to you to properly verify that the instructions you give to the machine will produce the expected output before you begin milling.

    What kind of 3D models did you import into VCarve and then have milled?  Take a moment to show them off here!

    Thursday, January 7, 2016

    Interrupts for the Arduino Uno - More than you might think!

    Are you looking to make a program that requires a bunch of interrupts using an ATmega328 or ATmega168 chip (such as on the Arduino Uno, Nano, or Mini platforms)?  If so, you may have been disappointed by the basic documentation you can find on this matter, and tempted to buy a more advanced Arduino such as the Mega2560, Zero, or even Due.  But have you seen how much their chips cost on Mouser?  If you're looking to do a small run of boards with what you ultimately produce, you will be taken aback to find that ATmega2560-16AU chips cost over 5x more than ATmega168A-AU chips!  In fact, I just recently bought an Arduino Mega2560 for less than what you can buy its chip for.  Having seen this, I knew there had to be a better way to leverage a cheaper chip.

    The Problem, In Short


    I need an application that can read from multiple sensor inputs, each of which will pulse with a high-frequency square wave (i.e. toggle on & off very rapidly) upon activation of the particular magnetic device they sit next to.

    Given the sensors' behavior, and that there's no guarantee each sensor will produce more than one pulse if the underlying magnetic device does not stay on for a very long time, the best way to look for the changing state of the sensors is to use interrupts that can quickly capture exactly which pin just changed state, and then during the CPU's downtime (when it's not handling interrupts), it can go and take the appropriate action given which sensor(s) just pulsed.

    Another problem to battle on the road to solving my problem!


    By reading the standard documentation, you might be led to believe the Arduino Uno or any other ATmega328-based platform only has two useful interrupts, existing on digital pins 2 & 3.  Specifically, it says that the attachInterrupt() function only works on those two pins.  This, however, is misleading.  In fact, any of the ATmega's I/O pins can be used as interrupts -- it only really makes a difference if you need to use an external interrupt versus simply a pin change interrupt.

    An external interrupt has the capability to fire upon a state transition, such as the rising edge or falling edge of a signal.  It can also be triggered upon the change of value of an input pin, and by the signal going to logic level low.  Since external interrupts on pins 2 & 3 of the ATmega328 have different interrupt vectors (addresses where the routines related to these interrupts are stored), such interrupts on these pins are distinguishable from each other, and also distinguishable from pin change interrupts you might be listening for on those pins as well.  External interrupts also have a higher priority compared to pin change interrupts, so they will be processed first.

    pin change interrupt happens when the value of a pin suddenly changes state from low to high or vice versa.  The interrupt does not tell you exactly what the new value is (and the value is subject to change again by the time the interrupt can be processed), and as you will read below, pin change interrupts tend to share just a few vectors, so you might need a way to reconcile exactly who caused the interrupt.

    Since my application only really needs to know about pin changes, especially since there's a small probability these sensors might get stuck High instead of being pulled back to Low upon the end of the magnetic pulse, I can leverage any and all of the input pins for my purpose.

    Nota Bene: For my particular sensor, it drives High given one magnetic polarity, Low given the other polarity, and can become Undefined in the absence of the magnetic field.  I thought the sensor would pulse on its own each time with no further action from me, but the pulses did not actually appear until I used a pullup resistor on the input pin.  This is easily achieved in Arduino-land by supplanting INPUT with INPUT_PULLUP, as such:

    pinMode(p, INPUT_PULLUP);

    Once this was done, I was ready to move on. However, I faced yet another problem: the documentation would lead you to believe there are only three interrupt vectors you can use across all the pins. Here's what the Arduino Playground says about the topic:

    • ISR (PCINT0_vect) pin change interrupt for D8 to D13
    • ISR (PCINT1_vect) pin change interrupt for A0 to A5
    • ISR (PCINT2_vect) pin change interrupt for D0 to D7
    Unfortunately, in my application, I need to read from at least four sensors.  What can I do?

    The Shining Light


    The Arduino documentation suggests using libraries, but links to a very scant piece of code with little documentation.  A quick Google search for this, though, yielded me a much more up-to-date and comprehensive solution: the EnableInterrupt library.  I suspected there would be an answer in here as to how to reconcile exactly which pin fired the interrupt, and sure enough, I wasn't disappointed.  It just looked a little bit different than I expected:

    // These two statements must be written in this exact order!
    #define EI_ARDUINO_INTERRUPTED_PIN
    #include <EnableInterrupt.h>

    // It's OK to use 0
    // since the library doesn't really support interrupts on pin 0
    // because of the implications for serial TX/RX
    volatile uint8_t pinChangeIndex = 0;

    void interruptFunction() {
      pinChangeIndex = arduinoInterruptedPin;
    }

    void setup() {
      // put this in a loop, perhaps, to initialize more than just pin "p"
      pinMode(p, INPUT_PULLUP);
      // ...
    }

    Simple, right?

    It turns out that the ATmega has an internal register storing the index of exactly what pin toggled the interrupt, and this library exposes that for your consumption.  Now in your loop() function, all you need to do is branch off (i.e. write an if statement utilizing) the pinChangeIndex variable, and you don't have to process any of the application logic in the interrupt at all.  If you want to listen for multiple devices at once, it's possible to replace the uint8_t with a bool[] array and then replace the interrupt function's contents with pinChanged[arduinoInterruptedPin] = true

    Incidentally, the volatile keyword in this context is used as a helper to the compiler so that it doesn't assume any code dealing with use of the pinChangeIndex variable is anywhere near where it might get changed.  This way, the variable's value will always be copied into one of the microcontroller's registers right before any comparison or operation is done on it, such as branching to particular spots depending on the variable's value.  The register will never contain an old copy of what the variable contained a long time ago.

    May your project dreams be more attainable by unlocking cheaper microcontrollers to handle many I/O devices!