vrijdag 23 augustus 2019

Nirvana live at The Hollywood Palace, California (October 25, 1991)

One month after "Nevermind" was released, Nirvana played at The Palace in Hollywood, on the Rock for Choice benefit[1]. Full bootleg audio of the concert is available online[3].
Jack Black recalls this concert as being a "historical musical experience" and remembers it happened on the evening Bill Graham passed away in a helicopter crash[2]
On that same day, Nirvana gave an interview on MTV's Headbangers Ball with Riki Rachtman about which he states: "I did that show for five years, and when Nirvana was coming in, here was a band that I could tell was on the verge of making it huge"[4].

donderdag 22 augustus 2019

Making VirtualBox vboxdrv kernel driver 5.1.x compatible with Linux 4.20.x


When Ubuntu 16.04.6 LTS users do:

sudo apt-get install virtualbox

...they receive VirtualBox 5.1.38-dfsg-0ubuntu1.16.04.3.

This should also install virtualbox-dkms, which compiles the vboxdrv kernel driver for their Linux kernel. If it doesn't (for example, because virtualbox-dkms is already installed) then it can be reinstalled with:

sudo apt-get install --reinstall virtualbox-dkms

But if they are running Linux kernel 4.20.x, they will find the following compilation errors in /var/lib/dkms/virtualbox/5.1.38/build/make.log:

  CC [M]  /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.o
/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.c: In function ‘VBoxHost_RTTimeNow’:
/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.c:175:5: error: implicit declaration of function ‘ktime_get_real_ts’ [-Werror=implicit-function-declaration]
     ktime_get_real_ts(&Ts);
     ^
cc1: some warnings being treated as errors
scripts/Makefile.build:291: recipe for target '/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.o' failed
make[2]: *** [/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.o] Error 1
scripts/Makefile.build:516: recipe for target '/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv' failed
make[1]: *** [/var/lib/dkms/virtualbox/5.1.38/build/vboxdrv] Error 2
Makefile:1563: recipe for target '_module_/var/lib/dkms/virtualbox/5.1.38/build' failed
make: *** [_module_/var/lib/dkms/virtualbox/5.1.38/build] Error 2

To fix this issue, we need to make the VirtualBox vboxdrv code compatible with Linux 4.20 and after some trial and error, I came up with the following patches:

--- /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/include/iprt/time.h.orig 2019-08-22 10:09:34.909117248 +0200
+++ /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/include/iprt/time.h 2019-08-22 10:09:04.949044978 +0200
@@ -28,6 +28,7 @@
 #include <iprt/cdefs.h>
 #include <iprt/types.h>
+#include <linux/time64.h>
 RT_C_DECLS_BEGIN
@@ -429,6 +430,13 @@
 }
 #endif /* various ways of detecting struct timespec */
+// Added for Linux 4.20 compatibility
+DECLINLINE(PRTTIMESPEC) RTTimeSpecSetTimespec64(PRTTIMESPEC pTime, const struct timespec64 *pTimeval)
+{
+    return RTTimeSpecAddNano(RTTimeSpecSetSeconds(pTime, pTimeval->tv_sec), pTimeval->tv_nsec);
+}
+
+
 /** The offset of the unix epoch and the base for NT time (in 100ns units).
  * Nt time starts at 1601-01-01 00:00:00. */
 #define RTTIME_NT_TIME_OFFSET_UNIX      (116444736000000000LL)

--- /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.c.orig 2019-08-22 10:12:14.633414691 +0200
+++ /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/r0drv/linux/time-r0drv-linux.c 2019-08-22 10:12:29.865436296 +0200
@@ -171,10 +171,11 @@
 {
     IPRT_LINUX_SAVE_EFL_AC();
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
-    struct timespec Ts;
-    ktime_get_real_ts(&Ts);
+// Modified for Linux 4.20 compatibility
+    struct timespec64 Ts;
+    ktime_get_real_ts64(&Ts);
     IPRT_LINUX_RESTORE_EFL_AC();
-    return RTTimeSpecSetTimespec(pTime, &Ts);
+    return RTTimeSpecSetTimespec64(pTime, &Ts);
 #else   /* < 2.6.16 */
     struct timeval Tv;


To recompile the module, change to the directory /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv and do:

sudo make

Finally, to load the vboxdrv module, do:

sudo insmod /var/lib/dkms/virtualbox/5.1.38/build/vboxdrv/vboxdrv.ko

Enjoy VirtualBox!

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

Result:

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 5.1.6.2 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:

#!/bin/sh
# 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: 5.1.6.2
# 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

tofix="$1"
if [ -z "$tofix" ]; then
echo "Usage: $0 <filetofix>"
echo "Example: $0 bla.dockx"
exit 1
fi
cwd=$(pwd)
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.




Results

Thrust

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




Conclusion


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:

Output:

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

Where:

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

and

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.

Enjoy!

#!/bin/bash
# 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)

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

# Default to 0 boost
if [ -z "$boost" ]; then
boost=0
fi
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 .
done
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!




Price


  • 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

Conclusions

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.