vrijdag 12 mei 2017

Waking up Linux from suspend mode

To see wake-up sources on Linux, use acpitool -w:

acpitool -w

   Device S-state  Status   Sysfs node
  1. UAR1  S3 *disabled
  2. RP01  S4 *disabled  pci:0000:00:1c.0
  3. PXSX  S4 *disabled
  4. RP02  S4 *disabled

[cut for brevity]

  16. RP08  S4 *disabled  pci:0000:00:1c.7
  17. PXSX  S4 *disabled  pci:0000:0e:00.0
  18. GLAN  S4 *enabled   pci:0000:00:19.0
  19. EHC1  S3 *enabled   pci:0000:00:1d.0
  20. EHC2  S3 *enabled   pci:0000:00:1a.0
  21. XHC  S4 *enabled   pci:0000:00:14.0
  22. HDEF  S4 *disabled  pci:0000:00:1b.0
  23. TPD4  S4 *disabled
  24. TPD7  S0 *disabled
  25. TPD8  S0 *disabled
  26. PEG0  S4 *disabled  pci:0000:00:01.0
  27. PEGP  S4 *disabled  pci:0000:01:00.0
  28. PEGA  S4 *disabled
  29. PEG1  S4 *disabled
  30. PEG2  S4 *disabled
  31. LID0  S3 *enabled   platform:PNP0C0D:00
  32. PBTN  S3 *enabled   platform:PNP0C0C:00

To modify:

 sudo acpitool -W 18


18. GLAN  S4 *disabled  pci:0000:00:19.0

Or use the raw kernel API:

cat /proc/acpi/wakeup
echo GLAN | sudo tee /proc/acpi/wakeup
cat /proc/acpi/wakeup

donderdag 6 april 2017

Linux shell script to fix LibreOffice 5.1's docx "unknown error" word/document.xml issues

I investigated some issues that caused LibreOffice version to error out when opening certain docx files created with Microsoft Office.

Here's the error:

File format error found at # SAXParseException: '[word/document.xml line 2]: unknown error', Stream 'word/document.xml', Line 2, Column 928831(row,col).

After a deep debugging session, it turns out this is caused by some values of the relativeHeight attributes in the word/document.xml file of the docx.

I made a script to workaround the relativeHeight issue by setting all relativeHeight attributes to zero which, according to the docx specification, means infinite.

After fixing this, I ran into another problem where LibreOffice would sometimes duplicate the w:themeColor attribute upon saving in docx format, thereby invalidating the XML. That is also checked and fixed by the code below.

I figured other people might find this useful, so here's my script:

# Fix to workaround LibreOffice 5 docx issues
# Copyleft 2017 (c) Tom Van Braeckel <tomvanbraeckel@gmail.com>

# This fixes these errors I've been getting:

# File format error found at 
# SAXParseException: '[word/document.xml line 2]: unknown error', Stream 'word/document.xml', Line 2, Column 928831(row,col).
# Problematic LibreOffice version:
# --------------------------------
# Version:
# Build ID: 1:5.1.6~rc2-0ubuntu1~xenial1
# CPU Threads: 4; OS Version: Linux 4.11; UI Render: default; 
# Locale: en-US (en_US.UTF-8); Calc: group

if [ -z "$tofix" ]; then
echo "Usage: $0 <filetofix>"
echo "Example: $0 bla.dockx"
exit 1
tofixreal=$(readlink -f "$tofix")

tempdir=$(mktemp -d)
cd "$tempdir"

unzip "$tofixreal"

# Fix relativeHeight issue
sed -i "s/relativeHeight=\"[^\"]\+\"/relativeHeight=\"0\"/g" word/document.xml
# and then after saving in LibreOffice 5.2 docx format, we sometimes need this fix:
sed -i 's/w:themeColor="text1" w:themeColor="text1"/w:themeColor="text1"/g' word/document.xml

zip -r "$tofixreal" *

cd "$cwd"

echo "Done! The file $tofixreal has been cleaned from relativeHeight and themeColor issues."

To use this script, make sure it is executable and do:

./fixdocx.sh filename.dockx

donderdag 13 oktober 2016

First successful static firing of tiny steel GALCIT rocket motor

We're happy to report that we have made our first tiny steel GALCIT rocket motor and successfully static fired it to thrust!

For this experiment session we had 3 goals:
  1. build and validate prototype of small scale GALCIT melter based on a deep fryer: success!
  2. build simple steel GALCIT rocket motor and cast the propellant: success!
  3. static firing of the test motor: success!

Small scale GALCIT melter 

For this prototype of a small scale GALCIT propellant melter we used a standard deep fryer.

In the deep fryer we placed a used, cleaned metal paint can to use as the crucible of the melter. We left the metal net of the fries in to facilitate the handling of the crucible and to provide an extra barrier between it and the heating elements.

Below is a picture of the melting of the asphalt/bitumen, the fuel component of the propellant.

Melting of bitumen fuel
Before adding the oxidizer, we made a simple wooden cover plate around the crucible to prevent the oxidizer from falling in the oil. There is a small gap between the crucible and the cover plate which we plan not to have in the final version of the melter.

The oxidizer had clumps in it, as expected. We ground and sieved the oxidizer with a standard powder sugar sieve and that worked very well.

Adding the KClO4 oxidizer 

When measuring the temperature of the oil with an independent sensor, it was found to be 176 degrees Celcius rather than the indicated maximum of 190 degrees Celcius. This gives the propellant slightly more viscosity and makes casting in tiny motors (made out of narrow pipes) more challenging.

Tiny steel rocket motor

For the motor we wanted to keep things as simple as possible.

We used a standard 12cm pipe with threads at the ends so it can be closed off with 2 end caps.

The cylinder needs insulation and/or cooling from the heat of the reaction. We used thick paper rolled into a cylinder to achieve ablative cooling and this worked nicely. Surprisingly, the material stood up during the incredibly long burn time. 

The diameter of the propellant grain is 18mm for the right one and 16mm for the left, which has thicker insulation. 

We need a nozzle, of course! Again using the simplest design possible, we went for an end cap with a hole drilled into the center.

To be able to drill the hole into the exact center, we fixated the end cap with a reusable wooden holder. We drilled using a drill press.

Nozzle cap holder
We chose the 3mm hole knowing that it is actually way too big rather than risking a RUD on the first firing test.
Resulting nozzle cap
It is expected that using this simple steel nozzle exit will result in severe erosion because the steel would not be able to resist to the heat of the exhaust for long.

Casting the solid rocket propellant

Casting takes some skill due to the aforementioned high propellant viscosity but we managed to cast it by letting it ooze down from a metal rod into the motor and pushing it down further with the rod.

Casting the propellant
A standard electronic match was installed in the propellant, described previously.

Final configuration without end cap nozzle

Static test firing

For the static firing, we mounted the motor inside a soft plastic bucket, full of with hard sand. The sand serves as a first sound, shock and shrapnel muffler and holds a small low-thrust motor in place very well.

The motor was ignited remotely at a safe distance from behind with a 25cm thick brick wall.

Checkout the slightly long and slightly underwhelming video...

That's a burn time of 3 minutes 29 seconds for a 12cm motor, which is huge!

From this low burn rate (0.57mm/s) is clear that the motor chamber was severely under-pressurized, as intended.  

Rudimentary thrust measurement

We measured the thrust with a simple scale. Not very accurate, but enough to verify that the motor was able to generate thrust. From image analysis, we estimate the thrust to be around 1-2 Newton.



Some thrust was measured, estimated at 1-2 Newton. In the future we'll measure the thrust more accurately and we obviously intend on generating a lot more thrust.

Propellant burn rate

As mentioned, we burned through 120mm of propellant in 209 seconds so that's a burn rate of around 0.57mm/s and we are aiming for a burn rate that's at least 40 times higher.

Burn completeness

All of the GALCIT burned away and the motor was found to be empty and almost clean.

Nozzle erosion

Only minor nozzle erosion was observed, which may be due to the low pressure and exhaust velocity.

Chamber cap thread leakage

It was observed that threads of the nozzle end cap had a slightly darker color up until halfway of the thread and some black residue was present. This leads to the conclusion that some gas slowly crept through the threads over the duration of the burn.

This is expected because we are not using any o-rings or sealing mechanisms for the pressure vessel other than the thread of the pipe. The same pipe filled with water would also start leaking without some seal (such as hemp fibers) on the threads.

Residue buildup

We observed an interesting (and unexpected) residue buildup at the nozzle exit. We're not sure yet what the residue is composed of.

It may be leftover or incompletely burnt propellant, eroded steel, ash or a mixture of a bit of everything.

The sharp edges of the nozzle hole and the small diameter of 3mm might contribute to the buildup of this material at the nozzle exit so this may go away by itself as we transition to smooth nozzles and/or bigger motors.

Residue buildup at nozzle exit


The experiments went very well and being able to make a basic functional steel GALCIT motor was a milestone that we really needed to pass.

The prototype small scale GALCIT melter also worked adequately well so we will be able to use it for future small scale experiments and tests.

dinsdag 27 september 2016

Successful GALCIT 61-C ignition using standard electrical matches

We recently experimented with standard electrical matches to ignite GALCIT 61-C propellant samples in combination with an off-the-shelf 433Mhz wireless pyrotechnic ignition system.

This work is part of our effort to obtain a reliable, cheap and easy method for igniting GALCIT propellant for both small and large scale GALCIT solid propellant rocket motors.

We learned from the experiments that these igniters work very well when used correctly.

The correct way to use these igniters with GALCIT 61-C is to make sure they are embedded in GALCIT 61-C in such a way that they have a layer of at least 1 cm of GALCIT 61-C propellant extending from the tip of the igniters.

This is the wrong way:

Wrong way to embed the igniter in the GALCIT propellant with only a few millimeters of GALCIT at the tip of the igniter

When this igniter is used, a small hole is blown through the GALCIT and the hot flames are just heating up air.

Here's the brief video of the failed ignition:

 Here a closeup of the GALCIT sample that failed to ignite:

Burned through rather than ignited GALCIT sample

This is the corrected way to embed the igniter in GALCIT:

Correct way to embed the igniter in the GALCIT propellant with around 2 cm of GALCIT located at the tip of the igniter

The reason that this works a lot better is that the 2-3 cm long hot flame of the igniter is propelled forward to the tip of the igniter and not (or barely) to the sides. Any GALCIT that is placed will not ignite, although it can still be useful to have it there for practical purposes such as ensuring the igniter adheres properly to the GALCIT.

Below is a picture of the receiver of the standard RF pyrotechnic igniter that we used. We later added an extension cord to ensure it would not get too hot from being close to the burning propellant.

Receiver of the 433Mhz RF pyrotechnic igniter 

In the end, we were able to attain 100% reliable ignition of GALCIT 61-C in 3 out of the 3 tests where we had properly installed the igniter in the propellant.

We were also successful in achieving contact ignition, where a small burning chunk of propellant ignites a larger one that is in contact with it rather than being embedded in it.

Below is a close-up of the contact ignition set-up. The igniter, embedded in a small chunk of GALCIT, is placed underneath the big GALCIT sample:

Contact ignition setup

Contact ignition has the benefit that the igniters do not necessarily need to be embedded in the main propellant grain. Instead, they can be embedded into a small chunk of propellant that is in contact with the main propellant grain, which might reduce the complexity of casting propellant and installing igniters into large scale motors.

Finally, below is a video of one of our successful ignitions:

donderdag 11 augustus 2016

Reverse engineering the SLAB HT2000 CO2, temperature and relative humidity sensor

TL;DR: I reverse engineered the SLAB HT2000 CO2, temperature and relative humidity (RH) data logger made by Dongguan Xintai Instrument Corporation. Sourcecode and binaries available at http://GitHub.com/tomvanbraeckel/slab_ht2000

I found a great CO2, temperature and relative humidity meter with USB connection. It's made by Dongguan Xintai Instrument Co. in China and branded under the name HT-2000. Available online for less than $100, it is quite cheap for this kind of device.

There's only one downside to a lot of cheap Chinese product and this one is no exception.. it came bundled with:
  • no Windows software or drivers, although I found one online
  • no manual, although I found one online
  • no Linux software or drivers
  • no protocol specification
  • no datasheet
So how does one read out the values or communicate with this thing through the USB port?

With the help of the Linux kernel, of course!

Connecting the device to a modern Linux (kernel version 4.7) and running lsusb yields in:

# lsusb
Bus 003 Device 011: ID 10c4:82cd Cygnal Integrated Products, Inc. # this is the one!

Cygnal makes USB-to-serial devices that are supported by the cp120x driver but this device actually reports itself to be a Human Input Device in its USB device descriptor:

# dmesg | tail
[407925.138165] usb 3-6: new full-speed USB device number 10 using xhci_hcd
[407947.073443] usb 3-6: new full-speed USB device number 11 using xhci_hcd
[407947.203629] usb 3-6: New USB device found, idVendor=10c4, idProduct=82cd
[407947.203632] usb 3-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[407947.203634] usb 3-6: Product: HT2000
[407947.203635] usb 3-6: Manufacturer: SLAB
[407947.209156] hid-generic 0003:10C4:82CD.0007: hiddev0,hidraw0: USB HID v1.01 Device [SLAB HT2000] on usb-0000:00:14.0-6/input0

And our Linux system creates a /dev/hidraw0 device as a virtual respresentation of the physical hardware device, the CO2 meter.

Manually reading out the device descriptor results in more confirmation that this is a raw HID device:

# lsusb -v -v -v

Bus 003 Device 003: ID 10c4:82cd Cygnal Integrated Products, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x10c4 Cygnal Integrated Products, Inc.
  idProduct          0x82cd 
  bcdDevice            0.00
  iManufacturer           1 SLAB
  iProduct                2 HT2000
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower               64mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
      Warning: Descriptor too short
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         2
          bDescriptorType        34 Report
          wDescriptorLength     128
          bDescriptorType        31 (null)
          wDescriptorLength     157
         Report Descriptors: 
           ** UNAVAILABLE **

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
Device Status:     0x0000
  (Bus Powered)

So we know it's a Human Input Device but we have no further info on how to get data from it.

No data is coming out when just reading from it while the device is making measurements:

sudo head -c 1   /dev/hidraw0 # Never finishes so not a single byte of data comes out here

Time to check out the Linux kernel samples of how to deal with hidraw devices. In the Linux kernel sources (version 4.7) there is an example samples/hidraw/hid-example.c which shows how to read a report from a hidraw device.

This example tool yields the following output:

# sudo ./hid-example /dev/hidraw0 

Report Descriptor Size: 128
Report Descriptor:
6 0 ff 9 1 a1 1 85 1 95 6 75 8 26 ff 0 15 0 9 1 91 2 85 2 95 3c 75 8 26 ff 0 15 0 9 1 91 2 85 3 95 1 75 8 26 ff 0 15 0 9 1 91 2 85 4 95 2 75 8 26 ff 0 15 0 9 1 91 2 85 5 95 1f 75 8 26 ff 0 15 0 9 1 81 2 85 6 95 3c 75 8 26 ff 0 15 0 9 1 81 2 85 7 95 3c 75 8 26 ff 0 15 0 9 1 81 2 85 8 95 3c 75 8 26 ff 0 15 0 9 1 81 2 c0 

Raw Name: SLAB HT2000
Raw Phys: usb-0000:00:14.0-6/input0
Raw Info:
bustype: 3 (USB)
vendor: 0x10c4
product: 0x82cd
ioctl HIDIOCGFEATURE returned: 4
ioctl HIDIOCGFEATURE returned: 61
Report data (not containing the report number):
8 9b a5 22 5 5 9b a3 22 5 5 9b a1 22 5 5 9b a1 22 5 5 9b a1 22 54 5 9b a2 22 54 5 9b a4 22 54 5 9b a5 22 54 5 9b a4 22 84 5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 

write() wrote 2 bytes
read: Resource temporarily unavailable

The "Report data" looks interesting but we are looking for live measurements and these values don't seem to change when running the program multiple times.

So we need to dig deeper.

Now, looking at the source code of the hidraw example, I noticed that it is trying to read out report number 9. So I set out to read the other report numbers and seeing whether anything useful comes out.

Report numbers 1 to 4 all have the same content as the 9, except that the first byte is different and seems to contain the report number itself.

Report number 5 is interesting though. The report data seems to be live because it is changing slightly after every run:

5 77 0 c4 e1 0 36 2 8c 1 f7 1 90 3 20 0 64 3 b6 b0 3 1 a1 22 2 73 0 0 7 d0 5 9b
5 77 0 c4 c6 0 64 2 8c 1 f9 1 90 3 20 0 64 3 b6 b0 3 0 a1 22 2 75 0 0 7 d0 5 9b
5 77 0 c4 e6 0 64 2 8c 1 fa 1 90 3 20 0 64 3 b6 b0 3 0 a1 22 2 7c 0 0 7 d0 5 9b
5 77 0 c4 c7 0 64 2 8c 1 f9 1 90 3 20 0 64 3 b6 b0 3 0 a1 22 2 7f 0 0 7 d0 5 9b

AHA! That is live data we are seeing there!

Comparing these values to the actual measurements on the display allowed me to spit out the following partial reverse engineered specification for this report #5:


5 77 0 c5 f2 0 64 2 97 1 ee 1 90 3 20 0 64 3 b6 b0 3 0 ff ff 2 e8 0 0 7 d0 ff ff 
  DD D DD DD      T TT H HH                                  C CC


DD D DD DD = a timestamp, seconds since epoch + 2004450700 (magic number)
T TT = the temperature, multiplied by 10 and plus 400 (26.3 degrees Celcius in the example above)
H HH = the humidity, multiplied by 10 (49.4 % R.H. in the example above)
C CC = the CO2 concentration, 744 ppm in the example above

Writing a C program that reads out these values was trivial and published on GitHub.

Future work

Report #6

Report #6 toggles between 2 different outputs:

6 1 3 20 0 64 3 b6 0 57 22 b5 b6 1 57 22 b5 b6 0 0 0 0 7 d0 0 0 0 0 0 0 0 6 ff ff ff 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1


6 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 fe 20 0 64 1 0 1 b0 1 90

Report #7

There seems to be a pattern here, similar to what report #6 outputs:

7 1 3 20 0 64 3 b6 0 57 22 b5 b6 1 57 22 b5 b6 0 64 0 0 7 d0 0 0 0 0 0 0 0 7 ff ff ff 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

More features

According to the specifications, this device also contains memory for at least 10000 measurements and can log in different modes (Immediately, Schedule, Real-time & Roll-over) but I could not figure out how to change the default mode without having the Windows PC software. This means the "REC" button does not do anything because the mode is set to "Immediately" instead of "Manual".

Also, recalling minimal and maximal values should be possible on the display but I have no idea how.

If you have any idea on how to use those extra features, leave a comment please. Thanks!

dinsdag 19 juli 2016

Linux shell script to stream audio to a Zavio IP Camera

While I was showing an apprentice how to reverse engineer closed HTTP interfaces, we figured out how the proprietary Zavio webbrowser plugin streams audio data to the Zavio F3510 IP Cameras and other models from around 2016.

Then I wrote a rudimentary bash shell script that enables you to play MP3, WAV, FLAC, MIDI or any other file formats that avconv can handle through the camera's poor little speaker.

Interestingly, you can also use this script to continuously stream live audio from a HTTP URL to the camera and that way you can enjoy music anywhere you've installed your camera's.

Currently, this script is compatible with bash on Linux but I don't see why it wouldn't work on bash for Windows or Mac OS if you're into that kind of thing. Just make sure you have the necessary dependencies (avconv, curl,...) in your search PATH.


# Simple shell script to stream audio to a Zavio F3510 or other Zavio camera's from around the year 2014, 2015 and 2016
# No configuration is required, just supply the parameters on the command line
# Supported audio formats: mp3, mp2, wav, ogg and many more, thanks to avconv and all related projects
# Uses curl, the Swiss Army Knife of networking
# Copyleft by Tom Van Braeckel, 19-07-2016 🔥

# How it works:
# =============
# The Zavio needs to receive pcm_mulaw encoded audio samples
# at a sample rate of 8000 samples/second (8kHz) mono at 16 bits per sample through a HTTP web interface.
# The data is sent through periodic HTTP requests in chunks of 1000 bytes audio data + HTTP protocol overhead.

# Dependencies:
# -------------
# - avconv
# - curl
# - base64
# - sleep (a modern one that accepts non-integer arguments, as in: sleep 0.06)
# - echo (a modern one that supports the -e "\r" construct)

# Theoretical note:
# -----------------
# The HTTP API that we call has no active two-way synchronisation protocol so there will always be timer drift between the rate at which we send samples and the rate at which they are consumed. When there is drift, the Zavio seems to insert a few dummy samples or drop samples to correct the drift. This could theoretically be heard as clicks or cracks when playing long audio clips but it seems to work fine for me.

# Calculation of the rate at which we need to send chunks of audio to the HTTP API:
# ---------------------------------------------------------------------------------
# 1000 bytes/chunk / 2 bytes/sample = 500 samples/chunk
# 8k samples/second / 500 samples/chunk = 16 chunks/second = 1/16 second/chunk = 0.0625 second/chunk

sleep_time_per_audio_chunk=0.06 # target sleep time (0.0625 seconds) minus around 4% = 0.0025s overhead (forking new sleep process)

if [ -z "$ip" -o -z "$auth" -o -z "$file" ]; then
echo "Usage: $0 ip_address username:password filename/url [volumeboost in dB]"
echo "Example: $0 tom:supercool world_domination.wav"
echo "Example: $0 admin:admin audiofile.mp3 10"
echo "Example: $0 admin:admin http://streamingserver.com/stream -5"
exit 1

# Default to 0 boost
if [ -z "$boost" ]; then
echo "boost = $boost"

# Encode the username:password pair in base64 encoding
auth64=$(echo -n "$auth" | base64)

# Arbitrary string of text
string="--As from earth's Bos om, sprung to sight"

echo -n "Sending audio sample..."
# Send the encoded audio to stdout with avconv and amplify it a bit, read it in chunks of 1000 bytes, add the HTTP prefix and let curl do the HTTP 1.0 request
# We do this while curl is doing requests of size > 1000 bytes, until the grep no longer exits successfully, which ends the loop
avconv -i "$file" -f wav -acodec pcm_mulaw -ar 8000 -ac 1 -af "volume=$boost"dB - | while (echo -en "$string\r\nContent-Type: audio/wav\r\n" ; head -c 1000 ) | curl -v --http1.0 -H "Content-Type: multipart/form-data; boundary=$string" -H "Authorization: Basic $auth64" --data-binary @- http://$ip/cgi-bin/operator/transmit 2>&1 | grep -q "Content-Length: 1[0-9][0-9][0-9]"; do
sleep "$sleep_time_per_audio_chunk"
echo -n .
echo "done."

woensdag 6 juli 2016

Nichrome and black powder electrical pyrotechnic igniter with GALCIT solid rocket propellant

We've made progress on the pyrotechnic igniter. It now uses nichrome wire because it has a high resistance, so it gets very hot when electricity is forced through it.

We wind the nichrome in a spiral so that it is longer, again for more electrical resistance, and place it on a paper rectangle of around 5x10cm.

Nichrome wire spiral

The nichrome wire has a diameter of 0.2mm and when stretched out is about 150mm long. With a resistance of 34 Ohm per meter, that is around 0.51 Ohm resistance in that piece of 150mm nichrome wire. 

Then we pour some black powder over the coil and roll it up into a cylinder, reinforced with some paper tape.

Black powder in paper with nichrome wire spiral inside

As a test we ignited a bit of leftover GALCIT in a heat resistant 2l lab beaker.

Heat resistant, yes, but as it turns out it was not GALCIT heat resistant. 

Our final setup looks like this, placed on a burn pile:

Pyrotechnic igniter installed in a lab beaker 

To get the nichrome to glow red hot, we used a powerful 18V battery from a Makita drill to supply the current through an old UTP cable. But a 4.5A 6V battery should also do the trick.

The ignition went smoothly and the burn was quite spectacular. The jet of fire that was spewed into the air indicated that we were already developing some thrust, which is remarkable with such a huge throat (in this case, beaker) area.

Here's the video:


dinsdag 7 juni 2016

Electrical model rocket black powder igniter with steelwool

Hypothesis: A cheap rocket motor igniter can be made out of steel wool and black powder.

We tried out the concept by sending an electrical current through some steel wool and got it to ignite at 3 Volt and 5 Ampère.

Power required = 3 Volt * 5 Ampère = 15 Watt

Readers note: steel wool requires quite some energy to get it to glow red hot and ignite. But 15 Watts is easily reached by using a powerful battery such as an 28 Volts battery of an electrical hand-held drill.

Here's the video of steel wool igniting at around 15 Watts:

Then we arranged the steel wool on some paper tape:

Poured black powder on top and simply wrapped it up.

We made 3 versions:

  • test #1: a black powder igniter with paper tape, pictured above
  • test #2: a black powder igniter with a plastic straw
  • test #3: a black powder igniter with a post-it, rolled up and taped together 

Test #3 contained a bit too much black powder than strictly necessary, check out the video. It will be discussed in the"Safety Improvements" section below!


  • Small amount (2 g) of steel wool: 0.2 euro
  • Small amount (10 g) of black powder: 0.6 euro
  • Copper wire (10 cm) to connect to the steel wool: 0.2 euro
Total price per igniter: around 1 euro


The hypothesis has been validated; a cheap rocket motor igniter can be made out of steel wool and black powder. The igniter has a power requirement of around 15 Watt.

Safety improvements

To stay focused on safety, we'll conclude every session by trying to find at least one safety improvement to be implemented.

Next time we'll be sure to fixate the camera and leave it unmanned. That will give us a better view of the reaction from close by and will keep us safe at the same time.

Simple do-it-yourself automatic (black)powder mixer

This simple, cheap home-made powder mixer can be used to mix relatively large quantities of powder, such as blackpowder, automatically.

Since a picture says more than a 1000 words, let's start with 160 pictures in rapid succession:

Above: The powder mixer in action. This video says more than 160 x 1000 words.

Note that the final model looks slightly different; in the end we moved one of the rolls to the opposite side of the box so that the mixing container is spinning between the powered and the non-powered roll. This reduces the friction on the container so that it spins around more easily.

Shopping list

  • 1 hand-held drill (battery powered or otherwise is fine) to provide rotation
  • 1 plastic box (cheap at IKEA or simular) to support the axles
  • 2 cardboard cylinders
  • 2 axles (we used metal bars but wood should also work)
  • glue to attach the cylinders to the axles

Mixing container

The mixing happens inside a container. We used a metal container with a plastic lid, which is not ideal for pyrotechnic powders - see the "Safety Improvements" section below.

To ensure proper mixing, we glued a wooden stick to the inside of the mixing container. That way, the powder gets scooped up and dropped down continuously, similar to a concrete mixer.

We also threw in a few plastic cubes, again to have better mixing by creating some "chaos" or randomness in the mixing process.

On the picture below, the wooden stick is barely visible because of the blackness of the powder:

The powder mix container filled with black powder.

The results

This blackpowder is of the best quality that we have made with any dry mixing process so far.

The only superior quality that we obtained in the past was with a liquid mixing processes, such as the process that involves boiling water, a blender and isopropyl alcohol, but that is more cumbersome and the quality we have here will suffice for our igniters.

We will be able to use this powder for our igniter tests, as documented in the article "Electrical model rocket black powder igniter with steelwool".

Safety improvements

In hindsight, mixing black powder in a metal container with a plastic lid is not ideal for safety. Should the black powder ignite for whatever reason, and should the plastic lid fail to release, then  the pressure would build up inside the container and it could potentially blow up. 

In general, it is recommended never to place pyrotechnical substances inside metal or glass containers because they could shatter when the substance ignites. Also, make sure that the container has a lid that easily comes off to ensure no pressure can build up inside. 

woensdag 1 juni 2016

Making GALCIT propellant + test burn

We resumed our GALCIT solid rocket propellant preparation that was started a few days ago.

High-level plan:

  1. Melting the mixture of bitumen and paraffin oil that we made last time
  2. Mixing in 2614.4 grams of potassium perchlorate (KClO4)
  3. Casting the GALCIT into some cardboard test engine tubes and into a small ceramic bowl
  4. Lighting a small amount of GALCIT at atmospheric pressure in the ceramic bowl

Step 1. Melting the mixture of bitumen and paraffin oil

Melting the mixture of bitumen and paraffin oil took around 1 hour at 300 degrees Celcius. This is quite slow because the contact surface area between the melting beaker and the temperature controlled hotplate is quite small. To optimize this, we'll use a crucible with a larger base next time.

For safety, a gas mask and protective gloves were worn during the mixing. The gloves are great, they protect from heat and fluids so they are ideal for bitumen. Without them, this would have been a lot more difficult.

Once the mixture had completely melted, it was quite runny, quite comparable to molten chocolate.

Step 2. Mixing in potassium perchlorate

Mixing in the oxidizer was done in open air for safety reasons, again with protective gear.

The large volume of propellant in comparison with the small surface contact area with the hot plate meant it was difficult to keep the GALCIT hot enough.

And since the oxidizer has quite a high heat capacity (111.35 J/mol·K), every time a quantity of the oxidizer was added, the mixture would cool down and stiffen up. Also the volume would increase, so the unfavorable volume/contact area would worsen at every increment.

In the end, we divided the 2l mixture over two containers and continued with 1l of volume.
That resolved the heating issue and allowed us to finish up our first liter of GALCIT.

The final GALCIT mixture is quite viscose (similar to wet clay) and very sticky.

Step 3. Casting the GALCIT

The viscosity of GALCIT was too high to allow pouring it directly into our cardboard test engine tubes but it was easy enough to scoop it out, smear it into the tubes and pack it together with a stick.

We filled up 3 cardboard test engine tubes that had a clay stopper on the bottom, those still need to be reinforced, fitted with an ignition and nozzled up.

To avoid pockets of air in the propellant, which would dramatically increase the surface area, we just tried to pack it tightly. In the future, we might need a better way to ensure no bubbles get trapped inside to ensure a continuous burn.

Step 4. GALCIT burn test

Since we had a bit of GALCIT left, we decided to do a small test to see whether we could get it to ignite at atmospheric pressure with 10 grams of (relatively poor quality) black powder that we made on the side.

We placed around 14cl (around 300g) into a small ceramic bowl and poured the black powder on top.

This batch of black powder did not easily ignite when placing a burning cigarette into it, although the previous batch did. But that wasn't unexpected, considering the poor quality of the black powder. Luckily, we had a solid plan B for igniting the black powder and the GALCIT, using a simple hand-held propane blow torch.

The blow torch did the trick. We were surprised of how well GALCIT burns at 1 bar, considering it burns optimally at 70-140 bar.

Just take a look at the video, at 1m 10s...

Timeline of the test burn:

  • 1:10 start of blackpowder ignition
  • 1:20 start of GALCIT ignition
  • 2:05 fade out of GALCIT burn (smaller diameter and surface area at bottom of ceramic cup)
  • 2:13 end of GALCIT burn

Total burn time: 45 seconds

A total burn time of 45 seconds is huge for such a small amount of GALCIT. It means the burn rate was very slow, about 0.27 mm/s. Consider the excessively high burn rates (~ 36.5 mm/sec) that have been reported and large burning surface area, estimated at around 40 cm3.

Yet this is to be expected, due to the low atmospheric pressure at which the burn occurred.
GALCIT has a very high pressure exponent so the burn rate increases dramatically with the pressure.

So the time has come to test the propellant at a higher chamber pressure!

maandag 30 mei 2016

First stab at making solid rocket propellant (GALCIT)

Last weekend we took a first stab at making the solid rocket propellant GALCIT.

Long story short: melting bitumen on the temperature controlled hotplate took a lot longer than expected so we weren't able to mix in the potassium perchlorate yet. That's planned for the next session, tomorrow!

Below is a small report of the session with some visuals.


The goal was to make 2l of GALCIT, with the following composition:
  • 76% Potassium perchlorate (KClO4) Superfino from Nitroparis
  • 20% Solid block bitumen from Icopal through Defranq
  • 4% Paraffin oil from the local drugstore, Estfarma
Lots of gratitude to Peter Madsen of the RML spacelab for providing us with this formula and lots of useful pointers. They are doing amazing work.

Also a big thanks to the mentioned suppliers. Each of them went out of their way to guide us to the proper channels for acquiring these products. They seem to be of great quality!

We are making a volume 2l of GALCIT so at the published density of 1.72 kg/l the total propellant mass computes to 3.44 kg.

Applying the above GALCIT composition, that translates to:
  • 76% = 2614.4 g of potassium perchlorate
  • 20% = 688g of solid block bitumen
  • 4% = 137.6 g of paraffin oil
The high-level procedure to make GALCIT is:
  1. Heat paraffine oil
  2. Melt bitumen in paraffin oil
  3. Mix in potassium perchlorate through a fine sieve to remove lumps
This is our block of 10kg bitumen:

Cutting 688g off of it was difficult, as expected. Bart discovered that cutting it with a long, strong, heated knife turned out to be the easiest way to cut slices off. 

I was surprised to see that although bitumen looks heavy, it actually has a low density. Look at all that volume to make up just 689.28g! 

To melt the bitumen, we are using the RSM-05-H by Phoenix Instrument. This hot plate with built-in magnetic stirrer can stir 20l of water and reach up to 550 degrees Celsius. Having accurate temperature control is important to avoid overheating the propellant. Overheating is in any case unlikely because of the big difference in preparation temperature (less than 200 degrees C) and combustion temperature (500 degrees C).

The RSM-05-H has an RS-232 interface to control and monitor it from a computer but we didn't use that this time. And although it came with a probe for accurate in-liquid temperature control, we wanted to spare the poor probe from being dipped into sticky, molten bitumen...

Melting the bitumen took a lot longer than expected. This is not unusual - bitumen just takes a long time to melt. Also, we did the mixing outside with a light breeze, which is great for ventilation but causes quite some heat loss on the hot plate.

To speed up the melting process, we turned the temperature up to 200 degrees C, which is without risk as long as the oxidizer (the potassium perchlorate) is not mixed in yet.

In total, melting 689.28g of bitumen with paraffin oil took around 1 hour and 15 minutes.


Before melting bitumen, we took the time to make various cardboard test-engines to cast the propellant into. Bart knew that the best way to cut cardboard tubes is with a fine metal saw:

While melting, we experimented with a small amount of ignition powder, composed of 80% potassium nitrate (KNO3) and 20% fine carbon powder.

We were unable to ignite this mixture, not by electrically heating a thin copper wire until melting point and neither with open flames. The video isn't quite as spectacular as we hoped, anti-climaxing at around 0:20s with some smoke:

We posit 2 reasons the powder is not igniting:
  • Poor mixing of the ignition powder components
  • Lack of sulfur in the mixture
So next time, we'll add some sulfur to the mix and try to get our hands on an automatic powder mixer.