Project to enable Bosch + Garmin integration

My ten pence worth, could an app on your phone take the live Bosch power data from the flow app and broadcast that from the phone as a Bluetooth power meter to the garmin during the ride?

I’d pay for that app…
 
⚡ EMTB Pro Go Pro — exclusive discounts & ad-free Peaty's 25% off & more · Ad-free browsing · Pro badge See the deals →
As a first step, I'd build an app to read the battery percentage, and simply push it as a notification on the phone. Anything conected will then recieve it - Garmin, smartwatch etc..

It's only going to update when it changes, but its a start.
Then work out how to push it to a custom data field on a garmin or similar. I got Cursor to have a quick go and creating something - need to test it
 
I think I decoded the battery info....
Each bluetooth message seems to start 30-XX where XX is the length of the message
When I looked at the data, my battery was at 99%.. there were only two messages showing the value 99 (63 in hex notation), one of them was

30-04-80-88-08-63
Which I think is the battery level. 80-88-08 must be the code that give battery state of charge.
I cycling a bit more until the battery level dropped 1% in Flow app to 98% and it matched up in the little app I built (well, Claude coded most of it)

If i publish this as notifcation on phone, it will automatically sync to my smartwatch
github here if anyone fancies joining in... (I have no apk available yet I'm afraid)

1750943372758.png
 
It seems to output the assist modes as ascii text as well. I periodcally see the message

"30-22-18-0D-C0-80-12-0A-03-4F-46-46-0A-03-45-43-4F-0A-04-54-4F-55-52-0A-04-65-4D-54-42-0A-05-54-55-52-42-4F"

  • 4F-46-46 = "OFF"
  • 45-43-4F = "ECO"
  • 54-4F-55-52 = "TOUR"
  • 65-4D-54-42 = "eMTB"
  • 54-55-52-42-4F = "TURBO"

    Messages are output at different frequencies depending on what the data is.
 
Brilliant
So I assume once the flow app has connected to the bike the data that isn't encrypted.

Are you sniffing this traffic in an app that you wrote using Claude?
 
I think I decoded the battery info....
Each bluetooth message seems to start 30-XX where XX is the length of the message
When I looked at the data, my battery was at 99%.. there were only two messages showing the value 99 (63 in hex notation), one of them was

30-04-80-88-08-63
Which I think is the battery level. 80-88-08 must be the code that give battery state of charge.
I cycling a bit more until the battery level dropped 1% in Flow app to 98% and it matched up in the little app I built (well, Claude coded most of it)

If i publish this as notifcation on phone, it will automatically sync to my smartwatch
github here if anyone fancies joining in... (I have no apk available yet I'm afraid)

View attachment 163145
I'm going to fork your repo if that's ok; reckon we could have this sending data to a Garmin widget
 
Sure, fork away. I don't even have my bike at the moment (being fixed by canyon) but when i get it back i'll try and gather more data to decode the other fields. Really I just want power / cadence data.

"My ten pence worth, could an app on your phone take the live Bosch power data from the flow app and broadcast that from the phone as a Bluetooth power meter to the garmin during the ride?"

This might work, it depends if a phone can broadcast the right BLE profile to emulate a cycling power meter, I'm not sure at the moment.
 
i tried getting my phone to broadcast itself as a "GATT" (Generic ATTtribue profile) for bluetooth in nrf Connect. Basically just go into nrf Connect app, setup as advertiser profile with uuid 0x1816 which is the id for Cycling/Cadene, and my Garmin watch picked it up as a sensor.

This means the phone simulated being a bluetooh speed/cadence sensor

I didn't do power meter as my watch appears not to support it (Forerunner 165).

But this means the above solution of rebroadcasting the the BLE data from Bosch might work!
 
My theory was broadcasting as existing sensor data more easily fits in with Garmin. You can just record an activity as normal on Garmin and capture the data for analysing in Garmin Connect / Strava etc. I might even relabel "power" as "cadence" as a temporary bodge.

However, I don't know much about the connect IQ side of things, what apps are possible
 
I would very much like to use a Garmin edge mtb,. know nothing about the technicalities of the subject, but I cannot understand why Bosch would restrict access so that 3rd party devices could not be connected via the app, to benefit from the relevant battery state, range, sped etc, except for the fact that Bosch offers a inferior product in the kiox 300 & 500.

Having used the 300 for navigation, it is useless unless your phone has a signal. I guess I will have to run the edge and kiox alongside each other on the bars, but it would be nice to just replace the kiox with the edge mtb.
 
I finally dusted off my various files and filings and posted what I know so far to Github - GitHub - Soarcer/bosch-garmin-bridge: Bridge app for displaying Bosch e-bike data on Garmin cycling computers.

Key Requirements:
• Phone must be previously paired with Bosch Flow app (secret key exchange prevents standalone ESP32/Arduino solutions)
• If BLE sensor mode is needed, the phone needs to be able to run a software BLE peripheral (limits target phones)
• Uses paired phone as intermediary to capture and relay data

Data Transmission Methods:
Standard cycling data (speed, cadence, power) → Phone acts as BLE peripheral for native Garmin datafield display
○ More work on app and less phones can do this, but natively supported in Garmin device, captured data files and fitness sites like Strava
E-bike specific data (assist level, battery, other?) → Garmin Connect IQ custom data fields
○ Separate implementation needed and data is stored as add-on to native fields, will need custom visualizer or after the fact parsing / merging to be visible in most sites / apps

Protocol Progress So Far:
• Bluetooth primary service: 00000010-eaa2-11e9-81b4-2a2ae2dbcce4
• Bluetooth data characteristic: 00000011-eaa2-11e9-81b4-2a2ae2dbcce4


Assist Level (high confidence): 30-04-98-09-08-XX where XX = level 0-4
• Assist Mode Names (medium confidence): 30-22-18-0D-C0-80-12-0A-03-[ASCII_NAMES]
Power (low confidence): A2-43-XX-YY-ZZ where YY = watts, ZZ = additional data for >255W ?
• Battery/Cadence: Still investigating

Data Collection Methodology
Next up is collecting proper test data. My methodology is:

Setup sequence:
  • Start the bike
  • Launch Bosch Flow app and begin an activity
  • As soon as the activity starts, switch to NRF Connect
  • Connect to the bike's primary service and subscribe to the data characteristic
  • Start logging data
Controlled testing:
  • Plan specific tests beforehand (works much better than random data collection)
  • Example - Assist level testing: Pedal at assist level 1 for 10 seconds, switch to level 2 for 10 seconds, continue cycling through all modes at known intervals
  • This makes correlation much easier during analysis
Data analysis:
  • Download the .FIT file from Bosch Flow app
  • Export raw Bluetooth logs from NRF Connect
  • Compare both datasets against current protocol understanding to identify patterns and validate findings
  • Every iteration of AI improvements makes this easier, Claude Sonnet 4 is my current favorite.
  • Do not be tempted or fooled by high confidence analysis! :) The AIs love to pretend to be right, smugly so. Run it though different chats or different AIs to get cross verification.
This controlled approach makes it much easier to correlate Bluetooth data patterns with known bike states and verify protocol accuracy.

Questions, comments and any other feedback welcomed!
 
Great work! I'll have a look for the power data in my logs.
I am 99% certain the battery data is at 30-04-80-88-08-XX if you want to see if it matches in your logs too.
I think the "08" field is identifying the datatype.

I want to tidy up my code, likely storing BLE codes in a json so they can be updated easily, and then push battery data as a BLE sensor data to display on a garmin
 
  • Like
Reactions: GrJ
I've figured out the power data!
It's in field
30-05-98-5B-08-XX-YY

Where XX and YY are the bytes for the power data in "varint" format (Encoding)
i get this data out...
1752006142439.png
Which exactly matches the data saved into the GPX from the Flow app


Now to update the app and push it out as a power meter over BLE
 
I've figured out the power data!
It's in field
30-05-98-5B-08-XX-YY

Where XX and YY are the bytes for the power data in "varint" format (Encoding)
i get this data out...View attachment 163902Which exactly matches the data saved into the GPX from the Flow app


Now to update the app and push it out as a power meter over BLE
Amazing job man! We're looking forward to see it in action. The app is android only i assume?
 
I don't know anything about ios or iphones I'm afraid. here are all the bluetooth hex codes I've decoded if someone else reading wants to create an app (struggling to find much time to work in the android one at the moment...)

BLE data is broadcast as a sequence of bytes (8 bits), represented here by two hex values (4 bits each) such as "5F"

Sample message:
30-05-98-2D-08-FC-01

30 - start of message
05 - total message length
98-5A = Id of data
08 = type of data
FC-01 - the data stored as a variable length int (varint).

For varints, the most significant bit (e.g. the X -> X000 0000) is set to 1 when there is another byte of data following.
This needs to be removed to correctly convert the binary/hex data to decimal.
The length of the message seems to be overdetermined, and set by both the length bit (05) and the MSB in the varint encoding.

Cadence - DIVIDE BY TWO
30-02-98-5A = 0
30-04-98-5A-08-56 = 86 = 43rpm

Human Power
30-04-98-5B-8-4F = 79w
30-05-98-5B-8-B2-04 = 562w
30-02-98-5B = 0w

Motor Power
30-04-98-5D-08-5F = 95w
30-05-98-5D-08-F6-01 = 246w
30-02-98-5D = 0w

Speed
30-05-98-2D-08-FC-01 = 252 = 25.2kmh

Battery %age
30-04-80-88-08-59 = 89 percent

Assist Mode
30-04-98-09-08-04 - Mode 4 (Turbo)
30-02-98-09 - Mode 0 (Off)
 
Maybe someone could create an ios app with the help of AI?

I far as I understand it, the most of the work is already done by robbydobs.

The usable data we need from the bosch system he has already figured out. We need 'just' an ios app to forward it to the garmin simulated as BT sensor.

From my point of view just data for cadence and power being forwarded to garmin would be a really great contribution!
 
I'm guessing this never got anywhere? I just ordered the new top tube screen for the Bosch Gen 5 motor. Hopefully I'll be able to see my cadence and power output on there. Kindof a bummer that they don't link up with Garmin. My Shimano would link up to Garmin, but realistically it showed battery in % and everything else I didn't really care about. But the Battery in % was huge as the display was only in 20% blocks. I think Bosch showing it in 10% color changes on the top tube doesn't make me as concerned to see exactly where my battery is. I think that new screen is supposed to display battery in a percentage as well.
 
Maybe someone could create an ios app with the help of AI?

I far as I understand it, the most of the work is already done by robbydobs.

The usable data we need from the bosch system he has already figured out. We need 'just' an ios app to forward it to the garmin simulated as BT sensor.

From my point of view just data for cadence and power being forwarded to garmin would be a really great contribution!
I am actually working on this but not a developer license so it is harder to have tested
 
  • Like
Reactions: GrJ
I'm guessing this never got anywhere? I just ordered the new top tube screen for the Bosch Gen 5 motor. Hopefully I'll be able to see my cadence and power output on there. Kindof a bummer that they don't link up with Garmin. My Shimano would link up to Garmin, but realistically it showed battery in % and everything else I didn't really care about. But the Battery in % was huge as the display was only in 20% blocks. I think Bosch showing it in 10% color changes on the top tube doesn't make me as concerned to see exactly where my battery is. I think that new screen is supposed to display battery in a percentage as well.
Are these now available to buy?*

*clearly, as you’ve ordered one, but as in in stock or pre-order?
 
I would very much like to use a Garmin edge mtb,. know nothing about the technicalities of the subject, but I cannot understand why Bosch would restrict access so that 3rd party devices could not be connected via the app, to benefit from the relevant battery state, range, sped etc, except for the fact that Bosch offers a inferior product in the kiox 300 & 500.

Having used the 300 for navigation, it is useless unless your phone has a signal. I guess I will have to run the edge and kiox alongside each other on the bars, but it would be nice to just replace the kiox with the edge mtb.
I’m on the same page with this. I have the Kiox 300 and the additional module for extra features and supposedly better gps signal! My experience is that if you ride where there is no mobile signal, the navigation is hopeless.

So, I now want to go the Garmin route! So I hope at some point either someone cracks the code to allow Garmin connectivity, or Bosch finally caves to customer pressure. 🤞
 
I am actually working on this but not a developer license so it is harder to have tested
That's great news! Please let us know if there's anything we could do to help.

Maybe there is a ios licensed developer on these forum that would be willing to help sign the app and push it to the app store so we can test it?
 
That's great news! Please let us know if there's anything we could do to help.

Maybe there is a ios licensed developer on these forum that would be willing to help sign the app and push it to the app store so we can test it?
I been trying to test on my end but can't resolve one of the errors fully as I am a hobby developer. But I will be uploading to GitHub to see if there are any people who are good at adjusting it.
 
I’ll just say, none of this makes sense to me but you should be absolutely applauded for your efforts here.

I may end up with a Bosch powered bike and I have a Garmin head unit. I might be target market.
 
I have been trying to port @robbydobs code to python to test on a PC.
So far I can establish a connection but not much more. I get an unknown payload in the characteristic and then it just stops streaming data. More info here: Help implementing a proof-of-concept · Issue #3 · RobbyPee/Bosch-Smart-System-Ebike-Garmin-Android

I think some pairing / key exchange is necessary before the bike accepts to stream data, as @Soarcer suggests here: GitHub - Soarcer/bosch-garmin-bridge: Bridge app for displaying Bosch e-bike data on Garmin cycling computers.

The easy way is to pair beforehand by running the Flow app on the device... in theory, I could do it (I have a Silicon Mac so I can run iOS apps) but it's not available on the Mac App Store and I couldn't figure out how to extract /download an IPA (a copy) of the Flow app.

I am willing to test your code @Prisondude if you need testers.
 
I decompiled the eBike Flow APK and I am looking at the code.
Am not an expert, but the code appears to be partly obfuscated:
  • some code could not be decompiled (unsure if caused by obfuscation or just a decompiler limitation)
  • use of a "Redactor" helper to obfuscate toString() strings
So far I have not found a lead, but I'm sharing what I got:

Interesting symbols:
  • EBikeAccessToken: interesting one, though not sure if this token is used with the bike or with Bosch's servers
  • EBikeAccessToken.writeTo
  • OcpStoreEbikeAccessTokenRequest
  • BikeDao
  • WiredComponentDao
  • registerForCompanionshipResult with text: CDM Pairing successful
  • ModelTypesConverter: appears to be a converter to/from hex messages to Java objects
  • SpeedManipulationStatus -> interesting, could be a mechanism to detect speed SpeedBox and the like?

Some BLE UUIDs are in cleartext:
Java:
static {UUID uuidFromString = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
Intrinsics.checkNotNullExpressionValue(uuidFromString, "fromString(...)");
ClientCharacteristicConfigurationID = uuidFromString;
UUID uuidFromString2 = UUID.fromString("00002901-0000-1000-8000-00805f9b34fb");
Intrinsics.checkNotNullExpressionValue(uuidFromString2, "fromString(...)");
ClientCharacteristicDescriptionID = uuidFromString2;
}

Java:
private HeartRateMonitorGattService() {
}

static {
UUID uuidFromString = UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb");
Intrinsics.checkNotNullExpressionValue(uuidFromString, "fromString(...)");
    serviceUUID = uuidFromString;
}

Java:
private Bes3EbikeGattService() {
}

static {
UUID uuidFromString = UUID.fromString("0000FE02-0000-1000-8000-00805F9B34FB");
Intrinsics.checkNotNullExpressionValue(uuidFromString, "fromString(...)");
    serviceUUID = uuidFromString;
}

These ones match the ones we already use:

Java:
public McspGattConfig() {
UUID uuidFromString = UUID.fromString("<00000010>-EAA2-11E9-81B4-2A2AE2DBCCE4");
Intrinsics.checkNotNullExpressionValue(uuidFromString, "fromString(...)");
    this.serviceUUID = uuidFromString;
UUID uuidFromString2 = UUID.fromString("00000011-EAA2-11E9-81B4-2A2AE2DBCCE4");
Intrinsics.checkNotNullExpressionValue(uuidFromString2, "fromString(...)");
    this.receiveCharacteristicUUID = uuidFromString2;
UUID uuidFromString3 = UUID.fromString("00000012-EAA2-11E9-81B4-2A2AE2DBCCE4");
Intrinsics.checkNotNullExpressionValue(uuidFromString3, "fromString(...)");
    this.sendCharacteristicUUID = uuidFromString3;
}

Java:
try {
iArr7[RemoteControlFeature.RootCertificateReadout.ordinal()] = 15;
} catch (NoSuchFieldError unused60) {
 
Last edited:
hello, i'm jose from spain.
hello everybody. Time ago i was looking for a device that allows me to link bosch with garmin or others brands.
in the last sea otter europe in girona, i found this company that was intriducing this doongle.

i have for orbea wild 2021 with bosch gen 4 and works well.
i had ask them about bosch smart system , and the answer me that they are working on it.

i attach link for who can have interest (sorry for my bad english)



It seems that Bulcan already offers a device that fully downloads data from the Bosch Smart motor (4th and 5th generation) and sends data to ANT+ and BT bike computers.

Quoting the manufacturer:
Bulcan – Connect your Bosch to GPS and control your electric bike like never before. If you have an electric bike with a Bosch motor and still can't see real-time riding data on your bike computer or smartwatch... you're not doing it right. Bulcan is a device that connects your Bosch Gen 4 or 5 Smart System motor to your favourite GPS, smartwatch or bike computer. In real time. Hassle-free. No complicated installation. You can install it in less than a minute, without tools or complicated cables.
And suddenly, all the information about your electric bike appears exactly where you need it:
Assistance level: Check how much the motor is helping you with every pedal stroke.
Battery charge level and voltage: Monitor the charge level and condition of your electrical system in detail.
Cyclist power: Measure your effort in watts and improve your performance.
Cadence: Pedal better with accurate data.
Ambient and motor/battery temperature: Protect your components in extreme conditions.
Remaining kilometres: Plan exactly how many kilometres you have left.
Motor assistance: Find out how your electric bike performs on each section.
All this combined with maps on one screen. No need to switch devices or views. Compatible with Bluetooth and ANT+. Connects to Garmin (Edge, Fenix, Venu...), Wahoo, Hammerhead Karoo, COROS, IGPSPORT and many others.
The price remains unchanged at €199. It may not be cheap, but it provides much more information than a Bosch subscription.


Bulcan Smart system installation manual
Bulcan Smart system user manual

All that remains is to order, install and test.
 
Keep reading
    Browse all

    Similar Threads

    Community Stats

    Since 2018
    668K
    Messages
    40,749
    Members
    Join 30,000+ Riders, it's free!
    Back
    Top