spengy.net

Update 2023-10-10: the issue with the bike shutting off was corrected by replacing the Bosch Purion controller under warranty.

Introduction

Last year my spouse spoiled me with a new cargo e-bike, the Tern HSD P9 Performance (Note: It's the 1st generation HSD – the 2nd generation has some major changes). I had been wanting a cargo e-bike to do school dropoffs and small errands. I've been commuting to work by regular “acoustic” bikes for years, but hadn't tried an e-bike until the Tern. Since then, we've put about 700 miles (~1100km) on the bike. Here are some thoughts.

“Folding” and Storage

The HSD is often described as a “folding bike.” Usually, when you hear folding bike, you think of a bike that folds in half for extremely compact storage or transport. With the HSD, it just means that the really long stem/headtube thing can fold. There's even an included rubber strap and This is helpful, because this part of the HSD is very long compared to “standard” shaped bikes, but not nearly as helpful as being able to fold the entire frame in half in the middle like some other folding bikes. Even with the handlebars folded down, it's not that easy to fit this thing in the back of a car.

Another storage feature: the HSD is designed so you can tip it back and stand it vertically on the back of the integrated rear rack. This is pretty cool and could really come in handy if you wanted to store the bike upright in a small space. In practice, I have been way too paranoid to do that. I fear that it would be far too easy for kids or others to tip over the bike from this position. I've considered attaching some kind of bungee anchor to the ceiling of my garage to prevent the HSD from tipping from vertical... maybe someday. In the meantime, we've just been parking it like a normal bike with the kickstand.

E-System

The HSD P9 Performance comes with a 500Wh Bosch PowerPack and a Class 1 Bosch “Performance” motor capable of 65 Nm of torque. You control it with a Bosch Purion display.

The battery can be removed from the bike (key required) and charged separately, or you can plug the charger directly into the bike, with a charge port on the battery mount, which is protected by a weather-resistant rubber flap. In practice I pretty much always take the battery off to charge it, because the cord from the Bosch battery charger brick is awkwardly short and I can't plug it into the bike in my garage without leaving the brick in dangling or in the way in the garage.

The bike has 5 assist levels, ranging from “Eco” to “Turbo.” I keep it on Eco most of the time, but my spouse likes to use Turbo almost constantly. With Eco, the bike feels roughly like a normal bike (no mean feat for a bike that weighs ~60 lbs (~27 kg)), or perhaps a bit easier than a normal bike. On Turbo you really feel the motor helping, making all but the steepest hills easy, even with kids or cargo.

This is a mid-mount motor pedal-assist bike. Meaning that the motor is mounted in the middle with the chainring and there is no throttle. Instead, the system has torque and speed sensors. Based on your assist setting and a measurement of how hard you're pedaling, the system adds some extra assistance automatically. I like this approach as it's more like a normal bicycle vs. some e-bikes with throttles that feel like they would rather be electric motorcycles, but just added pedals as a regulatory endrun.

In Eco mode on flat terrain, the battery is good for about 70 miles. In practice, around here, we can usually get around half of that, which IMO is more than sufficient.

Shifting

The bike is a 1x9 drivetrain. One chainring, 9-cog cassette. It uses an indexed trigger shifter, which I like. One of the derailleur/tension pulleys in the back hangs very low to the ground, so you want to be careful riding over rough terrain and keep in mind that this is no mountain bike.

Fenders

The bike comes with integrated front and rear fenders which are great. They don't rattle or make any noise and feel very solid. I think it helps that the wheels on this bike are pretty small, so the fenders can also be small and the fender stays thereefore have good leverage.

Brakes

The Shimano hydraulic disc brakes on the front and rear work well, though sometimes noiser than I would like. I think I have a sticky piston on the front caliper that I need to address. But these brakes seem to stop the bike well even with passengers or heavy cargo.

Lights

The bike has integrated front and rear lights. They can be turned on/off from the Purion display and are powered by the drive battery, which I appreciate. The front light is pretty bright, though perhaps not so bright as some of the brightest USB-rechargeable standalone bike lights. Overall, I'm happy with the lights and I haven't felt the need to bring another bike light along during nighttime rides.

Cargo Carrying / Accessories

The HSD comes with a built-in rear rack that is very stable/sturdy. And it has to be. Because whatever optional passenger seating you may want to install is supported by this rack.

The bike doesn't come with passenger seating or bike cargo-carrying boxes. Instead, you have to buy and install accessories to help carry whatever people or cargo you want. This makes sense, because there are many things you could do with the bike and there's no real one-size-fits-all solution that they could sell. But it's also a bit frustrating because the official Tern accessories are a bit pricy. We wanted to carry our kids, so we got the “Captain's Chair” and “Joyride Bars” and “Sidekick Footrests”. This is a good setup to carry one passenger or some cargo (in practice, we sometimes cheat a bit and cram two kids in the seat). But these accessories alone cost us a few hundred dollars.

Will it Bus?

There was actually a period of time when I was commuting to work with this bike, which included one leg of the journey on a bus. This bike actually fits (barely!) the Sportworks bike racks found on the front of many buses. Now, this bike is heavy. In base form without accessories, it weighs in at a bit over 55 lbs (~25kg). Lifting it on and off of the racks is not super easy, but doable. Also, make sure to check with your transit authority's rules/regulations on weight for bikes, and also check the rack manufacturer's specs. I checked all of this and made sure that the HSD was barely within the maximum length and weight limits if I took the battery off.

One gotcha is that the Sportworks racks our buses use secure the front wheel with a telescoing arm that you lift over the top. Because the HSD's wheels are so small, this arm doesn't have good leverage to hold the bike down and could possibly slip down the wheel while the bus is moving/rocking. When I first started taking this bike on the bus I had horrible visions of the bike sliding off the rack on the freeway. But then a bus driver had a good suggestion – to use a bungee or velcro strap to prevent the rack arm from sliding. I used a velcro strap for this and it provided a lot of peace of mind.

Misc Gripes

Update: Our local bike store worked with Bosch to replace the Purion controller (under warranty) and that seems to have resolved the issue with the bike shutting off.

  • We've had a few issues with the bike turning off mid-ride and not wanting to turn back on. We've taken it back to the bike shop twice now. Most recently, the bike store has been able to reproduce the problem and they're talking with Bosch to figure out the problem.
  • Otherwise, my only complaint is the price. It is an expensive bike. So expensive that you'll probably be paranoid about locking it up outside the grocery store, etc.

Conclusion

It's a good, if expensive, bike. We've got a lot of use out of it, eliminating many short trips by car. It rides like a regular bike but can carry a decent amount of cargo or passenger. The accessories are pricy but nice.

Would I buy it again? Yes. But I might buy ones of the bigger GSD models, instead, so that I can carry two passengers more easily. And I'd prefer one of the belt-drive models Tern offers now vs the traditional chain / derailleur setup on this bike.

Book: Witch King by Martha Wells

Published: 2023

ISBN: 978-1250826794

Martha Wells is perhaps best known for The Murderbot Diaries, a series of sci-fi stories about a misanthropic cyborg “security unit” that hacks its own internal governor so that it can watch soap operas inside its own head. Those stories are really fun and I recommend them.

Most recently, in 2023, her new fantasy novel Witch King was released. After a long wait in the hold queue at our local library, I was able to get a copy and read it.

Warning: some minor spoilers follow.

Witch King is about a demon named Kaiisteron, or Kai. Kai and many of his fellow demons live peacefully within tribes of nomads, inhabiting some of the bodies of their dead. But everything changes when the Fire Nation Hierarchs attack. Wielding powerful magic, the Hierarchs take over much of the world, killing many and enslaving others. Many demons, including Kai, are captured and imprisoned. A charismatic mortal, held as a hostage by the Hierarchs, hatches a daring plan to escape or at least achieve some small revenge in the attempt. The plan involves freeing Kaiisteron and the other imprisoned demons, and then killing some powerful Hierarch magicians. It doesn't go all to plan, but works out in a surprising way, resulting in, essentially a revolution and overthrow of the Hierarchs.

The book actually opens many years after all of that history, and explains it in bits and pieces with “flashback” chapters interspersed throughout the “present day” narrative. In the present day, an older, and more jaded/bitter (for reasons that become clear in the flashback chapters) Kaiisteron is again imprisoned as part of a nefarious plot, seemingly an attempt against a post-Hierarchs-overthrow coalition of nations called “The Rising World.” It turns out that Kai's latest captors are woefully unprepared to deal with him and he quickly escapes using magic fueled literally by the deaths of his enemies and his own pain 🤘. However, trouble is in close pursuit. Kai frees one of his close friends, the witch Ziede, who was also imprisoned. Ziede and Kai quickly discover, through magical means, that Ziede's wife Tahren is also missing. They set out on a quest to find Tahren and search for answers as to who is behind it and what their motives are.

Overall, I enjoyed this book quite a bit. The conclusion was very satisfying to me. The reason behind the plot to imprison Kai and his friends becomes clear and it was a bit of a surprise to me. But like all the best surprises, it makes sense in retrospect. A lot of the history/context provided throughout the book in the “flashback” chapters is related to this conclusion/payoff. I enjoyed Kaiisteron as a character and he had a lot of cool moments using his supernatural abilities. The magic and the worldbuilding is very good. Wells throws you into the deep end right away, and you'll have to pick up almost everything through context. There's not a lot of spoon-fed exposition in this book. There is tons of backround that was only hinted at and that seemed very delicious. I'd love to learn more about this world.

One thing that was difficult for me was keeping track of names. I don't know why, but between the present day and flashback chapters I had a hard time keeping track of some of the cast. I also had a bit of trouble remembering which was which of some of the nations, cities, and organizations, as well as how they related. This was made a bit more difficult by the fact that some of the allegiances change between the flashbacks and the present day chapters. I think this is a fairly minor gripe, and likely mostly my fault for not reading this book faster. I think I lost some context in between reading sessions.

Ultimately, as I think the dust jacket mentions, the book is about revolutions and how they can be fragile, requiring lots of different people to work together. Kai and his friends are in many ways an unlikely group of allies, and some of the people who you'd think would be closest to Kai are, well, not.

A solid book. I recommend it.

Introduction

I wanted to cross compile Rust programs into self-contained, statically-linked binaries targeting embedded Linux devices, like my MIPS-based router. There are a number of Rust cross-compilation guides/tools, but none seem to do exactly what I want. This is what worked for me.

Prerequisites

I suspect the experience will be substantially similar on any x86-64 Linux distribution). But here's what I'm using:

  • Ubuntu 20.04.1 on x86-64
  • Rust 1.49.0 (2020-12-29)
  • Buildroot 2020.02.9

Installing Rust

First, install Rust. Follow Rust's own documentation for this.

Install MIPS target for Rust

Rust comes with a utility for managing targets and toolchains, rustup.

Use it to install precompiled Rust libraries for a musl-based MIPS target. Rust supports using musl to produce fully static binaries.

$ rustup target add mips-unknown-linux-musl

After that installs, you might be thinking “Sweet – now I can cross compile Rust programs into self-contained binaries for MIPS. All done!”.

Nope! If only it was that easy. We also need to provide Rust with a cross-compilation toolchain. I like buildroot so let's use that.

Building a Cross-Compilation Toolchain with Buildroot

Buildroot is a great project that can help you build cross compilers or even full Linux firmware images. We're just going to use it to build a cross compiler, though.

  1. Download a buildroot tarball from Buildroot's download page.
  2. Extract it, and cd into the buildroot folder.
  3. Run make menuconfig to configure some options:
  4. Under Target Options configure the target architecture and architecture variant. In my case, this is MIPS (big-endian) and MIPS32R2.
  5. Under Build Options configure buildroot to build only static libraries. We want to produce self-contained Rust binaries and this is slightly easier if we don't even have any dynamic libraries to mistakenly link against.
  6. Under Toolchain:
    • Choose musl as the C library. It's small-ish and works well with static linking.
    • Select the Kernel headers version. You should select a version less than or equal to the kernel running on your target device. Things built against older headers will almost always work on newer kernels, but this doesn't work in the other direction. If the version you want isn't listed in the menu, there is an option to enter a free-form version. I've built buildroot toolchains against header versions as old as 2.6.36 just fine.
    • You can also enable C++ support, gdb, and other options here. These can be nice to have later for other projects, but shouldn't be strictly needed for what we're doing.
  7. Save all your changes and exit menuconfig
  8. Run make and go bake something while buildroot downloads code and compiles a toolchain for you.

Configure Cargo to use the Toolchain

Now we need to Create (or Edit) ~/.cargo/config to point Cargo/Rust to the cross compilation linker produced by buildroot in the previous section.

Mine looks like this:

[target.mips-unknown-linux-musl]
linker = "/home/spengy/buildroot-2020.02.9/output/host/bin/mips-buildroot-linux-musl-ld"
rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-s"]

Some highlights:

  • target.mips-unknown-linux-musl needs to match the target you installed earlier. If you're compiling for some other architecture, make sure everything matches.
  • Obviously, you should adjust the path to the linker to point to the location of cross linker that buildroot provided for you.
  • The rustflags section tells cargo to provide some extra arguments to rustc.
    • -C target-feature=+crt-static tells rustc not to link against some libgcc dynamic libraries. We want everything static linked. I received linker errors until I added this.
    • -C link-arg=-s tells the linker to strip the binary when it's done. This is optional, but will give you smaller binaries. Cargo will have an easier way to do this when this reaches stable.

Compile Something

Now for the fun part! Go to an existing rust project or create one with cargo new and then run cargo build --target=mips-unknown-linux-musl (add the --release flag for a smaller binary). If all goes well, you can grab the binary, transfer it to your embedded MIPS device, and run it.