A modern, low-power and hackable cycling computer designed for bicycle touring

Similar projects worth following
BicycleCompanion is cycling computer designed for long-term use, giving not only speed/distance information but also useful ambient data such as temperature, air pressure/altitude, compass as well as useful features such as calendar, alarm and even nearby storm warning via lightning detection sensor.

Most bicycle tourists use a smartphone for GPS navigation which can become a power hog and require worrying about frequent charging or carrying enough backup power. Thus, a low-power device which can be used to measure distances, time and compass heading can be used to minimize GPS use and even only require paper maps for the most part.

At the same time, when bicycle touring it is interesting to know about current weather conditions which is not information smartphones usually give without connecting to the internet. In particular, BicycleCompanion even aims to warn about an impending storm, something important when outdoors with a bicycle or inside a tent.

In general one can find the typical simple and featureless bicycle computer which only count speed/distance and (supposedly) calculate sport-related information (such as burnt calories and fat). There are also more advanced bicycle computers but they typically rely on a smartphone for obtaining more information (such as GPS location or pull weather forecast), which means more battery on both the device itself and on the smartphone. Furthermore, they seem to be aimed towards the competitive cyclist.

In contrast, for the case of bicycle touring there's much more useful information one could have, such as elevation (current and change), compass orientation, temperature, etc. At the same time, there's no good reason to attempt to replace all the functionality of a smartphone.

BicycleCompanion is a modern bicycle computer aimed towards bicycle tourists, featuring various sensors while still consuming very low power, without requiring a smartphone connection (in fact, it does not provide any wireless connectivity at all).

Currently bicycle companion is in development but the following specifications are more or less defined:

  • Based on STM32L476 low-power microcontroller which allows for different types of sleep modes
  • Uses Sharp Memory LCD display (similar to e-ink, much faster refresh rate). Currently I'm testing a 1.3'' 128x128 display, but I may reconsider going for a 2.7'' in the future
  • Four hardware tact buttons for good mechanical feedback (touchscreens are difficult to use while riding)
  • LiPo battery (possibly a few hundred mAh). I'm aiming for a battery life of at least one week of typical use (ride several hours a day, low use when off the bike)
  • USB recharging
  • Battery level indication (ideally, with an estimate of remaining run-time)
  • RTC time-keeping
  • 3D printed case with some level of waterproofing and accesories (magnet mount, etc)
  • Piezo buzzer for feedback beeps and wake-up alarm or signaling other events
  • Sensors:
    • Reed switch: this is what most low-cost bicycle computers use. Hall sensors may be better but they draw considerable power. As a plus, it can be made compatible with existing mounts.
    • LSM303 accelerometer + magnetometer: mainly to give compass orintation. The accelerometer can also be used to detect bike motion and activate the device from sleep or could be even used as a motion-based alarm
    • BMP280: temperature + pressure sensor. Pressure can be used both as weather information as well as for sea-level altitude information (for measuring elevation changes during rides).
    • AS3935 franklin lightning sensor: this notifies you of lightning strikes within a 40km radius. When outdoors for long periods of time, it may be useful to be aware of impending storms.

BicycleCompanion was originally designed as a teensy shield (which was never built) and then as a custom board based on the low-cost STM32F103 microcontroller (check early logs of this project) and using an OLED for display. This board was built but it was too power hungry. Moreover, it lacked any other interesting sensors to justify its use. Therefore, BicycleCompanion design is the result of this evolution. 

  • The wheels are in motion

    Matias N.11/12/2019 at 22:27 0 comments

    While it may not seem so, from last log entry I made significant advances on this new revision of the project. Thus, I wanted to tell a bit of the project status.

    You may also noticed I renamed the project to BicycleCompanion (dropped the MINI suffix) since I feel this is still the original project but with one more refinement step. I also renamed the (never finished) teensy-based project so that no confusion is generated.

    It is also interesting that from all my (four) projects on Hackaday, this one received a constant influx of about 100 views per week. While it may be the result of google crawling the site I choose to think that there's at least some interested people. So, I hope to provide more project updates soon to show my progress.


    While it may not be the typical choice, I'm particularly inclined towards writing the firmware for my electronics projects using the NuttX real-time operating system. In contrast to other RTOS, NuttX feels like a "tiny Linux" in that it is POSIX compatible and aims for high-level hardware abstractions. This is very good to decouple the logic from the specific hardware and maximize re-usability. However, it also comes at the cost of a steeper learning curve and friction than simple hacking something using Arduino.

    As an initial development platform for BicycleCompanion, I chose to work on the ST Nucleo L476RG board, which has very good support on NuttX and also provides a very nice set of features. However, there are always some little (and not so little) things to improve in NuttX once one starts to poke around various parts of the OS.

    When my Digikey order FINALLY arrived

    Besides some small required changes (like adding support for low-power hardware timers or LPTIM) I found that the power-management algorithm baked into NuttX was a bit high-level and did not let one to have tight control over entering and exiting low-power run modes. For this reason, during the last few weeks I reworked NuttX PM system to allow for different PM algorithms. In particular, I added a separate "greedy" algorithm that always tries to go as soon as possible into a low-power run mode (unless an application or driver requests it to stay at a specific level). This is working really well and I have already handled STOP mode on the STM32L4 succesfully.

    A second important step was to tame the Sharp Memory LCD. While a driver was already present in NuttX it didn't quite fit in a low-power setting. Specifically, the Sharp Memory LCD requires a pulse between 1-60Hz to be sent all time to maintain the currently displayed data visible and at the same time avoid burn-in. The NuttX driver used a simply pin toggling mechanism tied to a timer interrupt. Thus, taking advantage of one of the LPTIM timers, I added support for PWM generation using this peripheral, which allows me to send the required pulse, even while the MCU is in deep sleep. This is also working perfectly.

    In regards to the display, I also added support for the latest littlevgl library in NuttX (it officially supports an older version) and developed a simpler driver to access the LCD from littlevgl. So far I have managed to start designing a very simple system of different screens (time, speed, etc.) and a boring splash screen.

    My next and final step before going into building a BicycleCompanion prototype is to test the other LPTIM in pulse-counting mode, to sense the bicycle wheel turning. I originally considered (as in the previous BicycleCompanion design) a latching hall sensor to receive pulses whenever the magnet of the wheel passed over it. However, after looking at various models, all Hall sensors tend to be a bit power hungry (a few mA when measuring). After much consideration I decided to go the easy and zero-power way: use a plain reed switch. This also has the advantage that I do not need to fabricate all the sensor mounting and so on, since there are hundreds of cheap bicycle computers and can try this on.


    Read more »

  • Present and future of BicycleCompanion

    Matias N.09/29/2019 at 22:34 1 comment

    I've realized I didn't ever posted the final assembled version of the BicycleCompanion Mini. I actually built and tested one, and played with the code. However, I eventually lost a bit of interest. Moreover, I saw that the power consumption was higher that what would be useful and that this would require more work that I was not interested in at the time.

    In any case, here are some pictures of the assembled board.

    As you can see, it has some last-minute fixes. Mostly I forgot about the EEPROM I2C pullps and also the USB D+ pull-up was wrong. Other than that, everything worked perfectly. In particular, the whole circuit which chooses input power from between battery or USB (when available) as input to the regulator worked as expected. The idea for this was to avoid parasitic discharge on the battery while it was actually being charged. I think this is often overlooked in many battery powered devices and it was something I wanted to consider.

    What lies ahead

    While as I mentioned I originally lost interest in BicycleCompanion MINI (in the current formulation of the idea) I actually recovered interest in working in a variation of the idea, based on my own experience going on bicycle tours. In principle, what I would like to achieve is a bicycle computer / smartwatch that can last for weeks on a battery charge and be useful during bicycle tours / camping trips (in my case, I ideally combine the two). The idea is not to build the typical bicycle computer oriented towards the competitive cyclist interested in calories burned, watts, etc. but to provide the basic information that is needed during a long distance tour: distance traveled, time, temperature and similar information. I still don't want to add GPS and all that since I believe a smartphone is much more reasonable to use for this purpose. I also do not want to add bluetooth or any other connectivity since I believe it is not actually necessary and it fact I do not like to depend on a companion app (I like a device that can act independently). Similarly, I found during camping that I wanted to have the time and set wakeup alarms, which made me depend on my smartphone. This is also something which I do not like since I preserve smartphone battery to the maximum and typically turn it off once I get to the campsite. So, having a simple watch which can wake me with a vibration/sound alarm is also something I would like to have. As a result, the idea is to have a single device perform both roles. While the features I mentioned are the basic ones, I'm also interested in adding some other ones such as a compass, which can help during navigation and also to be aware of the sunset/sunrise direction during camping. Having also pressure and humidity besides temperature is also useful (or at least interesting to me when outdoors). On the software side I can also think of a simple calendar which helps tracking and planning during trips.

    Of course to make such a device I need to achieve very low power use in the first place and this is where I'm actually interested in working now. First, I'm in the process of getting a couple of Sharp Memory LCDs to play with, which I believe is the best choice for this. Second, I'm also going to explore the STM32L4 family of MCUs since these are too really low power. Finally, all of this will let me (and actually require for me to) learn about all the intricacies of low power design. All in all, at the moment my idea is for this to be a learning experience and see where it goes. If I start to converge on an idea I may either: a) redefine this project under the new conception or b) create a new project for it. I also may even consider a watch-only (not bicycle computer) in the form of a smartband with a very simplistic alarm/watch function (that's what the second LCD display is for) as an alternative.

    In any case, if you're still interested (or have any comments), please continue following this project and feel free to speak up!

  • Coming soon...

    Matias N.07/31/2017 at 22:56 0 comments

    I just received an e-mail from the PCB manufacturer to let me know the boards are ready =)

    They turned out pretty nice, and even with the blue soldermask. In about a week I'll start soldering one and test it.

  • Finalizing PCB design

    Matias N.07/04/2017 at 22:44 0 comments

    This week I've bought remaining components so I have now finalized the PCB design. I did many changes and I feel I could go on forever so I decided to stop here and give it a rest.

    Anyway, I think that many changes were really positive since I considered how this will fit into the enclosure, what will be the space available for the battery and how things will be connected. First change was to move buttons to underside, where the battery will be. It should fit a 30x40 LiPo cell, which can have 460mAh (at least I have one of that size) and I could simply put several in parallel to improve capacity.

    I also decided to remove most connectors (for battery, hall sensor and display) since they were really bulky. The display will be (in the final version) soldered to a flat cable directly to the board, with some extra length. This way you could disassemble the device and have the screen connected. The screen will be firmly mounted against the top cover so I'm not relying on the connector for mechanical support. The hall sensor will be connected via an external stereo connector. This is also a bit better in therms of removability and waterproofness (yes, I invented those two words). Having a connector which needs to go through the enclosure seems convenient but it impedes any tight seal. In any case, in the future I would do this using spring contacts or something like that, but this isn't available here so I decided to leave that for this version. Finally, same concept can be applied to battery (connect via cable connector instead of having a PCB connector) which I think is a bit more space efficient.

    I will print the design and confirm how it would be in relation to the display. If everything is okay I will send the PCB for fabrication this week. Then, a long wait before I can do anything.

  • BluePill and its annoyances

    Matias N.06/22/2017 at 01:44 2 comments

    So, some updates on the project so far. First, I received my nice new solder station. Second, I started porting the code to the BluePill (was previously using Teensy 3.1) in order to be closer to what the final device will be (as I will use same STM32F103C8T6 microcontroller).

    A bit of surgery

    The first thing I had to deal with using the BluePill is the wrong resistor on the USB D+ line. The board comes with a wrong resistor value of 10k, which should be actually 1.5k (pulled to 3.3v). Now, replacing a 0603 (I believe) resistor crammed between other resistors requires some work: a perfect opportunity to test the hot air of my station. After a few second under hot air (at around 300C), I managed to remove the resistor easily.

    On my finger is the resistor (R10 on the board). Yes, I'm using a french wrench as a ghetto vise. I then later soldered a 0805 resistor of the correct value, which luckily still fitted the original pads.

    USB Bootloader

    Now that USB supposedly worked, I tried flashing the USB bootloader (using the ST Link v2 programmer). Obviously this didn't work (the device would not enumerate on Linux when plugged). After several hours of searching and asking around in some forums, complaining that this did not work, I eventually found out the problem was actually a bad soldered USB connector. So, some flux and a pass of the solder and now I was getting some response from the board.

    With the bootloader loaded I managed to flash the example blink program from within the Arduino IDE. So the second issue was to do this from platformio. This required fiddling a bit with board definition files since it seems a wrong linker script is chosen (you can see the issue here

    Also, I had to configure the board as being STM32F103CB (not C8) which allows me to use the whole 128KB of the flash which is present on most of C8 boards. I needed this since I'm already at 112KB on my code.

    Fighting with Arduino Code

    The final issue I had was with the change of board. I amusingly assumed that since Arduino is mostly standarized nowadays and since I was not using any really advanced or low level stuff the change from Teensy to STM32 should be more or less simple. Wrong.

    The main problem is that the STM32 Arduino port is not as complete or carefully organized as Teensy core libraries. For example, by default Wire is actually software I2C and you actually need to use HardWire to use hardware I2C. This in turn implies that STM32 Arduino port includes many I2C libraries simply because they need to change Wire to HWire. Since these libraries are actually a bit old now, luckily I managed to make this change to my copy of a recent version of Adafruit's SSD1306 driver.

    Another issue is the lack of printf in Print class, which affects SSD1306 driver. I had to resort to calling sprintf() using a local buffer, which was not that bad but a bit annoying.

    In the end I managed to port most of the functionality I had with the exception of RTC. For some reason, when I enable the RTC (using the RTClock library which comes with STM32 Arduino) it hangs. It appears the 32k crystal is not oscilating. I'm not sure if there is (another) hardware issue or there's something missing. This is a bit worrisome since I copied this part of the circuit for my device so I hope it works in my case.

    PCB changes

    All this testing on the BluePill was actually good since I realized I had some design flaws on the PCB. First, I was not grounding the RTC cristal capacitors (ouch). Also, there are some pins which, when SWD is enabled, cannot be used since they are supposed to be used for JTAG. I was using these pins for reading buttons, so I switching pins. Also, I decided to wring back the user LED and tie it to PC13 to conform the BluePill. I think this will simplify any customization down the line.

    What's next?

    A few days ago I ordered some of the difficult SMD components, which should arrive in about a week. In the meantime I will probably go...

    Read more »

  • Porting to BluePill (STM32F103C8T6)

    Matias N.06/18/2017 at 21:45 0 comments

    The last weeks I haven't been advancing much since I was a bit busy. Also, I iterated the PCB design a bit more and simplified some things. I removed power and user led for minimal power use, remove UART port (not of much use if USB actually works as serial) and rearranged some things to be able to have four aligned mounting holes. At the moment, the board looks like this:

    Dat hackaday logo ;)

    You can see I'm still missing the USB 3D model (still waiting to decide if I will get the SMD version or not) and the reset button (which is now a two contact tact switch) for which Kicad does not have a 3D model.

    Porting to BluePill

    Now to the point of this log. While I was waiting for having the PCB ready and everything soldered to port the Teensy-based code to the STM32F103, I decided to give it a try on a BluePill board. After all, Arduino is pretty generic nowadays, right? Wrong.

    I've found some annoyances and difficulties. First, since the BluePill comes without USB bootloader and, in fact, with a wrong pull-up resistor on the USB connector, I had to flash it via st-link. The problem was that after flashing the LED didn't blink and it would seem that afterwards it was kind of bricked. After much trying and browsing I realized that: a) the correct pin code for the LED is PC13, not 13! (so much for standarization) b) I had to define "CONFIG_MAPLE_MINI_NO_DISABLE_DEBUG=1" since otherwise the SWD pins were turned into GPIOs and thus it would not respond to st-link anymore. Fortunately, one can re-flash during reset so I managed to recover from this.

    Once this was solved, I managed to upload a blink example and see it working. Now, I had to build and flash my code for this board. First problem was that several libraries I've been using were actually part of the Teensy platform package. I had to copy these (since they weren't actually Teensy specific) and place them directly along my code. Also, I had to find replacement for Teensy specific code, such as RTC handling code (which luckily seems to be there). Finally, I found out that TwoWire library is actually a bit-banged implementation of I2C! Luckily there is HardWire which can be used instead.

    Once it started building correctly, I found a second problem: binary size was 112kb, above the 64Kb of the STM32F103C8 flash. I tried to reduce it but it does not seem possible. On the other hand, I knew that these line of chips are supposed to actually have 128kb. So, I configured the board in platformio as being STM32F103CB not C8, which has 128kb officially. Also, I had to download a recent version of st-link utilities which allow to override the flash size. While platformio does not support setting this flag, I could flash it manually once the firmware is built.

    And FINALLY I managed to flash it and it was running. I also verified (by reading the firmware back) that there were actually more than 64Kb of flash there and indeed it was.

    Next steps

    So, now that it at least runs I need to repeat the test on the breadboard to test basic functionality on the BluePill. To do so, I still need to fix the USB pull-up and solder the header pins. For this, I'm actually waiting on a nice solder station (with hot air) I just bought and should be getting tomorrow from the post-office.

    I've also already ordered some of the SMD components I needed for the PCB which should arrive in about a week. I will also need to go to the electronics store and buy all remaining components.

    I'm planning on finishing the breadboard test before finalizing the PCB design. Also, I would like to print the design to ensure all footprints are correct w.r.t. to the components I'm getting. After that, I will send it for fabrication.

  • PCB Designed

    Matias N.06/05/2017 at 02:27 0 comments

    In the last days I've been playing a bit more with the code using the Teensy 3.1 based prototype. While there are still many things to be done on the software side, I decided I wanted to design the board based on STM32F108CT6 since it seemed more fun.

    Last week I ordered a Blue Pill board but it seems stuck in the mail so I gave the PCB design a try. In the end, I learn many things and included many components I was considering initially but was unsure to include in the first version or not. I finally designed the PCB with all these included since I realized I didn't really need to include solder them all at once to start testing it since many things are quite "modular".

    After finishing the schematic, which was not really that hard I went on to the PCB layout, which took me quite a while. I think that while the traces are a bit of a mess I didn't want to be overly perfectionist and simply finish it. I'd rather learn from a failed attempt than not do anything at all.

    I must say I'm quite happy about it and, most importantly, it looks good =)

    I think I will source some of the basic components first and then print this in a sheet of paper and check that all footprints match. After that, I will try and send it for fabrication to a local company (which I think actually fabricates using some Chinese company, and thus it can take about a month).

    PS: I couldn't find a compatible 3D shape for the micro-usb connector. And I think the one I would get does not have those two ground pads some I may have to change that later.

  • Random considerations

    Matias N.05/31/2017 at 22:31 0 comments

    Since I can't advance mucj during the week (yeah, work) I've took the time to learn about some things which relate to the project.

    Font rendering quality

    I've been using Adafruit's GFX library for rendering fonts on the SSD1306 and I was really disappointed by the rendering quality of self converted TTF fonts. It seems that it isn't so easy to convert vector fonts to bitmap and expect them to look nice on 1 bit displays.

    When I used NuttX I had a better experience with don't rendering and I realized it used bitmap fonts, which are generally hand made and thus look really nice on these kind of displays. There are several formats for bitmap fonts such as PCF. The nice thing is that freetype library can read it and thus I could use the same fin convert utility from Adafruit. When I get the chance I'll test it but I suspect it will look much better.

    Battery re-charging and permanent settings storage

    I started with the idea to leave out battery re-charging in the device since I couldn't find any SMD ICs locally for that and did not want to include Chinese modules to the PCB. This meant that I would loose settings on the device when taking battery out for re-charging.

    Since the STM32F103 does not have EEPROM un thinking on adding a cheap I2C or SPI chip for that.

    However, I found a local distributor which has many nice SMD components and found that it sells several re-charging ICs. Among those, I found the MAX1555 and MCP73834.

    Thus, in trying to decide if it would be worth to add this feature after all.

    If anyone has any opinion about that, it is welcome.

  • Good progress

    Matias N.05/28/2017 at 18:38 0 comments

    In contrast to my original BicycleCompanion project, this time I made progress much faster.

    The main difference is that in this case I decided to go back to the original approach of using Arduino famework. On the original project I actually switched to using NuttX which was a bit overkill for a project like this (at least, initially).

    Another good thing is that I started using PlatformIO. Since I really hated the Arduino IDE but liked the existing libraries this was the perfect choice. I even can use QtCreator as the IDE. I used some existing libraries to base this: Adafruit SSD1306, Button, MenuSystem and Metro. This has made everything much simpler.

    So far I have the main screens in place (some of which are stubs) which can be browsed with two of the four buttons. These screens are: current speed, traveled distance, moving time, current time, battery level and temperature. Each of this screens will have alternative views which can be browsed with the two remaining buttons. For example, different ways of looking at the speed, distance and so on, or browsing maximum, average stats of each. This is not yet made but will be.

    The part which required more work is the configuration. While I used the great MenuSystem library, having this being nicely rendered on the SSD1306 required a bit of thinking. In the end it looked quite nice (with that Sci-Fi 80s look I love).

    The functionality so far is:

    • Speed in KM/h from reading Hall sensor (using interrupts), which also accumulates traveled distance
    • Current time and date display from RTC
    • Temperature screen stub (this will be finalized using the reading of the actual DS18B20 sensor)
    • Battery charge: reads supply voltage and converts it to a charge percentage (assuming single LiPo cell)
    • Configuration menu allowing to change different settings:
      • Wheel perimeter
      • Exit config menu if motion is detected
      • Time & date setting (not yet finished)
      • Powersaving (screen sleep time)
      • Display settings (inverted mode for higher contrast, dimmed mode for less battery use

    Since I decided not to include battery charging circuit for simplicity for now, I realized it would be imperative to use permanent storage for settings. Since the STM32F103 does not have internal EEPROM (ouch), I need to include an external EEPROM chip for that. Luckily they are cheap and easy to use.

    At the moment I've done all this on a Teensy 3.1 since I had it available for the original project. The next step is thus run the code on a Blue Pill board for testing. Also, I will add the temperature sensor and EEPROM chip.

    After that I will probably start testing battery life and look into using Sleep modes to reduce battery usage to minimum (with such a device, most of the time it does not need to do much).

    Finally, I plan to design a PCB and sent it for fabrication and try to solder everything myself (will see how it goes with all SMD components). I still have to decide whether to include a USB port for programming or use the SWD interface.

    Here you can see some picture of how the user interface looks so far (will probably look better in the future):

View all 9 project logs

Enjoy this project?



Sammy wrote 01/26/2020 at 03:31 point

The hardware tact buttons are a nice touch!! I would like to know more about how this detects elevation/angle! I love the outdoors and going for long bike rides in my spare time!

Sammy |

  Are you sure? yes | no

PointyOintment wrote 09/09/2018 at 11:07 point

What is the physical meaning of km*h (as opposed to km/h)?

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates