Objective: Design a product that can be mounted to any multi-gear bicycle that will allow it to shift gears automatically based on sensor input as well as user preferences.
Goals:
Manufacturing
The mass production cost for a single unit must be less than that of a tank of gas
Durable materials
Engineering
Determine when to shift by using the speed and tilt of the bike
Power loss redundancy
Prevent gears from shifting unintentionally in the event of power loss
Ability to still shift gears manually if the user desires
Clean aesthetic
Note: This project was initially designed for a specific bicycle, but later adapted to be universal
The general premise of this system was that the shifting cable would come in from the sides and be put under tension by winding it around a pulley. Sensors, both internal and external to the main box, would feed information to a microcontroller that would allow it to make a decision on when to shift.
An important part of ensuring the success of the shifting system was to have high rotational precision in the pulley pulling the shifting cable. Based on the success of the automatically shifting bicycle project (link above), a stepper motor was used.
To prevent the cable from loosing tension in the event of power loss or when the bike is stored, a worm gear was used. This type of gear can only transmit torque from the worm to the drive gear, preventing the tension from the cable from rotating the motor. In addition, worm gears have the advantage of high reduction ratios. This was extremely beneficial as the stepper motor used was relatively low torque. In addition, the stepper motor had a speed-torque curve peaked at an RPM that was too high for the application.
This revision of the product included a location for the PCB as well as for a li-ion battery. Li-ion technology was chosen for this application as it was relatively safe, inexpensive, and could deliver a high enough current.
Mounting brackets were also designed (bottom) that would allow the device to mount securely to the handlebars of a bicycle (typically between 25 mm - 25.4 mm [1 in] in diameter, depending on the manufacturer).
It was evident that the stepper motor would have been a rather expensive part as the one used here (NEMA17 short body) would have been at least $10 per motor. Given the cost requirement, a redesign was necessary in order to run both the front and rear derailleurs with a single motor. Thanks to the use of worm gears, this would simply mean engaging or disengaging the motor shaft from the worm.
In addition, the previous gear ratio was only able to get about halfway through the rear gear-set (which requires less torque than the front) before stalling. For this reason, this iteration also included a higher gear ratio.
Going into more detail with the engagement/disengagement mechanism, a shaft with gears of different tolerances (clearance or press fit) would move axially in accordance to a solenoid. This would force two custom 'gears' (circled in render below) to either engage or disengage. With this setup, there were only two states: engaged with rear worm and engaged with front worm. These two states would never occur at the same time.
Although the mechanism in the previous iteration successful engaged and disengaged the gears as designed, after taking a step back and looking at the goals of the project, this mechanism was far to complex. Any small amount of dirt would result in the failure of one of the joints. In addition, the cost of assembly would increase by a reasonable margin along with the cost of manufacturing so many different types of parts. With all things considered, it made the most sense to revert back to using two separate motors and fewer custom parts.
This revisions main focus was to reduce the size of the mechanism considerably. The prototyping of this was highly limited given that a 3D printer was the main tool producing a lot of the parts (CNC mill and lathe were not accessible at the time). That being said, this iteration was more of a test of how small the mechanism could be built than actual functionality of the overall system. In addition, the gear ratio in the previous mechanism was still not high enough to shift through all of the bicycle gears on the front derailleur, although it was now able to shift through the front successful.
Given that the front and rear derailleur cables require a different amount of force to shift through all the gears, a good next step would have been to determine that those ratios were in order to make better informed decisions about future designs. This could have been calculated, but since there were many inefficiencies in the system (such as the worm gear system, friction in the cable sleeves, etc), this would have been challenging without arbitrarily selection efficiency coefficients.
This revision was simply a method of rapidly changing the gear ratios without making a new prototype each time. There were a number of discrete gear ratios that this system could achieve between 24:1 and 192:1. To change the ratio, the gears simply had to be swapped out by adding or removing pins in the appropriate location. It was found that a ratio of 168:1 was required.
This revision focused less on the actual shifting mechanism and more on data collection for the microcontroller. The focus was to measure the RPM of the pedals and the rear wheel (more details on this in the firmware section). A red ring was taped to the pedal as it was something quick and on hand for testing. Magnets were adhered to the ring with known spacing and a hall effects sensor was attached to frame. Magnets were added to the spokes of the rear wheel and a hall effects sensor was utilized in a similar manner.
Given that the previous revision proved to be effective, a more permanent solution was needed. On occasion, if the ring was bumped or moved slightly out of position, the system would no longer function. For that reason, a more permanent solution was made that could hold everything together with screws.
After revisiting the actual shifting mechanism from two revisions ago, it was evident that the 168:1 gear ratio was not feasible as it would reduce the speed of the shift down by too much. Going back to the drawing board, it seemed like the logical option was to switch the motor I was using. Instead of using a different stepper motor (which would have been more expensive and larger), it made the most sense to switch to a different motor technology entirely.
To recap, a major requirement of the motor was that it could accurately position itself. Given this requirement, a DC motor with an encoder was an option. After some research, it seemed like there were small, cheap, and relatively abundant DC motors that had a gearbox (with a worm gear) and encoder built in (JGY-371). Furthermore, the encoder was on the motor output, not the output shaft, resulting in a higher resolution. Given the high cost of manufacturing worm gears, it made sense to go with an off-the-shelf option for that price point.
This required a complete redesign. Instead of 3D printing, it seemed easiest to water jet sheet metal and bend it into shape. This gave the part a cleaner look as well as a stronger structure. In addition, it was not necessary to 3D print the part since the complexity drastically decreased with this iteration. Since metal was being used for the main structure, the pulleys were also converted from plastic to aluminum, processed on the lathe.
There was also a space for a breadboard in this iteration, as it was still too early to commit to a PCB. The battery was also not included in the unit as it seemed like it would be better to store the battery externally. This is because it makes the most sense for the user to charge the battery in their house rather than bringing the entire bicycle inside.
The previous revision proved to have a high enough torque to shift through all the gears. There was one major problem with this new iteration: positional accuracy. Given that prototype 7 was the first one to utilize DC motors and encoders instead of stepper motors, a number of new problems arose with the positioning not being accurate.
This problem persisted for a few months, and in that time, many different options were explored to find out what the problem was. One theory was that the pulley was causing a lot of wear on the shifting cable, bending it out of shape. Given that there is only a few mm of pull required to shift between gears, this slight bending could have caused a major issue over time. This revision attempted to remove the pulley entirely from the system to test out this theory. To do this, a rack and pinion was used to convert the rotational motion into linear motion. This iteration was the only one that was not physically built; this is explained more in the next revision.
By doing some debugging in the firmware, it was found that the issue was actually with the way the code was reading the encoder (explained more in the firmware section). This issue was fixed and a design similar to prototype 7 was readopted.
In this revision, a full PCB was made (more information in the electronics section) and an enclosure was made using plastic. One important engineering consideration was that the system had to be water tight, and a bent piece of sheet metal would not have allowed for that. Sheet metal was still used in the design for the motor mount, however, because it can handle far more stress before yielding compared to plastics.
This revision was much cleaner than any previous designs because all electrical connections were internal to the box, eliminating free floating wires.
The previous iteration was fully functional in terms of accurately shifting through both the front and rear set of gears. Given the success, the only thing left to do was make it more aesthetically pleasing.
This revision was made much more compact and the PCB was moved from its previous location in order to shrink the size.
The use of SolidWorks surface tools was required to get the curvature of the top surface.
In addition to these minor changes, this was also optimized for mass production. There are very few unique plastic parts (3) and they all follow guidelines for injection molding, such as the inclusion of a draft angle.
There are two holes at the top of the enclosure, which are temporarily included so the reset and IO0 buttons are accessible for programming. There are also holes on the sides for additional wires (wires for buttons the user can manually shift gears with), which will eventually be replaced by a grommet or similar.
This is labeled as revision A' because revision A starts from the first PCB
The first circuit was simply an H-bridge driver (L293D) with a microcontroller (Arduino Uno/ESP32). This was used for much of the early stages of the project (mechanical revisions A-H) as it supported both bipolar stepper motors and DC motors.
This revision also included an store-bought accelerometer (ADXL335) and hall effects sensor for testing purposes.
The images below show later versions of the circuit utilizing the ESP32. This microcontroller was chosen for its native bluetooth support, which is used with a smartphone to input user preferences.
MOSFETs used as crude logic level shifting temporarily for hall effects sensors
This was the first PCB made, used in mechanical revision I. This included everything from the previous circuit with the addition of dedicated pins for the hall effects sensors. All required pull-down resistors were included on the PCB.
It was found that the motors require 14V rather than the battery's nominal 12V, so a boost converter circuit is used externally before power is fed into the system. There is also a linear regulator used to bring the input voltage down to 5V for the microcontroller. This is not ideal because a boosted voltage of 14V is brought back down to 5V from a nominal 12V, producing excess heat. This issue is resolved in the next revision of the PCB.
The PCB uses all DIP and through-hole components as they were easy to solder for a quick prototype run. In addition, this PCB was rather large, so space was not a concern.
This revision of the board had a lot of changes made to it.
To begin, the PCB size was cut down to 30% of its original size (by area) due to the mechanical constraints in mechanical revision J; this meant that through hole components were no longer suitable. For this reason, everything was switched to SMD components. For resistors, capacitors, and inductors, the sizes went as small as 0805. This size is both pick-and-place machine friendly as well as easy to solder by hand.
To address the concern of heat wasted in the previous iteration, extra circuitry was added. Instead of going from 14V to 5V, the system must decrease from 12V to 3.3V. This is not a large improvement in heat wasted until the rest of the changes are considered (explained in the next paragraph). Additionally, the boost converter was now added to the PCB instead of it being external (based on the LM2577). This allowed the input voltage to be 12V instead of 14V. A solder jumper was added to the board in case this boost convert circuit failed. Unfortunately, this jumper had to be set such that an external boost converter was used as the on board boost converter was not functioning as expected. This will need to be revised in future revisions.
Previously, an ESP32 was used. This is a breakout for the Espressif Wroom-32 microcontroller. The full breakout board was no longer used and was swapped for the bare microcontroller. This ran on 3.3V (the breakout had a 5V to 3.3V regulator on board), which required a different boost converter. In addition, the breakout board had an LED, which drew about as much current as the Wroom-32 when idle. Without this LED, far less current was running through the linear voltage regulator.
The final major change made was to the accelerometer. Instead of using the breakout board, a cheap QFN accelerometer was purchased. This IC had enough resolution to accurately determine the angle of the bike and operated through I2C, which is a communication protocol that is easy to use with microcontrollers. It was a challenge to solder this IC on as the pins were under the chip, so reflow soldering was employed for this specific component.
Code will not be included in this section as it contains proprietary information. A general description of each revision and some pseudocode will be provided. In addition, only major updates or feature additions will be noted as many incremental changes were made. All code was written in the Arduino IDE in C
The very first revision of code was used to simply run the stepper motors. This was done using a stepper library to speed up the prototyping process. If this library had not existed, the steppers would have to be run using lower level programming, which would not be too challenging. This simply requires well timed pulses between when each of the coils are turned on and off (assuming the use of full-steps).
When the motors were switched from steppers to DC, a lot of the code had to be changed. Running DC motors with an h-bridge driver is far easier than running steppers. The following pseudocode represents the functions written (all pseudocode written will now refer to the paragraph above it):
motor1 clockwise:
turn on driver side A
turn off driver side B
motor1 counterclockwise:
turn off driver side A
turn on driver side B
motor1 brake: (this works because the driver shorts the outputs, quickly converting the mechanical energy to electrical, then heat)
turn off driver side A
turn off driver side b
In addition to this, encoders had to be added to the system. In order to ensure that no encoder steps are missed, interrupts must be used. An interrupt works by stopping the code wherever it is, doing another task, and then resuming the main code. In the case of this project, this occurs whenever the encoder pin reads as high. The ESP32 includes two cores, but the second one will be used later. For this reason, interrupts are used on the same core as the rest of the code.
create global counter variable
when interrupt is triggered:
increment counter
This revision implemented indexing of the gears. In order for the microcontroller to know how far to turn the pulley, positions had to be stored in an array. This array would be created by taking inputs from the console of what position to move to and adjusting this position until the derailleur was in the right location.
create counter variable and set it equal to 0
go through as many cycles as there are gears
receive input from console
if the input is not a 0
move by the specified amount
else
go to next index
Additionally, how to actually perform a shift was added. The idea is to keep the chain as parallel to the bicycle as possible. This means avoiding being in the highest front and lowest rear gear at the same time, for example; the motion should be step-wise. This pseudocode will not be shown as it is proprietary.
This added code to modify the indexing of a specific gear in order to avoid having to run through the entire setup for one error.
receive input from console
run motors so that the derailleur is at the gear with an indexing error
receive input from console
while this number is not 0
move by specified amount
receive input from console
set index of that gear to current value
Code was added in order to remedy the positioning error of the encoder. The code is written such that a certain position is called and the ESP32 will run the motor until that position is received. The problem is that the amount of tension on the cable and the direction that the motor is turning will effect how quickly the motor stops. This revision helps reduce that error. In the future, this will be switched to use a PID control loop as it is much cleaner.
wait for a few seconds after the motor is stopped
see how much the motor has turned since the stop was called
while this error is over a certain amount
momentarily run the motor to compensate
Bluetooth code was added to allow the ESP32 to interface with an Android phone using the Blynk bluetooth client. Recall that the ESP32 supports bluetooth. The phone, over bluetooth, replaced the console inputs. The main bluetooth client was run on the second core as it would allow for smoother data transmission. Pseudocode will not be shown for this as the bluetooth code is spread out throughout the entire firmware.
EEPROM support was added for this version. EEPROM stands for electronically erasable programmable read only memory (the term "read only" exists in the name due to historical naming, and is in fact misleading because EEPROM can be reprogrammed). When a variable is created on a microcontroller, it will only store its value for as long as the device is powered. As soon as power is lost, this memory will be erased. In order to prevent this from happening, non-volative memory, such as EEPROM, must be employed. This is crucial for this project as without it, the ESP32 would loose all indexing information every time the device was powered off. Unfortunatly, EEPROM has limited write cycles, so it has to be used sparingly. The EEPROM will write data byte by byte, which means bitshifting must be used for values greater than 255.
In order to write to EEPROM, this is done for each relevant variable:
determine the low byte of variable
determine the high byte of variable
write low byte to register x of EEPROM
write high byte to register y of EEPROM
In order to read from EEPROM, this is done for each relevant variable:
read low byte from register x of EEPROM
read high byte from register y of EEPROM
bitshift the high byte to the left by 8 bits
do bitwise addition of low byte and shifted high byte
The purpose of this revision was simply to clean up the code. Throughout the course of editing all of this code, a lot of vestigial functions and variables were left behind. This was an opportunity to delete all of those as well as comment what is left. In addition, definitions were used for each pin and register in order to make programming and maintaining more intuitive in the future. This also added 'manual shifting' mode where users can press a button mounted to the handlebars to shift up or down instead of the microcontroller deciding when to shift.
When the revision B of the electronics board was created, there was a problem with the bluetooth. The microcontroller still connected to the phone, but was no longer communicating as expected. After spending some time debugging this issue, the best course of action seemed to be going back to using the console instead of bluetooth for input.
In addition to this, a console menu was written to make interfacing with the microcontroller much easier for the user. The console output would look similar to this:
Select 1 to run full setup
Select 2 to fix indexing for rear gear
Select 3 to fix indexing for front gear
[etc...]
Given the revision to the electronics board, I2C communication code has to be written for the new accelerometer. I2C communication is a rather simple protocol that allows a 'master' device (in this case, the ESP32) with up to 256 'slave' devices (in this case, only the accelerometer) all over just two wires (SDL, or serial data, and SCL, serial clock). Each device has a unique address, allowing the master to select which device to communicate with. This is a simplified version of how the general protocol runs:
The master sends a start condition
The master sends a slave address bit along with a read/write bit (if the master wants to read or write to the slave)
Appropriate slave returns acknowledge bit
Master receives or sends data
Receiving device sends acknowledge bit
Steps 4-5 are repeated until data transmission is complete
Master sends stop condition
Using this information, code was written to communicate with the accelerometer to receive data on acceleration values in one axis. Since two registers are required, bitshifting and bitwise addition were used. All acknowledge bits were omitted for ease of understanding along with other simplifications.
Send start condition
Send slave address with write bit
Write settings for accelerometer (sensitivity, gain, etc)
Repeat appropriate steps for all settings
Send stop condition
Send start condition
Send slave address with write bit
Send data containing address of register (to request which register to read from)
Send read bit
Read two sequential registers (high and low bit)
Send stop condition
Bitshift high byte to the left by 8 bits and add it to the low byte
This was then put through a low pass filter in order to clean the high frequency noise produced by the accelerometer.
Future iterations of the code will implement automatically shifting using all the gathered data.