Introduction: Rocket Propellant Characterization, C++ Excel Automation, Boost Property Tree and the TC Logger

Picture of Rocket Propellant Characterization, C++ Excel Automation, Boost Property Tree and the TC Logger

Yes, all of that in one Instructable... Sort of. Covering the science and processes behind rocket propellant characterization would take more than one Instructable. And perhaps in the future I will release a few more Instructables showing the how-to of building a test stand, building rocket motor casings for testing, building "nozzles" for testing, and maybe someday even the how to of making Ammonium Perchlorate Composite Propellant. But not now -- for this Instructable we will focus on a task that is immediately useful to those who already own much of the equipment and have some experience with testing High Power Rocket Motors.

For everyone else, this Instructable will still be useful if you would like to see examples of how to automate Excel from C++, or parse XML files using Boost Property Trees, or even see a cutting edge C++ class from proposal N4189 used by Microsoft Visual Studio 2013. (N4198 is a proposal currently running through the ISO C++ Committee process for inclusion in the Standard Library)

We can't start talking about Rocket Propellant Categorization though without giving you some ideas of what that means... In the video below you will see a single motor test. This test was repeated 5 more times all with different sized "apertures" or "nozzles." Each test motor was identical -- that is the propellant "grain" was 3.0625 inches long and weighed the same as the grain from every other test of that new propellant. The only difference in each test was the size of the throat in our test "nozzles." To eliminate the nozzle coefficient from the math needed to characterize the propellant, our test nozzles do not have a divergent cone. The idea behind "categorizing" a propellant is to determine its burn rate at various pressures. Each test with a different nozzle throat changes the pressure. Eventually we will get a trend line from the pressure values we've obtained that will allow us to know how the propellant will perform when the motor is "scaled up." We also will calculate ISP values that are needed for scaling up motors and determining how a propellant will perform with different grain configurations.

For our tests we elected to use 54mm single grain rocket motors. This is relatively small for High Power rocketry, but it keeps the costs associated with characterizing a propellant down. Our testing shows that single grain tests work as well if not better than multiple grain tests.

But I'm getting ahead of myself. Let's take a look at a test burn. Then I will provide some pointers to those interested in getting involved for the first time in the High Power Rocketry hobby including eventually testing your own propellants...

Step 1: Rocket Motor Test Burns...

The video shown here was one of 6 burns of a new propellant. The goal was to test an old propellant with a new Burn Rate Catalyst. The base propellant (called Vee-Oh-Lay and identified as VOL) was created by Raymond Kinsel many years ago. It is used by many "research" rocketeers throughout the hobby. It is favored for being relatively low pressure but high performance. Ray, myself, and others have run VOL from small motors (54mm and smaller) up to very large motors. Personally I've flown rockets with "M" motors using VOL, but others have gone as high a "P" motors. By changing the burn rate catalyst Ray was able to increase the performance of VOL. But please don't ask for the formula! That is Ray's and while he is very good about sharing his formulas, if you are new to High Power Rocketry there are things you need to do first!

Step 2: Getting Involved in High Power Rocketry

Picture of Getting Involved in High Power Rocketry

If you are interested in getting involved in High Power Rocketry it is important that you understand a few things. Let's cover the good first.

High Power Rockery, in my opinion, is the Ultimate Maker Sport! You can start with building rockets from kits and buying motors, and move up from there. In these images you see a rocketry that I built from scratch. Some parts were machined on a metal lathe, some purchased at a hardware store (such as the airframe which unlike most High Power Rockets was made from PVC instead of fiberglass or carbon fiber.) The parachute was sewn at home, and the rocket motor was machined on my lathe. The rocket propellant was also made be me. Since this rocket was flown, I've also learned to make my own nozzles (from graphite rods), and have experimented with some of my own flight electronics. The point is that there is a whole world of "making" to get involved with in High Power Rocketry! And if that doesn't sell you, the math, the science, the physics, the geometry (especially in motor making), the chemistry, or maybe just the heart pounding thrill of hearing the motor roar will!

To get involved in High Power rocketry you *must* join either the National Association of Rocketry (NAR) or the Tripoli Rocketry Association! There are laws that govern High Power Rocketry and these organizations not only understand them, but have some respect with the authorities having jurisdiction in many countries! DON'T GO IT ALONE! You will probably end up in serious trouble!

If you decide you eventually want to build your own rocket motors, you need to join the Tripoli Rocketry Association. Only Tripoli allows "EX" or "Research" rocket motors at their insured launches!

Once a member of the national (actually international) Tripoli organization, join a local Tripoli prefecture (or NAR if that is your preference.) The local members will prove to be some of the most knowledgeable and helpful people you have ever met. They have a passion for science, learning, and rocketry! They value safety! This is important. We live in a day and time when many people are willing to yield up their personal liberties any time there is an accident. Many "dangerous" things end up being outlawed. To keep rocketry legal we need to follow all of the safety rules! Tripoli and NAR safety rules are governed by NFPA (Nation Fire Protection Association) regulations. These regulations keep us safe in this sport, and that safety is what makes it possible for us to have cooperation from government entities such as the BATFE (Bureau of Alcohol, Tobacco, Firearms, and Explosives) and the FAA (Federal Aviation Administration) from whom we obtain waivers allowing us to fly rockets in the same airspace where our airplanes need to fly! PLEASE abide by the rules and let us all retain the liberty to learn and experience science first hand through the hobby of High Power Rocketry!

After joining Tripoli, a Level-2 certification is required to begin building and flying "EX" (experimental) or "research" rocket motors. There are obvious dangers involved with building your own motors. Make sure you work with experienced hobbyists. Don't jump in blindly. There are people willing to teach you and help you to get it right and to avoid some of their mistakes! Again, follow the rules and be safe! Research rocket motors are not for the timid, the careless, or the thoughtless. If you fall into one of those categories, just move on to something else! You must take personal responsibility and remember that your actions not only impact the safety of those around you, but also have the ability to damage the reputation of all others in the hobby and potential worse. Be SAFE! <End of Lecture!>

Step 3: Characterizing Rocket Propellant: What Is Needed for Testing...

Picture of Characterizing Rocket Propellant: What Is Needed for Testing...

There are many ways of collecting the data needed to characterize rocket propellant. In this Instructable we are utilizing data collected by a TC Logger. TC Logger is a data collection device made and sold by NASSA, the Nevada Aerospace Science Associates. Visit the TC Logger website for details on their data acquisition hardware and software.

In addition to data collection a test stand is needed along with a load cell for measuring thrust and a pressure transducer for measuring case pressure. These last two are also available in a kit from the TC Logger website. You will need to buy or build a test stand. In the future I plan to release an Instructable illustrating how I built my test stand (see the photos).

You will also need rocket motor casings designed for testing. The motors I made (see the photos) use graphite nozzles that have a convergent section (90 degree) but no divergent cone. They are numbered according to the size of the throat in 64ths of an inch. (e.g. a #25 has a 1/4" throat.) The forward closures have a 7/16" hole all the way though with 1/4" NPT threads tapped on the outward facing side. They are 54mm cases that work with standard 54mm casting tubes. The cases are made from 2" Schedule 80 6061-T6 Aluminum pipe. As you can see in the photos snap ring grooves are cut to retain the nozzle and forward closure just as with a standard 54mm motor (like one made by Gorilla or AMW.) They are designed to hold a single 3" 54mm BATES grain, with liner overlap on the nozzle (or in some cases nozzle carrier) and on the forward closure.

Follow the directions on the TC Logger website for using their acquisition package and software. The files collected by the TC Logger software are XML files that the rest of this Instructable will utilize. Their ProPEP software can also read these files to produce categorization details, but we have found better results using Excel and being able to adjust parameters with finer control.

The following suggestions differ from what you will find in the TC Logger manual:

  1. Use single BATES grains for testing rather than the 2 or more grain setup the manual recommends
  2. Don't use the pressure hose that they recommend! Instead, use short solid brass tubing that keeps the pressure transducer close to the motor! The dead space can cause the pressure readings to fall behind the thrust readings. (See the photo of our pressure transducer and fittings.)
  3. Use fresh Teflon tape to seal connections between the transducer and the motor after each burn.
  4. Use a syringe to fill the pressure transducer and brass fittings with high-temperature grease. The syringe is to keep air bubbles out of the line and grease. Check the grease after each burn. Usually some burned grease needs to be removed and refilled after every 2nd burn. Care with this will keep the readings usable.
  5. Use neutral BATES grains (like the manual says) but don't stress over getting a perfectly square curve, and generally speaking don't use the TC Logger program to select averages. (On my PC it causes the shift key to be stuck down.) I use that only if I need to remove an ignition anomaly because of too much pressure from the ignition pellet.
  6. A pyrodex pellet is useful for ignition by e-match or initiator. Other pyrogens work well too but will not be discussed in this Instructable.
  7. Develop a system for naming files with burn data. We use the following system: The first two letters are the initials of the person who made the propellant. The next letters are the short name of the propellant. Then and underscore is followed by the "nozzle" number (e.g. #16 is 25/65" or 1/4".)
  8. Begin tests with the largest nozzle throat and work downward. Keep an eye on pressure to decide if you really want to test smaller nozzles. In our tests we use throats ranging from #12 (12/64") to #21 (21/64").
  9. A minimum of three good files are needed to generate a & n numbers needed by BurnSim (etc.) I recommend 5 or 6.

Step 4: Characterizing Propellant...

Picture of Characterizing Propellant...

Once a set of three or more files have been captured by the TC Logger software, use TCLogger2Excel to open the files in Microsoft Excel and to generate a & n numbers and an average delivered ISP.

The remainder of the article will focus on the TCLogger2Excel software code itself. If you are only interested in using the software, and not in modifying it or learning from it you can obtain a pre-built copy of TCLogger2Excel.exe from the following link on github: TCLogger2Excel.exe

Simply run TCLogger2Excel.exe and enter the requested parameters. A brief explanation of each follows:

  1. Pmax Threshold - This is the percentage of the Maximum Case Pressure (Pc) used to determine burn time. Sutton (See figure 12.13 "Definition of burning time and action time") recommends 10% of Pmax for the "Action Time." Sutton further recommends using the "aft tangent bisector" to mark the end of the burn. We have found that with our small motors simply using 10% Pmax works most of the time.
    A link to the image from the Sutton book is available here:

    https://dallapiazza.files.wordpress.com/2013/10/figure-11-13-definitions-of-burning-time-and-action-time.jpg

  2. Grain Weight in grams - Weigh each grain prior to motor assembly. Put the average weight here. Later, on the worksheet for each burn, you can update the individual grain weight value for more accurate ISP results.
  3. Grain Length in inches - For a neutral, single 54mm grain this should be close to 3 inches if the core is 5/8". Adjust as needed for your purposes.
  4. Grain Diameter in inches - This is the outside diameter of the grain, or the inside diameter of the casting tube.
  5. Grain Core diameter in inches - This is the diameter of the core. We use 5/8" for all of our tests.
  6. Casting Tube Weight - This is the weight of the casting tube in grams per inch. This weight is subtracted from the Grain Weight for determining ISP.

Once these parameters are set, click on Start to select files. After files are selected the data will be imported into Microsoft Excel (2010 or newer). Each file will have its own tab and it's own thrust and pressure chart showing the Pmax line selected in #1 above. If there are 3 or more files an a&n worksheet is generated with the burn rate graph and trend line, and displaying the Burn Rate Coefficient (a), Burn Rate Exponent (n), and average delivered ISP.

NOTE: The values entered above will be saved in the registry and used as the defaults for the next run.

Step 5: Building TCLogger2Excel in Microsoft Visual Studio 2013...

Picture of Building TCLogger2Excel in Microsoft Visual Studio 2013...

To build TCLogger2Excel the following are required:

  1. Microsoft Visual Studio 2013 or newer
  2. Boost 1.57.0 or newer (older versions of boost *may* work as well)
  3. A Git client

The source code is hosted on GitHub. Using a Git command-line client you can obtain the source by cloning as follows:

git clone https://github.com/alsliahona/TCLogger2Excel.git 

Next download the Boost 1.57.0 zip file and unzip it in the same top-level folder where the TCLogger2Excel folder exists.

You can then open the TCLogger2Excel solution and build the project. Note that if you use a different version of Boost you will need to change the project settings to point to the new name.

Remember that if you don't plan to change source code, you can simply download TCLogger2Excel.exe using the link in the previous step.

Step 6: C++ Automation of Excel...

Picture of C++ Automation of Excel...

TCLogger2Excel started off as a Windows command-line application but was converted to an MFC Dialog Based application in order to add the dialog that sets the default values in the variables section of each Excel worksheet. The advantage of using MFC is that it is easy! It is not a perfect UI library, but it is very easy to use. I will not cover the MFC code in this Instructable but instead will focus on explaining key parts of the code that may be useful when writing other programs. The first such area is How to Automate Excel from C++.

First a few words about both choices here, C++ and Excel.

Excel was selected because it is a powerful tool for manipulating and displaying data. Excel graphs make our thrust and pressure curves visually understandable. The Excel formulas on the a & n tab cover math that is far beyond my comfort level. By taking the TC Logger recorded data and putting it into Excel the user can now manipulate it and perform countless calculations and operations.

C++ on the other hand is my programming language of choice! I'll not bore you with a long monologue on its virtues or the faults of other programming languages. I will just say that as a professional programmer for several decades, and on many platforms, C++ provides the most power at the greatest convenience. With it I can not only generate optimized native code, but with modern C++ I can use the powerful features of the C++ Standard Library that rival those in any other programming language, in terms of power, understandability, and ease of use.

Automating Excel from C++

COM Automation makes it easy to automate Microsoft Excel. To start you need to import the Excel type libraries as shown below (from ExcelAutomation.cpp):

#import "libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52" \
rename("RGB", "MSORGB")\
rename("DocumentProperties", "MSODocumentProperties")
using namespace Office;
#import "libid:0002E157-0000-0000-C000-000000000046"
using namespace VBIDE;
#import "libid:00020813-0000-0000-C000-000000000046" \
rename("DialogBox", "ExcelDialogBox") \
rename("RGB", "ExcelRGB") \
rename("CopyFile", "ExcelCopyFile") \
rename("ReplaceText", "ExcelReplaceText") \
no_auto_exclude

This will cause the compiler to generate header files (EXCEL.tlh and MSO.tlh) and implementation files (EXCEL.tli and MSO.tli) that simplify the use of the COM automation objects we will use. If you open the .tlh files you will find a wealth of information that will be useful as you perform various tasks.

Next we need to create an instance of the Excel application object. The following code is taken from the CreateExcelSpreadsheet function in ExcelAutomation.cpp...

The first block sets up the COM environment and using N4189's scope_exit class, tears it down when aShutdown goes out of scope at the end of the function:

CoInitialize(nullptr);
auto aShutdown = std::experimental::make_scope_exit([]() ->void
{
CoUninitialize();
});

The next block will see if the registry on this machine has an entry for Excel. It is not a guarantee that Excel is installed but it is a good start:

//
// Make sure Excel is present...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);
if(FAILED(hr))
{
MessageBoxW(HWND_TOP, L"Unable to find Excel.Application's CLSID.", L"Excel unavailable...", MB_TOPMOST | MB_ICONERROR);
return;
}

Now we create an instance of the Excel object:

Excel::_ApplicationPtr pXLApp;
hr = pXLApp.CreateInstance(__uuidof(Excel::Application));
if(FAILED(hr))
{
MessageBoxW(HWND_TOP, L"Unable to connect with Excel.", L"Excel unavailable...", MB_TOPMOST | MB_ICONERROR);
return;
}

With this setup in place we can now create worksheets, populate cells, create graphs, and a whole lot more. To illustrate this we will next make the application visible, create a workbook, and maximize the application window.

// Make Excel visible. (i.e. Application.Visible = 0)
pXLApp->Visible[0] = VARIANT_TRUE;
// Create a new Excel Workbook. (i.e. Application.Workbooks.Add)
Excel::WorkbooksPtr pXLBooks = pXLApp->Workbooks;
_ASSERT(pXLBooks);
if(nullptr == pXLBooks)
{
return;
}
Excel::_WorkbookPtr pXLBook = pXLBooks->Add();
_ASSERT(pXLBook);
if(nullptr == pXLBook)
{
return;
}
pXLApp->ActiveWindow->PutWindowState(Excel::xlMaximized);

pXLBook is the Excel Workbook object we will use through the remainder of the program.

The (poorly named) function ProduceExcelWorkbook in ExcelAutomation.cpp will always work with the currently active Excel Worksheet:

// Get the active Worksheet and set its name.
Excel::_WorksheetPtr pXLSheet = pXLBook->ActiveSheet;
_ASSERT(pXLSheet);
if(nullptr == pXLSheet)
{
return false;
}

We can give the worksheet a name with code like this:

//
// Convert name to a wide string and assign it to the sheet
std::wstring wstrTabName(wstrFilename);
_bstr_t bstrTabName(SysAllocString(wstrTabName.c_str()));
pXLSheet->Name = bstrTabName;

It important to note that the Excel objects with a name ending in Ptr such as Excel::_WorksheetPtr are RAII C++ objects similar to those offered by N4189. _bstr_t is also an RAII object. In short that means that the memory behind these objects will automatically be released when the variable leaves scope, even if that happens because of an exception. (This last is a concept many C/C++ programmers forget about! It is very important to use RAII or to otherwise ensure object clean-up when an exception unwinds! If you lock a CRITICAL_SECTION for example using EnterCriticalSection but fail to LeaveCriticalSection when an exception causes your code to unwind -- maybe even an exception somewhere in a library you call, you will leak the lock and it will be nearly impossible to detect even under the debugger. You will end up with a deadlock that makes no sense! Use RAII objects! The only alternative is to use try/finally blocks for every resource!)

Later when we need a new worksheet the following code will create it:

//
// Create a new worksheet
Excel::SheetsPtr pWorksheets = pXLBook->Worksheets;
pWorksheets->Add(); // Becomes the active sheet...

With an active worksheet, data can be placed in a cell using a function like this one from ExcelAutomation.cpp:

void PlaceStringInCell(
     Excel::_WorksheetPtr &pXLSheet, 
     wchar_t wcColumn, 
     int row, 
     const std::wstring &wstrString)
{
std::wstring wstrRange;
wstrRange += wcColumn;
wstrRange += std::to_wstring(row);
Excel::RangePtr pDataRange = pXLSheet->Range[_variant_t(wstrRange.c_str())];
_ASSERT(pDataRange);
if(nullptr == pDataRange)
{
return;
}
pDataRange->Value2 = _bstr_t(wstrString.c_str());
}

Notice that a "Cell Range" (pDataRange) is created and then assigned a string value. You can also prepare a SAFEARRAY of data to assign to a cell range. To see how this is done look at ExcelAutomation.cpp lines 270 - 397.

ExcelAutomation.cpp also shows how to add a chart to a worksheet (2 different examples), customize chart labels, add a trend line, color cells, and change label font sizes.

One final tip... If you create an macro in Excel and then edit it, you will see Visual Basic code that is very easy to convert to C++ using the examples provided in ExcelAutomation.cpp.

Step 7: Reading XML With Boost::property_tree...

Picture of Reading XML With Boost::property_tree...

There are many libraries that can be used to read XML files. Most of them are heavy and complex. Boost property_tree however is very easy to use and consists of a header-file only implementation.

The TC Logger data files are in an XML (or XML-like) format. (See the sample files included with the code in github, as well as the image attached to this step.)

To use boost::property_tree to read XML files start by including the following header files:

#include <iostream>
#include "boost/property_tree/xml_parser.hpp"
#include "boost/property_tree/ptree.hpp"

To open an XML file and read the data, only a few more lines are needed as shown in the following code from ExcelAutomation.cpp:

boost::property_tree::ptree tree;
std::filebuf file;
if(false == file.open(wstrFile.c_str(), std::ios::in))
{
continue;
}
std::istream inputStream(&file);
boost::property_tree::xml_parser::read_xml(inputStream, tree);

That's easy! Now to read entries from the property_tree is just as easy. The following example will read a double value:

double dExitDiameter = tree.get("Document.MotorData.ExitDiameter", 0.0f);

If the entry was not found, the default value of 0.0 will be returned. In our case ExitDiameter is read from the MTD1 file which looks like this:

<MotorData>
<Propellant>ASBLUE4_18</Propellant>
<CaseDiameter>54 mm</CaseDiameter>
<NozzleThroatDiameter>0.28125</NozzleThroatDiameter>
<ExitDiameter>0.28125</ExitDiameter>
<Grains>1</Grains>

Strings can be read from the XML file using code like this:

const std::string strGrains(tree.get("Document.MotorData.Grains", "0"));
const std::string strCaseDiameter(tree.get("Document.MotorData.CaseDiameter", "0 mm"));
const std::string strNozzleThroat(tree.get("Document.MotorData.NozzleThroatDiameter", "0"));

Note that the boost documentation provides details on how to use property_tree. For example, if default values are not provided an exception will be thrown if the entry does not exist.

For more examples, view the rest of ExcelAutomation.cpp in the TCLogger2Excel code base, or visit the boost documentation page.

Step 8: Conclusion

Picture of Conclusion

The sport of High Power Rocketry is fantastic for makers like myself. Whether you are writing code to process data files from a TC Logger, building a rocket from scratch, or simply building your first kit there is a whole world of learning and fun.

I hope the coding examples here will be helpful even if you don't have an interest in rocketry. The next time you find yourself in need of automating Excel, or reading an XML file, or even just needing to know how to automatically clean-up resources (see comments on N4189) you can return to this Instructable!

In the meantime, as my mentor in High Power Rocketry always says, "Fly 'em high!"

Comments

VendicarD (author)2015-05-16

How can I make a UFO from this?

Robin- (author)2015-05-15

As betwys1 says, the math is strange. 16/64 equals 1/4 and 25/100 equals 1/4. Did you mean hundredths of an inch? That would make much more sense than the antiquated fractional system. As a machinist I think in terms of thousandths of an inch, which are called mils. I can believe that 5 or 10 mils would make a measurable difference in thrust.

Your first video doesn't seem to have sound and is confusing. Perhaps you could add a commentary to explain what you are doing. Also, perhaps you could graph a comparison of nozzle size to the other factors: thrust; burn time; etc.

als_liahona (author)Robin-2015-05-15

No, it is 64ths, I just wasn't thinking straight when I put in #25. If you notice elsewhere I indicated that we have been using #12 through #21 for our tests. I probably was thinking of of 1/4 being .25" when I put #25 instead of #16. It was late. :)

betwys1 (author)2015-05-15

Step 1 title for video clip misspells aperture

Step 3 "graphite nozzles that have a convergent section (90 degree) but no divergent cone. They are numbered according to the size of the throat in 64ths of an inch. (e.g. a #25 has a 1/4" throat.)" That's bigger than 3/8 inch.

als_liahona (author)betwys12015-05-15

Thanks! I meant #16. I've fixed that. I noticed the misspelling on the video after I uploaded it, but I don't think I can edit on YouTube now.

I appreciate you pointing out that mistake.

betwys1 (author)2015-05-15

"graphite nozzles that have a convergent section (90 degree) but no divergent cone. They are numbered according to the size of the throat in 64ths of an inch. (e.g. a #25 has a 1/4" throat.)" The example doesn't make sense - representing a little more than 3/8 in.

jcantalupo (author)2015-05-14

Pretty overwhelming, but thanks for sharing. As an L1 certified flyer, it's neat to see how much more there really is to explore someday. I appreciate your words of caution early on, as this is definitely not something for someone to casually jump right into, but there are sanctioned groups that not only help govern the hobby but provide a valuable opportunity for building up skills and learning and sharing information.

schabanow (author)2015-05-14

Thank you very much for your article. You remind me myself in rocketry when I was much younger... Thanks again for pictures, curves, total impulses, grains, igniters and so on... What a flavour there was - I mean NH4NO3 based propellant exhaust... The smell of careless youth... THAT WAS THE MATTER OF THRUST! ))

Thank you, brother. Good luck!

jimrittenour (author)2015-05-14

Wha?

I'm not smart enough for your project (no disrespect:), but the old Estes Gremlin was a mini-monster powered with a D. Mine got twenty feet of the ground and exploded into two parts. Great fun!

BrettHacks (author)2015-05-14

Very cool! I will be checking out NAR and Tripoli and hope we will see more from you. Hobby rocketry has certainly come a long way from the little Estes kits I remember.

seamster (author)2015-05-13

This was a fascinating read. Thank you!

I dabble in homemade mid-power rockets, but have always been intrigued by the high power stuff. Way impressive work here!

About This Instructable

34,449views

346favorites

License:

Bio: I've been writing software since I was in the 6th grade, and working with mostly-digital electronics since High School. These days my career consists ... More »
More by als_liahona:A Budget 3D Printer Kit Simple Enough for Kids...DIY 3D Printer Kits – Woes and WondersDelta 3D Printer Heated Bed - Heavy Duty
Add instructable to: