Byseekel (scrapped version)

Note: This is the scrapped version of Byseekel. I began writing this article in early 2022, until I abandoned it 2022-05-02. The next year I revived and finished the project, but killed many features. That was when I decided to rewrite this blogpost from scratch, with less groundless optimism you'd find in this scrapped version.

Nevertheless, for archival reasons, I am publishing the scrapped version on 2024-02-14. This article is not finished and will not be updated. Please disregard any sentence that suggests otherwise.

I deleted the images a long time ago, so I put in the alt text instead.

Article begins here.

Freddie Mercury famously sang:

I want to ride my bicycle I want to ride my bike I want to ride my bicycle I want to ride it where I like

I, too, own a bicycle, but in terms of performance and condition it is like crap. I certainly wouldn't take riding my bicycle as much of a pleasure as Mercury did. This makes me ask myself a question: What do I need to make a bike trip more enjoyable, or just less painful?

A cyclocomputer.

Image: Cateye cyclocomputer mounted to a bicycle handlebar.

Image credit: Wikipedia user Mnisawlpsa.

Despite its stupid name (cyclo? computer? smh) it's kinda useful. It keeps track of the speed and mileage of your bike, and displays them on a screen. I do know one application where it definitely fits, and that is cheating the 80-kilometers-of-jogging-per-semester mandate from my school office.

Every (able) undergrad in my uni has to accumulate a certain jogging mileage by the end of each semester. For boys, the number is 80 kilometers. To track your movement you turn on your phone's GPS and start jogging. But the loophole is, it can't actually tell if you're jogging, as long as you keep your speed within 3-9 minutes/km. Experiments show I am able to maintain a steady 3-5 min/km on a bike and trick the system into believing I'm legitimately jogging. But even so, I have to pull out my phone mid-journey — a quite dangerous act, even when no one was around — to make sure I'm not going too fast. It would be helpful if I had a device to check that for me and warn me if it exceeds that range.

Guess what, it'd be extra helpful if it could tell me how far I've cycled. It'd also be cool if I could integrate this with my previous semi-successful project, Bikeblinkers. What if besides blinkers, it also had a braking light. Hey, since looking for my bike among 200 others in front of the gate is such a pain, how about something that would tell me where it is?

I should totally make a bucket list of features to implement:

  • speedometer
  • odometer
  • blinkers
  • hazard light
  • braking indicator
  • remote-activated buzzer

And no, this is so much more than a cyclocomputer. As a tribute to Freddie Mercury, I'm going to name my project after how he enunciates the word "bicycle".

Note: this blogpost is continuously being updated.

Hardware

MCU (spoiler: it's AVR)

The project at hand really seems to dwarf Bikeblinkers in terms of complexity, and apparently a single 555 IC simply won't do, and an MCU is necessary.

Which MCU? That is one of the most consequential decisions to make. Up to this point I had a really limited tech stack, because before this one all I knew was Arduino (in this blogpost I treat the Arduino as a distinct "MCU", despite its real MCU being ATmega328P), and I hadn't really done any project that (a) has an MCU, and (b) runs more than one meter away from a computer and a power source that draws power from the mains.

"How about Arduino," I thought. However, upon close inspection, the Arduino isn't all cake.

  • Many Arduino libraries are bloat and inefficient (I'm not judging though)
  • Arduino IDE v1 is a huge pain to use (Yes I am judging here)
  • The Arduino has a 3.3V regulator which isn't useful for this project but consumes quite a substantial chunk of power

That said, I am really hoping there was some alternative. What I want is a bare-bones version of Arduino, where I get the freedom to do whatever I want, but still somehow match the Arduino's capabilities.

I have a few ATmega328P's lying around, so let's try them.

🛒 BOM += ATmega328P

Admittedly, you can embed AVR C/C++ snippets in your .ino or completely write your sketch with it, and if done right it's possible to compile and download an Arduino sketch onto your average ATmega328P, but what's the fun? I'm going to ditch Arduino completely here.

I'm going with avr-gcc and avr-libc, so that I won't touch assembly and as a bonus practice C which I had been learning for 2/3 of a semester.

I fetched a copy of datasheet for the ATmega328P and began reading. The most important information as of now is

  • Pinout (GPIO and SPI)
  • IO registers (PORTB, DDRB, etc.)

I bought a USBASP to download my code onto the chip. Well, in fact it's not the code I'm downloading, it's the hex file obtained by compiling the code, linking the libraries, generating an elf, then summoning Satan.

There are three limitations on the specs one ought to care about when designing software for an AVR chip:

  • Flash size
  • SRAM size
  • Clock frequency

The 328P has 32KB flash, 2KB SRAM and a maximum frequency of 20MHz, but of course we're not using all that much. Of the three what worries me most is RAM, because after 2/3 of a semester of C I know how hard keeping track of memory can be, and for some reason my programs easily ate up megabytes on the OJ. Although 2KB sounds extremely limited, fortunately we don't need fancy algorithms here.

So far, all I needed was the I/O functionality, and the most basic conditionals and loops C has to offer. By lumping up stuff like

#include <avr/io.h>
#include <util/delay.h>
int main(void) {
    DDRB |= _BV(PB5);   // set pin direction of PB5 to output
    while (1) {
        PORTB ^= _BV(PB5);  // toggle PB5
        _delay_ms(500);
    }
}

I was able to blink an LED.

Image: An illuminating LED driven by a DIP-28 ATmega328P on a breadboard

This was all very basic, equivalent to what you would do with pinMode and digitalWrite on an Arduino. Although I was eager to write some real code, I should figure out how my MCU would sense and react to the physical world.

Input Peripherals

Learn to reed

To calculate the speed the bicycle is traveling at, we need to count the number of revolutions. I initially had two ideas:

  • Optical: reflective material on the spokes, IR LED and phototransistor on the fork;
  • Magnetic: reed switch on the fork and a permanent magnet on the spokes.

The optical solution in theory works like this:

Image: Diagram showing the location and schematics of the optical sensor

However, the weaknesses are pretty obvious.

  • Requires power source
  • Requires directional alignment
  • Requires a minimum of three wires (VCC, GND, output)
  • Aluminum foil on a bicycle wheel looks silly
  • Electrical noise

Moreover, the industrial standard for cyclocomputers is reed switches.

Image: Wired reed switch sensor with spoke mounted magnet.

Image credit: Wikipedia user AndrewDressel.

They should be installed on the rear wheel, because rotations of the rear wheel are more representative of the real mileage.

🛒 BOM += reed switch, permanent magnet

Turning big

In Bikeblinkers my control for the blinker circuit was a tiny SP3T toggle switch. It's a bit too small, and it was the source of two design errors in my rev. 1 PCB (look I gotta find someone else to blame okay?)

Image: A SP3T toggle switch mounted on a PCB

🛒 BOM += bigger SP3T switch

I spent a fortune buying one, but it was worth it. Cue the industrial switch!

Image: Industrial SP3T switch in metal and plastic casing. It is thrown to the right.

(ruler in centimeters)

This is all good and such, but one problem is it's pretty likely you'll go all the way to the opposite direction when your intention is to throw it back to neutral. No countermeasure so far.

In February 2022, I came up with an alternative. This is one of those blinker switches they use on e-bikes. It has three terminals: left, right, and common. Flick it to the left, and left connects to common. Flick it to the right, and right does so. Push the stalk down to disconnect all terminals. Two models are sold: one that allows three terminals to be simultaneously connected, and one that doesn't. I picked the former, as hazard lights are just left and right blinkers on at the same time.

🛒 BOM += e-bike blinker switches

Image: A pair of e-bike blinker switches. One of them has its three copper terminals exposed.

Brake it apart

The brake is a little trickier and as of the time of writing I have one idea but have absolutely no idea how it would work out in the physical world.

Image: Diagram of the location and schematics of the brake switches

This design taps into movements of the brake handle but not anywhere further in the braking mechanism, e.g. brake pads. This is because brakes are a key part to bike safety, and it's stupid to tamper it just for a braking indicator.

It comprises of four metal contacts: two on the left, two on the right. Of each two, one is on the hinge and remains stationary relative to the bicycle, but the other is somewhere on the moving part, the handle. They are normally closed, forming an electrical connection. When you pull back the handle to engage the brake though, they are forced apart, breaking the continuity. The two pairs are wired in series and act like a single switch. When it is open, it means at least one brake is engaged, which is good enough since we don't care which.

Sure, ugly hack, but having read Arduino forum threads like this it actually isn't the ugliest one, while in my reach in terms of afforability.

Problems:

  • I can't think of a way to install them other than superglue
  • Rainwater can interfere with the connection, for example shorting a pair of contacts that are supposed to be open
  • The location of contacts have to be super precise
  • Exposed wires are a potential cosmetic imperfection (shut up your bike isn't beautiful anyway)

🛒 BOM += tiny metal contact plates? is that a thing?

📋 TODO: install this to prove I'm not an idiot

WhereTheFuck™

WhereTheFuck™ (WTF) is a bleeding-edge, innovative and disruptive technology which augments and accelerates your vehicle retrieving experience with audiovisual cues.

OK, it's just a regular bike finder. The components are an RF receiver and a transmitter at 433MHz, and a LED-beeper combo, which I will show later.

🛒 BOM += RF TX/RX kit

I am not a radio expert, so what I bought is this kit. It's a cute OOK RF receiver module capable of detecting four programmable signals, which happens to be the number of keys the transmitter has. The transmitter is just your average garage door opener.

Image: Left to right: four-key RF transmitter with antenna extended, RF receiver as a small yellow PCB with 7 bent pin headers.

Looks like I can attach an antenna to the RX module too, so here's a bunch of them I ordered a few weeks later.

Image: 10 RF antennae in plastic packaging. They look like coils with tails and are made of copper.

📋 TODO: install, find out how effective it is

Output Peripherals

Crystal clear

Sure, we're able to calculate our speed and mileage now, but of course we will need to display it somehow. For this purpose we will use an LCD. I have one lying around but it's one of those expensive 12864 models, which is best for complex graphics and a complete overkill for showing a bunch of numbers. So I opted for a downgrade, i.e. 1602, which can only draw characters only (of course, there are hacks which let you draw custom pixmaps. we won't touch them here)

🛒 BOM += 1602 LCD

Image: An LCD with soldered pins

Blinkers, again (also stop lamp)

On one hand, I could reuse the blinkers for my previous project, Bikeblinkers. They are working perfectly fine; they're just four sets of yellow LEDs. I even spent hours designing a 3D printed case, which in the end was never gestated into the physical world, but I feel it would be a pity if no one saw it, so here's a picture.

Image: FreeCAD model of an enclosure for blinkers and a stop lamp

However I want something big. No, not physically, but it's going to amaze everyone.

🛒 BOM += LED strips

Look at these bright boys!

Image: 1 meter long red LED strip resting on my lap. It's drawing 654mA at 12.0V from my DC power supply and hella bright

I bought a red strip for the stop lamp and a yellow one for blinkers, one meter each which is a plenty amount. Given I cut them in appropriate lengths, they don't draw much more power than the discrete LEDs do.

WhereTheFuck™

My idea for WhereTheFuck™ is, I press a button on my transmitter, and something on my bicycle flashes and/or makes a loud noise. That is basically my patent claim. How novel.

I bought this thingie, which is basically two LEDs and a (really loud) buzzer, and it fits in a 22mm hole designed for industrial pushbuttons. When fed 12V it happily beeps on and off thanks to its internal RC circuitry (it's visible from the holes).

Image: Buzzer. It's a black plastic cylinder with a transluscent red cap.

Image: The red lid is removed to reveal two LEDs and a buzzer.

Software

Oh boy, avr-gcc

That's right y'all, it's official: I wrote C for AVR. Embedded programming. One more line on my resume I guess?

Oh, you're asking me if it's enjoyable? You better ask yourself: is it really possible to enjoy C? Is it in all plausibility an attainable outcome that I, a mere mortal of flesh and soul, would have the __asm__ __volatile__ in my command, or possess enough power to conquer the army of CMOS logic gates? No. To write C one must suffer. To control the silicon, one must first wrestle it, then tame it.

State and global variables

Global variables are gross, but atop 2,048 bytes of memory there are no rules. I am representing global state in a pile of global variables. One of them is called state. At the time of writing it holds 5 boolean states in bits [6..2] (imagine the memory saved compared to 5 ints) and the two least significant bits represent a finite state of four.

uint8_t state = 0;

As you might have expected, reading and writing on state is a pain. But AVR C is full of bitwise stuff anyways, so it doesn't really matter if you define macros wisely.

In my main.h I defined the following:

#define BRAKE_BIT       _BV(6)
#define BLINKER_L_BIT   _BV(5)
#define BLINKER_R_BIT   _BV(4)
// ...

(_BV(x) is macro for 1 << x which is used to create a bitmask, with BV standing for Bit Value)

To test a bit, say BRAKE_BIT, I need to do:

if (state & BRAKE_BIT) { /*...*/ }

And to alter it:

state |= BRAKE_BIT;     // set to one
state &= ~BRAKE_BIT;    // set to zero

LCD

Stop lamp

Despite the hardware implementation being unproven, this is one of the easiest features in software. There are only two possibilities:

  • braking, and
  • not braking

If it is the former, we turn on the red stop lamp. Otherwise, turn it off. Here's the code:

void braking(void) {
    if (PIND & BRAKES && !(state & BRAKE_BIT)) {
        state |= BRAKE_BIT;
        PORTC |= STOPLAMP;
        lcd_print("[BR]", 0x19);
    } else if (!(PIND & BRAKES) && state & BRAKE_BIT) {
        state &= ~BRAKE_BIT;
        PORTC &= ~STOPLAMP;
        lcd_clear(0x19, 4);
    }
}

...where STOPLAMP is a macro for _BV(PC1), the assigned pin.

Blinker

Sleep mode

WhereTheFuck™