Making replacement Chieftec drive rails, 3

03.04.2021 18:30

A couple of months ago I was writing about 3D-printable replacement drive rails for my Chieftec PC enclosure. Back then I've designed and printed some parts that were functional enough to use for mounting a new set of 3.5" hard drives into my computer and I have been using them since. However I was bothered by the fact that the new rails required two parts to be glued together. I've now updated the design so that the latch snaps onto the base part of the rail. It's purely a friction fit and hence assembly of the new rails doesn't require any glue.

A pile of original purple Chieftec drive rails.

Having to glue together 12 rails for the complete set of drives was bothersome. However the main reason why I wanted to avoid using glue was because I couldn't find one that would bond well to the PETG plastic that my parts were printed from. Every glue I tried produced a very weak bond that soon fell apart. I tried to modify the design so that it minimized the stress on the bond, but that didn't really work. I've heard that this specific glue produces good results with the filament I used, however I could not find a shop that would have it in stock.

Replacement Chieftec disk rail render from FreeCAD.

Coming up with a design where the latch just snaps into the base required two more round trips between CAD and the 3D printer. I find that the most troublesome part of any 3D printed design is always the place where two parts need to slide or engage with each other. Each printer has slightly different tolerances. Often this differs even between prints on the same printer. It's hard to find the exact amount of space you need to leave in the STL files between surfaces. Too much and the parts fit too loosely, too little and the parts don't fit together or break when they are assembled.

Replacement drive rails being printed on a Prusa printer.

I've written in one of my earlier posts that I was also worried about the 3D printed rails getting soft when in contact with the warm hard drives. I've been using them now for close to 2 months and so far I haven't seen any signs of deformation due to heat. On the other hand, the winter has barely ended. I expect the drives to reach higher temperatures in summer.

I've put the new designs in place of the old ones. There's now also a README file there that has some condensed instructions for printing.

Replacement rail with the latch mounted on a hard drive.

In the end, this took way more time than I anticipated. 3D printers are fun and convenient, but getting to a design that works well can still be very time consuming. In this particular case it was also mostly due to me being stubborn and wanting a replacement that functions more or less exactly like the original. It turned out that even without the latch the rails function quite well. There are sheet metal springs in the case that grab onto the holes for the screws on the rails. At least in my enclosure, these springs alone provide enough friction that drives are held pretty well even without the additional security of the latch action on the rail itself.

Posted by Tomaž | Categories: Life | Comments »

Vector measurements with the HackRF

27.03.2021 20:09

Over the last year I slowly built up a small, one-port vector network analyzer. The instrument consists of a rtl-sdr USB receiver dongle, the ERASynth Micro frequency synthesizer, an RF bridge, a custom time multiplex board I designed and, of course, a whole lot of software that glues everything together and does final signal processing in the digital domain. In the past months I've written pretty extensively about its development here on this blog and I've also used the system in practice, mainly to measure VSWR and matching networks of various multiband LTE antennas.

The original instrument could perform S11 measurements up to around 2 GHz. However the 2 GHz limit was only due to the E4000 tuner in the rtl-sdr. Since ERASynth Micro can generate signals up to 6.4 GHz and I designed the multiplexer with signals up to 8 GHz in mind, getting measurements at higher frequencies was only a matter of upgrading the receiver. I was tempted to do that since the popular 2.4 GHz ISM band was just out of reach of my measurements. I did a lot of searching around for a suitable SDR that would cover that frequency range. The top of my list was an USRP B200, but amid the lockdowns and the general chaos in international shipping last year I couldn't find a supplier. In the end I settled for a HackRF One.

My small, home-made vector network analyzer, upgraded with a HackRF.

On one hand, moving from the rtl-sdr to HackRF was pretty simple. After I got hackrf_tcp up and running most of my old code just worked. Getting things to work reasonably well took longer though. I lost a lot of time figuring out why the gain in the system varied a lot from one measurement to the other. I would optimize signal levels for best dynamic range, only to try it again next day and find that they have changed considerably. In the end, I found out that the USB hub that I was using could not supply the additional current required by the HackRF. Interestingly, nothing obvious broke with the USB bus voltage wildly out of spec, only the analog performance became erratic.

I wish HackRF came with some hardware settings (jumpers or something) that would allow me to semi-permanently disable some of its more dangerous features. As it is right now I need to use a DC block to protect my multiplex board from a DC bias in case the antenna port power gets enabled by a software bug. I also had to put in a 20 dB attenuator to protect the HackRF in case its pre-amplifier gets turned on, since that would get damaged by the signal levels I'm commonly using.

Error network terms when using Henrik's bridge with HackRF.

Here are the error network terms with the upgraded system. The faded lines are the measurements by Henrik Forstén from whom I copied the RF bridge design. Up to 2 GHz the error terms match pretty well with those I measured with the rtl-sdr. As I noted in my previous blog post, the error terms are a measure of the whole measurement system, not only the bridge. Hence my results differ from Henrik's quite significantly since apart from the bridge his system is quite different.

Unfortunately, my new system still isn't very well behaved beyond 2 GHz. I suspect this has to do with the construction that has a lot of connectors everywhere and RG-316 cables of questionable quality. Every contact introduces some impedance mismatch and in the end it's hard to say what is causing what. I also know that I made an error in calculating the dimensions of the coplanar waveguides on my multiplex board. I'm sure that is not helping things.

Estimated dynamic range of the system.

This is an estimated dynamic range of the vector measurement, using the method described in this post. Let's say it's better than 50 dB below 1.5 GHz and better than 30 dB below 3.5 GHz. It quickly gets worse after that. At around 5.5 GHz I suspect there's some kind of a resonance in the 10 dB attenuator I have on the multiplex board that's made out of 0603 resistors. Beyond that point the signal that passes through the attenuator is stronger than the un-attenuated signal and I can't measure anything anymore.

I really would like to improve the dynamic range in the future. Basically all the practical measurements I did with this system was with the device-under-test being behind a U.FL connector. The problem with that is that the connector introduces a significant mismatch before the device-under-test. You can calibrate this out, but this comes at a cost of the effective dynamic range. Hence it may be true that 30 dB of dynamic range is enough for practical measurements. However as soon as you start moving the measurement plane away from the port of the instrument, you really want to start with as much dynamic range as possible.

I believe most of the total noise in the system now actually comes from the phase noise. Currently the signal source and the receiver use independent clocks and each of those clocks has its own drift and various artifacts introduced by individual phase locked loops. I suspect things would improve significantly if I could run both ERASynth Micro and HackRF from a common clock source, ideally from the ERASynth Micro's low phase noise TCXO. Unfortunately they use incompatible interfaces for reference clock in/out. I need to make an adapter circuit before I can try this out.

In the end, this is all kind of an endless rabbit's hole. Every measurement I take seems like it could be done better and there appears to be a lot of room for improvement. I've already accumulated a wish list of changes for the multiplex circuit. At one point I will likely make a new versions of the bridge and the multiplexer PCBs using the experience from the past year of experimenting.

Posted by Tomaž | Categories: Analog | Comments »

Characterizing the RF Demo Kit

19.03.2021 18:47

The RF Demo Kit is a small printed circuit board with several simple RF circuits on it. There are several variants out there from various sellers. It's cheap and commonly sold together with NanoVNA as a learning tool since it is nicely labeled. Among random circuits like filters and attenuators, it also contains a set of short, open, load and thru (SOLT) standards that can be used for VNA calibration. These are all accessible over U.FL surface mount coaxial connectors.

The "RF Demo Kit" circuit board, NWDZ Rev-01-10.

I've been using the SOLT standards on the Demo Kit for calibrating my home-made vector network analyzer. I've been measuring some circuits with an U.FL connection and I lack a better U.FL calibration kit at the moment.

Of course, for this price it's unrealistic to expect the Demo Kit to come with any detailed characterization of the standards. Initially I just assumed the standards were ideal in my calculations. However I suspected this wasn't very accurate. The most telling sign was that I would often get an S11 measurement of a passive circuit that had the absolute value larger than 1. This means that the circuit reflected more power than it received and that wasn't possible. Hence the calibration must have given my instrument a wrong idea of what a perfect signal reflection looks like.

The "RF Demo Kit" connected to my home-made vector network analyzer.

I suspected that my measurements would be more accurate if I could estimate some stray components of the standards on the Demo Kit and take them into account in my calibration. I did the estimation using a method that is roughly described in the Calibrating Standards for In-Fixture Device Characterization white paper from Agilent.

I did my measurements in the frequency range from 600 MHz to 3 GHz. First I used my SMA cal-kit to calibrate the VNA with the measurement plane on its SMA port. I then measured the S11 of the Demo Kit short standard, using a SMA-to-U.FL coax cable:

S11 for the short standard before port extension.

I used this measurement to calculate the port extension parameters to move the measurement plane from the VNA port to the position of the short on the Demo Kit PCB. I verified that the calculated port extension made sense. Calculated time delay was approximately 0.95 ns. With a velocity factor of 0.7 for the RG-316 cable, this gives a length of 19.9 cm which matches the length of the 20 cm cable I used almost exactly.

S11 for the short standard after port extension.

Using the port extension "unwinds" the Smith chart for the short standard. Ideally the whole graph should be in one spot at Z = 0 (red dot). In reality, noise of the VNA and various other imperfections make it a fuzzy blob around that point.

I then applied the same port extension to the measurement of the open standard. Ideally, this graph should be in one point at Z = infinity (again, marked with a red dot). This graph is still very noisy, like the previous one. However one important difference is that it clearly shows a systematic, frequency dependent deviation, not just random noise around the ideal point. Some calculation shows that this deviation is equivalent to a stray capacitance of about 0.58 pF. The simulated response of an open with 0.58 pF stray capacitance is shown in orange:

S11 for the open standard after port extension.

The Agilent white paper goes further and also estimates the stray inductance of the load standard. This is how my measurement of the Demo Kit load standard looks like with the same port extension applied as before. Again, the ideal value is marked with the red dot:

S11 for the load standard after port extension.

It looks pretty messy, with maximum reflection loss of about -12 dB at frequencies up to 3 GHz. Note that the U.FL connectors themselves are only specified up to about -18 dB reflection loss, so a lot of this is probably due to connector contacts. The white paper describes using time gating to isolate the effect of the load from the effect of contacts, but the mathematics of doing that based on my measurements escape me for the moment. I also suspect that my setup lacks the necessary bandwidth.

I stopped here. I suspect estimating the stray load inductance wouldn't make much difference. Keep in mind that the graph is drawn with port extension in place, which removes the effect of the cable. Hence the circling of the graph must be due to phase delays in the load resistor and the U.FL connector. The phase delay also cannot be due to the short length of microstrip between the U.FL and the 0603 resistor on the RF Demo Kit PCB. That shouldn't account for more than about 15° of phase shift at these frequencies.

At the very least I'm somewhat satisfied that the Figure 7(b) in the white paper shows a somewhat similarly messy measurement for their load standard. I'm sure they were using a much higher quality standard, but they were also measuring up to 20 GHz:

S11 measurement of the load standard from the Agilent white paper (Fig 7b)

Image by Agilent Technologies, Inc.

In the end, here is the practical effect of taking into account the estimated stray capacitance of the open standard when measuring S11 of a device:

S11 measurements calibrated using ideal open or open with stray capacitance.

When the VNA was calibrated with the assumption of a ideal open standard, the log magnitude of the measured S11 went above 0 dB at around 1400 MHz. This messed up all sorts of things when I wanted to use the measurement in some circuit modeling. However, when I took the stray capacitance of the open standard into account, the measured S11 correctly stayed below 0 dB over the entire frequency range. Since I don't have a known-good measurement I can't be sure in general whether one or the other is more accurate. However the fact that the limits seem correct now suggests that taking the estimated stray capacitance into account is an improvement.

Posted by Tomaž | Categories: Analog | Comments »

hackrf_tcp, a rtl_tcp for HackRF

13.03.2021 19:03

rtl_tcp is a small utility that exposes the functionality of a rtl-sdr receiver over a TCP socket. It can be interfaced with a simple Python script. I find it convenient to use when I need to grab some IQ samples from the radio and I don't want to go into the complexity of interfacing with the GNU Radio or the C library. Using it is also much faster compared to repeatedly running the rtl-sdr command-line utility to grab samples into temporary files at different frequencies or gain settings.

I wanted to use something similar for the HackRF. Unfortunately the stock software that comes with it doesn't include anything comparable. After some searching however I did find an old fork of the HackRF software repository by Zefie on GitHub that included a file named hackrf_tcp.c. Upon closer inspection it seemed to be a direct port of rtl_tcp from librtlsdr to libhackrf. It didn't compile out of the box and merging the fork with the latest upstream produced some conflicts, but it did look promising.

I resolved the merge conflicts and fixed the code so that it now compiles cleanly with the latest libhackrf. I also added a few small improvements.

Just like with rtl_tcp, the protocol on the socket is very simple. Upon receiving the client's connection the server initializes the radio and starts sending raw IQ samples. The client optionally sends commands back to the server in the form of a simple structure:

struct command{
	unsigned char cmd;
	unsigned int param;
}

cmd is the command id and param is a parameter for the command. Commands are things like SET_FREQUENCY = 0x01 for setting frequency, SET_SAMPLERATE = 0x02 for setting ADC sample rate and so on.

The original code attempted to keep some backwards compatibility by mapping HackRF's functionality to existing rtl-sdr commands. This included things like using the frequency correction command to enable or disable the HackRF's RF preamplifier. I dropped most of that. Obviously I kept the things that were direct equivalents, like center frequency and sample rate setting. The rest seemed like a dangerous hack. Enabling HackRF's preamp by mistake can damage it if there's a strong signal present on the antenna input.

Instead, there is now a new set of commands starting at 0xb0 that is HackRF exclusive. Unsupported rtl-sdr commands are ignored.

Even with hacks backwards compatibility wasn't that good in the first place and I wasn't interested in keeping it. HackRF produces IQ samples as signed 8 bit values while rtl-sdr uses unsigned. The code makes no attempt to do the conversion. There is also a problem that the 32 bit unsigned parameter to the SET_FREQUENCY = 0x01 command can only be used for frequencies up to around 4.3 GHz, which is less than what HackRF can do. To work around that limitation I added a new command SET_FREQUENCY_HI = 0xb4 that sets the central frequency to parameter value plus 0x100000000.

My updated version of hackrf_tcp is in my hackrf fork on GitHub. It seems reasonably stable, but I've seen it hang occasionally when a client disconnects. I haven't looked into this yet. In that case it usually requires a kill -9 to stop it. In hindsight, separating hackrf_tcp out into its own repository instead of keeping it with the rest of the upstream tools might have been a better idea.

As it is right now, you need to compile the whole libhackrf and the rest of the host tools to get hackrf_tcp. The basic instructions in the README still apply. After installation you can just run hackrf_tcp from a shell without any arguments:

$ hackrf_tcp
Using HackRF HackRF One with firmware 2018.01.1
Tuned to 100000000 Hz.
listening...

You can also specify some initial radio settings and socket settings on the command-line. See what's listed with --help.

Posted by Tomaž | Categories: Code | Comments »

RAID and hard disk read errors

27.02.2021 19:07

I have been preoccupied with data storage issues lately. What I though would be a simple installation of a solid state drive into my desktop PC turned into a month-long project. I found out I need to design and make new drive rails, decided to do some overdue restructuring of the RAID array and then had to replace two failing drives. In any case, a thing that caught my eye recently was a warning about RAID 5 I saw repeated on a few places on the web:

Note: RAID 5 is a common choice due to its combination of speed and data redundancy. The caveat is that if one drive were to fail and another drive failed before that drive was replaced, all data will be lost. Furthermore, with modern disk sizes and expected unrecoverable read error (URE) rates on consumer disks, the rebuild of a 4TiB array is expected (i.e. higher than 50% chance) to have at least one URE. Because of this, RAID 5 is no longer advised by the storage industry.

This is how I understand the second part of the warning: it talks about a RAID 5 array with total usable capacity of 4 TiB. Such an array would typically consist of three 2-terabyte disks. In the described scenario one of the disks has failed and, after adding in a replacement drive, the array is restored by reading the contents of the remaining two disks. This means we need to read out 2 times 2 terabytes of data without errors to successfully restore the array.

I was surprised by the stated higher-than-50% chance of a read error during this rebuild procedure. It seemed too high given my experience. Hence I've looked up the reliability section of the datasheet for the new P300-series, 2 TB Toshiba desktop-class hard drive I just bought:

Reliability section of the datasheet for Toshiba P300 hard drive.

I'm a bit suspicious of the way probability is specified here. Strictly reading the exponential notation, 10E14 means that the probability of an unrecoverable error (URE) is one error per 10⋅1014 bits. Expressed as probability of an error when reading a single bit:

P_{URE} = \frac{1}{10\cdot10^{14}} = 10^{-15}

In another datasheet for a different series of drives (however this time for data center instead of consumer use) the error rate is given as 10 errors per 1016 bits. This again gives the same error probability of 10-15.

Consider this probability for a second. It's such a fantastically low number. I don't remember ever encountering an actual technical specification that would involve a probability that has a one preceded by fifteen zeros - or in other words - fifteen nines of reliability.

The number is just on the edge of what you can represent with the common 64-bit double-precision floating-point format. If using a tool like numpy that only uses double-precision, any calculations with such values need to be done extra carefully to ensure that loss of numerical precision doesn't lead to nonsensical results.

Hard drives tend to use SI prefixes instead of binary, so I'll do the calculation for 4 terabytes instead of 4 tebibytes like it says in the quote:

n = 4 \cdot 10^{12} \cdot 8 \mathrm{bits}

For this calculation it doesn't matter whether we're reading this number of bites from one or two drives since the URE probabilities are assumed independent. The probability of getting at least one error during the rebuild is:

P_{rebuild-error} = 1 - (1 - P_{URE})^n \approx 3.1\%

Note that if I read 10E14 in the original reliability specification as 1014, the probability of a rebuild error goes up to 27%.

This comes out a bit more optimistic than the higher-than-50% figure given in the warning, at least for this specific series of hard drives. I guess whether 3.1% is still too high depends on how much you value your data. However consider that in the original scenario this is the probability of an error given that another hard drive in the array has already catastrophically failed. So the actual probability of data loss is this multiplied with the (unknown) probability of a complete drive failure.

Then again, consider that this is a desktop drive. It is not meant to be put into a RAID array and is typically used without any redundancy. Some people will even scream at you if you use desktop drives in a RAID due to timeout issues. Without any redundancy this probability directly becomes the probability of data loss. And that seems exceedingly high - especially considering that drives up to 8 TB seem to be sold with this same error rate specification. Even with that amazing reliability of reading a single bit, modern drives are simply so large that the vanishingly tiny error probabilities add up.

Posted by Tomaž | Categories: Life | Comments »

Reading RAID stride and stripe_width with dumpe2fs

20.02.2021 20:08

Just a quick note, because I found this confusing today. stride and stripe_width are extended options for ext filesystems that can be used to tune their performance on RAID devices. Many sources on the Internet claim that the values for these settings on existing filesystems can be read out using tune2fs or dumpe2fs.

However it is possible that the output of these commands will simply contain no information that looks related to RAID settings. For example:

$ tune2fs -l /dev/... | grep -i 'raid\|stripe\|stride'
$ dumpe2fs -h /dev/... | grep -i 'raid\|stripe\|stride'
dumpe2fs 1.44.5 (15-Dec-2018)

It turns out that the absence of any lines relating to RAID means that these extended options are simply not defined for the filesystem in question. It means that the filesystem is not tuned to any specific RAID layout and was probably created without the -E stripe=...,stripe_width=... option to mke2fs.

However I've also seen some filesystems that were created without this option still display a default value of 1. I'm guessing this depends on the version of mke2fs that was used to create the filesystem:

$ dumpe2fs -h /dev/... |grep -i 'raid\|stripe\|stride'
dumpe2fs 1.44.5 (15-Dec-2018)
RAID stride:              1

For comparison, here is how the output looks like when these settings have actually been defined:

$ dumpe2fs -h /dev/md/orion\:home |grep -i 'raid\|stripe\|stride'
dumpe2fs 1.44.5 (15-Dec-2018)
RAID stride:              16
RAID stripe width:        32
Posted by Tomaž | Categories: Code | Comments »

Showing printf calls in AtmelStudio debugger window

11.02.2021 16:26

Writing debugging information to a serial port is common practice in embedded development. One problem however is that sometimes you can't connect to the serial port. Either the design lacks a spare GPIO pin or you can't physically access it. In those cases it can be useful to emulate such a character-based output stream over the in-circuit debugger connection.

A few years back I've written how to monitor the serial console on the ARM-based VESNA system over JTAG. Back then I used a small GNU debugger script to intercept strings that were intended for the system's UART and copy them to the gdb console. This time I found myself with a similar problem on an AVR-based system and using AtmelStudio 7 IDE for development. I wanted the debugger window to display the output of various printf statements strewn around the code. I only had the single wire UPDI connection to the AVR microcontroller using an mEDBG debugger. Following is the recipe I came up with. Note that, in contrast to my earlier instructions for ARM, these steps require preparing the source code in advance and making a debug build.

Define a function that wraps around the printf function that is built into avr-libc. It should render the format string and any arguments into a temporary memory buffer and then discard it. Something similar to the following should work. Adjust buf_size depending on the length of lines you need to print out and the amount of spare RAM you have available.

#ifdef DEBUG_TRACEPOINT
int tp_printf_P(const char *__fmt, ...)
{
	const int buf_size = 32;
	char buf[buf_size];

	va_list args;

	va_start(args, __fmt);
	vsnprintf_P(buf, buf_size, __fmt, args);
	va_end(args);

	// <-- put a tracepoint here
	return 0;
}
#endif

We will now define a tracepoint in the IDE that will be called whenever tp_printf_P is called. The tracepoint will read out the contents of the temporary memory buffer and display it in the debugger window. The wrapper is necessary because the built-in printf function in avr-libc outputs strings character-by-character. As far as I know there's is no existing buffer where we could find the entire rendered string like this.

The tracepoint is set up by right-clicking on the marked source line, selecting Breakpoint and Insert Tracepoint in the context menu. This should open Breakpoint settings in the source code view. You should set it up like in the following screenshot and click Close:

Setting up a tracepoint to print out the temporary buffer.

The ,s after the variable name is important. It makes the debugger print out the contents of the buffer as a string instead of just giving you a useless pointer value. This took me a while to figure out. AtmelStudio is just a customized and rebranded version of Microsoft Visual Studio. The section of the manual about tracepoints doesn't mention it, but it turns out that the same format specifiers that can be used in the watch list can also be used in tracepoint messages.

Another thing worth noting is that compiler optimizations may make it impossible to set the tracepoint at this specific point. I haven't seen this happen with the exact code I shown above. It seems my compiler will not optimize out the code even though the temporary buffer isn't used anywhere. However I've encountered this problem elsewhere. If the tracepoint icon on the left of the source code line is an outlined diamond instead of the filled diamond, and you get The breakpoint will not currently be hit message when you hover the mouse over it, this will not work. You will either have to disable some optimization options or modify the code somehow.

Example of a tracepoint that will not work.

To integrate tp_printf_P function into the rest of the code, I suggest defining a macro like the one below. My kprintf can be switched at build time between the true serial output (or whatever else is hooked to the avr-libc to act as stdout), the tracepoint output or it can be turned off for non-debug builds:

#ifdef DEBUG_SERIAL
#  define kprintf(fmt, ...) printf_P(PSTR(fmt), ##__VA_ARGS__);
#else
#  ifdef DEBUG_TRACEPOINT
#    define kprintf(fmt, ...) tp_printf_P(PSTR(fmt), ##__VA_ARGS__);
#  else
#    define kprintf(fmt, ...)
#  endif
#endif

With DEBUG_TRACEPOINT preprocessor macro defined during the build and the tracepoint set up as described above, a print statement like the following:

kprintf("Hello, world!\n");

...will result in the string appearing in the Output window of the debugger like this:

"Hello, world!" string appearing in the debug output window.

Unfortunately the extra double quotes and a newline seem to be mandatory. The Visual Studio documentation suggests that using a ,sb format specifier should print out just the bare string. However this doesn't seem to work in my version of AtmelStudio.

It's certainly better than nothing, but if possible I would still recommend using a true serial port instead of this solution. Apart from the extra RAM required for the string buffer, the tracepoints are quite slow. Each print stops the execution for a few 100s of milliseconds in my case. I find that I can usually get away with prints over a 9600 baud UART in most code that is not particularly time sensitive. However with prints over tracepoints I have to be much more careful not to trigger various timeouts or watchdogs.

I also found this StackExchange question about the same topic. The answer suggests just replacing prints with tracepoints. Indeed "print debugging" has kind of a bad reputation and certainly using tracepoints to monitor specific variables has its place when debugging an issue. However I find that with a well instrumented code that has print statements in strategic places it is hard to beat when you need to understand the big picture of what the code is doing. Prints can often point out problems in places where you wouldn't otherwise think of putting a tracepoint. They also have a benefit of being stored with the code and are not just an ephemeral setting in the IDE.

Posted by Tomaž | Categories: Code | Comments »

Making replacement Chieftec drive rails, 2

08.02.2021 19:47

Two weeks ago I was writing about 3D-printable replacements for 3.5" drive rails used in an old Chieftec PC enclosure I have. The original plastic rails became brittle with time. They often broke when I was replacing hard drives and I've eventually ran out of spares. I've drawn up a copy in FreeCAD and modified the design so that it was easily printable on a FDM printer. At the time of my last post I was waiting to get a sample pair printed. This is a quick update on that. I've tried the new rails and found that they fit well, but need some minor improvements.

3.5" hard drive with the 3D printed rails.

This is how the new rails look when mounted on a 3.5" hard drive. Unfortunately I've specified a wrong diameter for the holes in the STL file, so the imperial-sized screws that go into these drives don't fit. I've manually drilled the holes with a larger diameter on the photo above, but obviously it's better if the rails come correct from the printer in the first place.

These pieces were kindly printed for me by Matjaž on a Prusa i3 MK3S in PETG filament. They feel sturdy and more than strong enough for their purpose. The only thing I was slightly worried about is them getting softer when mounted on warm disk drives. According to the Prusa website the material should be good enough up to 68°C. My monitoring says that no drive got hotter than 50°C in the last 12 months so even in summer they should be fine.

Drive on the new rails being inserted into the Chieftec case.

I was happy to see that the drive with the new rails fits into the case perfectly. Much better in fact than with the original rails. Original rails were very difficult to handle, even when new. These slide in and out with just enough force. The plastic arcs on the top and bottom of the rails engage nicely with the guides and provide enough friction so that the drive doesn't rattle in the case. The strengthened tabs on the side also fit nicely. I saw no need to fix any of the basic dimensions.

Apart from the holes the only other part that needed fixing is the flexible latch. This latch has to be printed in a separate piece. My original idea was that I will glue it to the base part of the rail. However the latches all broke off when I was testing them. Part of the problem was that the cyanoacrylate (superglue) I was using doesn't seem to have good adhesion to PETG plastic. I'll probably find a glue that works better, but I still wanted to change the design so that it depends less on the strength of the bond between the two plastic pieces.

The picture below shows how I've slightly modified the latch. The red part is the latch and purple is the base part of the rail. This picture shows a cross-section in FreeCAD. See my last post for the renders of the complete rail.

The new and the old design for the latch on the drive rail.

The new latch design (top) has a tab that inserts into a slot in the rail. When the latch bends, the slot itself should take most of the torque and hold it in place. The glue should only hold it so that the latch doesn't fall out of the slot when not under tension. In the old latch design (bottom) the glue itself was taking all of the torque when the latch was bending.

I would be even happier with a design that wouldn't require glue at all and where the latch would click into place somehow. However I felt that coming up with the right tolerances for that would require several more round trips between FreeCAD and the printer and, since I'm already at version 4 of the design, I didn't want to waste any more time on this.

Anyway, I've put the updated STL files at the same place as last time. Again I'm waiting for the printouts. Hopefully when I get the chance to test them no further changes will be necessary and I can finally install a new stack of hard drives into my case.

Posted by Tomaž | Categories: Life | Comments »

Notes on the APC Back-UPS 700VA

31.01.2021 10:59

I recently had to replace an old Belkin UPS that had developed some weird problems. After some searching around for a suitable replacement I bought an APC Back-UPS 700VA (model BX700UI) from a local web shop. Even though it's not listed under the Network UPS Tools hardware compatibility list, getting it to run under Debian was relatively simple. Still, here are some random notes about it in case anyone else finds them useful.

APC Back-UPS 700VA

Image by Schneider Electric

As this blog post reports, the UPS is fully supported by the usbhid-ups driver in NUT. I'm currently using version 2.7.4 as packaged in Debian Stretch. Apart from setting the driver in /etc/nut/ups.conf as described there is no other special NUT configuration necessary.

I also had to add an udev rule to change the owner and permissions of the USB device so that NUT could access the UPS. The correct rule can be found in this answer. Note that the "could not detach kernel driver from interface" part of the error message you get if this rule is missing is somewhat misleading.


I've been testing this UPS for around a week on a test setup and I didn't find any problems with the basic functionality. Switch over to battery works fine. The system shuts down cleanly when the UPS reports battery low and correctly turns off the UPS output afterwards. It also comes back on when the power returns.

My old Belkin UPS seemed to impose a hard 30 minute battery run time limit. It would not run the load for more than 30 minutes and would report that the battery is empty regardless of its state. This UPS doesn't have that limitation and will happily run on battery for hours if the load is low enough.

I did increase the battery.charge.low and battery.runtime.low to 20% and 5 minutes respectively. These settings determine when the system will consider the battery depleted and shut itself down. While I didn't find any problems with the defaults (10% and 2 minutes), they seemed to leave very little reserve. This reserve battery charge comes handy if the line power comes back for a minute and goes away again. In this case the UPS must have enough run time left to boot the system back up and shut it down again. Since the battery is large enough for my use case I thought increasing this safety margin made sense.

Note that setting the variables using upsrw requires that a upsd user is both defined in /etc/nut/upsd.users with the permission for the SET action and also has a permission to connect to upsd in /etc/hosts.allow. I overlooked that latter part and spent some time banging my head over the ERR ACCESS-DENIED error.

I'm not sure how persistent the settings done with upsrw are. They are supposed to write the values to the non-volatile memory in the UPS so they should only need setting once. They should survive across reboots and power losses, but so far I'm not totally convinced they will not revert back at some random time in the future.


As far as diagnostic information goes, this UPS isn't very generous. It doesn't measure line frequency, temperature or output voltage. It does measure input voltage and it returns reasonable values for it when the line power is present, but I noticed that it reports 256 V when the input voltage is in fact 0 V.

I had a quick look at the raw USB HID traffic from the UPS and I didn't find anything particularly interesting. At least it doesn't look like the relative lack of diagnostics is due to missing support in NUT.

The battery takes approximately 3 hours to fully recharge. I noticed some very faint crackling noise from it when it's charging. To me it hears like small bubbles popping when I press my ear against the case. This noise is distinct from the mains hum and doesn't hear like a DC-DC converter running. I don't have much experience with lead-acid batteries, so this is probably normal behavior. I just never noticed it before. Apparently even sealed gel batteries form some hydrogen gas that is then recombined inside with a catalyst.

Posted by Tomaž | Categories: Life | Comments »

Making replacement Chieftec drive rails

22.01.2021 19:32

I use an old Chieftec tower case for my home desktop computer. Instead of the usual screws the case uses purple plastic retainers for mounting 3.5" disk drives. These parts were quite fragile and even though the case came with plenty of spares I broke all of them over the years. I failed to find an off-the-shelf replacement so I went and designed a 3D printable part to replace them. I'm currently waiting to get the prototype printed.

I don't know the model name of the case or whether Chieftec had a specific name for this method of mounting drives. The only identifiable mark on the case is the name "Chieftec" embossed in the front panel. I wrote about it in this blog post so it must be at least 16 years old at this point. After quite a lot of web searching, this 2004 review of the BX-03BL-BL-BL model was the only other reference to this rail design I could find. It has a photo of the rails that look identical to the ones I have although the design of the rest of the case looks different.

3.5" drive bay in the Chieftec case.

I think these plastic rails were badly designed from the start. I remember even when the case was new it was very hard to insert or remove a drive. The rails are formed in the shape of a slight arc. When inserting a drive into the slot they straighten out which grips the drive in place. However the rails are fixed to the drive at two points so they can't flex freely. The other problem is that the triangular catch that latches into the metal case has no spring. You need to bend a relatively short and thick length of plastic to unlatch.

A set of broken and discolored Chieftec drive rails.

The plastic didn't age well. It got quite brittle and also changed color from a bright pastel purple to this purplish-gray. Very soon changing a hard drive in the case meant breaking one or both of the rails that held it in place. These days it begins to crack if I put even a slight pressure on it. An unfortunate flaw since otherwise I like this case and it has served me well for all those years.

The rails have a complex shape that isn't printable on a common FDM printer in one piece. To get a single, flat face that starts on the printer's build plate I removed the outward arc in the base shape. It was problematic anyway. Since holes for the sunken screw heads require an overhang in this orientation, I left a thin sacrificial layer that separates the two hole diameters. This needs to be drilled through before the screws can be inserted. I plan to use normal screws instead of the knurled pins the original rails used.

Replacement Chieftec disk rail, top view.

I moved the catch to a separate part that must be glued to the base part. It's mounted on a thin leaf spring that should provide the flexibility I was missing in the original. This part can be printed with layers following the spring shape which should make it more resistant to bending. I also extended the tabs on the sides to make them stronger. After the handle these were the second most common thing to break when unlatching the rail.

Replacement Chieftec disk rail, bottom view.

This was the first more complicated mechanical part I designed using the FreeCAD's Sketcher Workbench. I like the visual way of defining lengths and constraint on shapes mapped to object's faces. It's much clearer than the approach I took with my previous 3D printing projects - using cubes and other basic primitives from the Part Workbench, placing them using Python expressions and using unions and subtractions to get the final shape.

The problem I encountered however was that Sketcher isn't very convenient for experimenting. Because of the topological naming problem you can't easily go back and change things on a model. So far I've already ended up redrawing this shape 3 times from bottom up. Changing something in the sketch for the base shape tends to invalidate all other sketches for pockets and pads that are built on top of it. I don't know how to fix the missing edge and face references without starting all over again.

I'm now waiting to get these new rails 3D printed and I'm yet to see how they turn out in practice. This is the third iteration and I think I got all the measurements right. I'm mostly concerned if the new rails will fit snugly enough into the case to prevent the drive rattling and at the same time not be too hard to insert and remove. If they are too loose I may just add some rubber padding like the 2004 review I linked above suggests.

I've made the current version of my 3D printable design available for download here. I'll write another post when I have the chance to test these out and if I make any more iterations to the design.

Posted by Tomaž | Categories: Life | Comments »