Replies: 23 comments 1 reply
-
The support covers the blue colored ergometer models. You got it right that the orange ones belong to an earlier generation and their console uses different firmware and protocol compared to the blue (as far as I know). To clarify, when you say orange, those look like these? I don't have access to orange ones (and only have access occasionally to blue ones when I'm in Hungary, last time in the winter, and maybe late summer, or fall next time). However you have a GitHub account and you look like not technically challenged, so I'm curious about a few things.
If that app doesn't handle it then the road will be very rocky. We'd need to reverse engineer. I might try to reach out to some engineers, but when I was developing I was happy that I could get the blue ones working. (BTW, if you have blue ones as well I wonder if my app works with them. The initialization phase takes many seconds but then it should work well. Plus you can pair a HRM to my app as well. Plus in the future I plan to establish an FTMS companion app with which you can convert the protocol to FTMS and so transmit to Kinomap and similar, but that will require a good amount of work.) |
Beta Was this translation helpful? Give feedback.
-
Hi Csaba! Yes you are correct the ones on the picture (I think those are literally the exact machines I use(d) that are on the picture as I believe it is taken in the boat house of UTE :)) There is also one blue (not bull but one generation before that) now.
Yes it does support it and I have decompiled the original kayak first app before (for the purpose of getting some information how the calculation of the metrics work). To my surprise after looking at your app code, I realized that it is significantly different from the blue one (hence was the question). Actually the firmware of the orange MCU only measures the delta times between impulses and the calculation of the metrics are done in the app in line with the Physics of Ergometers. For the blue ones based on your code, the calculation is done on the MCU rather than on the app and the ready calculated metrics are broadcaster.
In terms of bluetooth it uses the same method as the blue ones i.e. serial BLE protocol with a hc-05 serial bluetooth module. I can connect to it with the Serial Bluetooth Terminal also I am able to receive the data (delta time at the start and end of the drive and the recovery I think).
I have to check the UUIDs but I believe its the same BLE serial. Based on my previous test and by looking at your code I believe commands are the same (though when I was testing I did trial and error with the limited help of the decompiled java code of the apk that is... not pretty to say at least :)
I will try the app with the blue shortly, though as the winter season is ended we will work on the water rather then the erg :) Before you start putting significant effort into the orange, I wanted to give you some background for my question: There are two consideration:
Due to the above two I was wondering whether you had any experience with the orange. But based on the above (and the fact that you dont have experience with the orange) it seems that your app simply consumes the metrics that has already calculated by the MCU of the KF. I dont want to send you on a wild goose chase for the orange that are not in production :) Rather I wonder whether you would be willing to discuss how ESP Rowing Monitor could be compatible with your app :) I mean its clear that I could implement the FTMS protocol into ESP Rowing Monitor (actually there is a fork that has done this) and then call it a day, but there are a few consideration:
|
Beta Was this translation helpful? Give feedback.
-
Yes, this is the UTE kayak club. (KayakFirst's place in Budapest was in the Csepel area whereas I reside in Óbuda and MULTI SE was branched off of UTE where I started in 1989 so I reached out to UTE and coaches were supportive and let me show up for some debug sessions). I wish I had known you when I had those two debug sessions recently. I would have tried the orange ergs BTW, but when I peeked at them I didn't see any consoles: (So no need to test with the blue, I've done that, I also tested the two sliding knee canoe ergs downstairs. There's also a catch that the power cable is gone, I used the magnetic cable I got with my engineering sample console. I did several hundred sessions with that engineer console (which is only the console) so I can hone in on everything. First I got it working when you manually have to help my app a little and click the Free Paddle button, and was not ready for the FIBO expo 2023 April which was a missed chance. Then I finally nailed the full procedure (not requiring any tap on the console) by extra session with my engineering console + finally the two in-person debug days.) You got it right, the blue machine's MCU does the calculations for me. I had two major challenges with it:
You are also right that FTMS do not support detailed power curve or most of the time differences between left and right side. The only paddle sport in FTMS is the Rower Data, and since rower ergs don't have dual flywheels or independent left and right paddles, it's a one dimensional movement. KayakPro's Genesis Port FTMS console implements Rower Data. (I have to mention it supports the FTMS spin-down protocol for internal calibration: you paddle the flywheel to a speed range and then let it spin off and the firmware measures how fast it spins down and can calculate the loss off the whole mechanics (bearings, etc). The only more sophisticated protocol I know (and also support) is the original non FTMS C2 Rower protocol. It was developed before the FTMS was standardized though. One quirk of that protocol is that it has quite many characteristics, but some of those can provide detailed force curves. So you may consider implementing that and then you might leverage C2's facilities to log, store, and display such detailed data. In any other case you'd loose that, since I don't think fitness portals have a place to store that or display. One of the main motivation behind my app (besides being able to connect to fitness machines and record the workouts) was integration: upload the workouts to fitness portals directly (Strava, SUUNTO, MapMyFtiness / Under Armour, Training Peaks) or indirectly via FIT / TCX exports (such as Garmin did not provide me upload API - see #5 (comment) and #5 (comment), or Saucony, Adidas, and a list goes on and on). For this reason I only focus on a few metrics to persist:
Non cumulative metrics:
So you say that the orange one would send me the flywheel pulses also in a serial format? I'd like to support that. Can you give me some code snippets in any languages (C / C++ if it's an ESP MCU I guess, or maybe Java)? I should even skip the "old" app's Java code if your method is superior! At the same time I also want to support your ESP based project: because that could still provide an achievable solution to everyone potentially - it wouldn't be a one-off. As I understand your ESP exposes a CSC (https://github.com/oesmith/gatt-xml/blob/master/org.bluetooth.characteristic.csc_measurement.xml) and a Power Meter (https://github.com/oesmith/gatt-xml/blob/master/org.bluetooth.characteristic.cycling_power_measurement.xml) profile. It'd be great if I can identify your ESP by manufacturer name and possible advertisement data. That way I'd know that even though I see Cycling sport profiles but they are really for another sport. This would not be the first time:
If it comes to extra data like left/right balance I can imagine that I might be able to display some of these extra metrics real time but don't persist. In fact soon I'll display the resistance level that way (#337), or also steps / strokes / revolutions (#303). Maybe one day I can also add power curve or left / right balance or both? One advantage I could see if I support your ESP is that if that supports other ergs then transitively I will as well. My app's mission "preventing fitness machines from becoming laundry drying racks" points beyond my app. Let me know your thoughts. |
Beta Was this translation helpful? Give feedback.
-
I'll extend the code changes for the Old Danube #384 so Cycling Power meters could be also used for paddling sports (kayak for now). Note, that when someone use these sensors (speed sensor, cadence sensor, power meter) my app doesn't immediately shuttle to the recording screen, because it doesn't know what other and how many more sensors you'd want to connect to. For example in an extreme case someone can pair 4 sensors: power meter, speed sensor, cadence sensor, heart rate monitor. |
Beta Was this translation helpful? Give feedback.
-
They are slightly hidden, this is how the board looks like. This machine does not have a display only works over the app via BLE. They work of a 9V barrel jack that is located at the top. Sensor is an unipolar hall sensor. It has the HC-05 BLE Serial module that is connected to the serial UART of the MCU. I've attached an FTDI programmer so I could listen to the Serial interface (this is how I was able to understand the commands and then verify from the APK).
Its important to clarify that ESP Rowing Monitor does not use the data of the KF MCU. It measures signals directly from the hall sensor (via interrupt of the ESP32). Its a totally independent from other implementation. All it needs is a hall sensor and magnets on the rotating flywheel. Actually the algorithm uses the delta time between two consecutive signal. From that point metrics are calculated by using quadratic and linear regression models fitted to the data poinst received. Drag factor (to which Kayak Pro refers to as spin down) is actually determined and calculated on every stroke, hence there is no need for specific action "calibration". So basically it functions as a server, what it is missing is the client. Currently it has two ways to connect that is concurrent:
The issue is that the Wifi and web GUI approach is bit crappy (to have all the functions it requires workarounds on the user side)
Actually you are right about the persistence. Currently the way sessions can be persisted is
You dont need a personal one, actually they have an API, this is how connect works, you just need to imitate a full auth session, its pretty easy to imitate this actually (though sometimes they brake it which halts things for a couple of days). There are project in all languages for instance here is one in C#: https://github.com/lswiderski/yet-another-garmin-connect-client. I have an implementation in JavaScript but that does not support 2FA.
This is a setting in the implementation that can be changed before compiling, which means there is flexibility. So basically the initial questions are:
|
Beta Was this translation helpful? Give feedback.
-
I know this might not be the main stream, but I made some code changes, so the "Paddling with cycling sensors" mode (can be switched in the preferences) doesn't only affect CSC sensors, but now Power Meters as well. Can you test a custom APK if it works well both with the CSC and the Power Meter profile your ESP exposes? https://drive.google.com/file/d/1bWmpcZlOIaN6wQmldydE5a3wMKtxyEtC/view?usp=sharing Regarding the initial questions:
Regarding the format it'd be great to settle on something existing if it fits the needs. For example I wonder how C2 can log that data (if it can), they have detailed power curves. Actually you might consider implementing a protocol like theirs? See https://www.concept2.co.uk/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf 0x003D "C2 force curve data characteristic": it's 2 - 288 bytes separated into multiple successive And maybe they have also some format to export that to their log. Otherwise I can accommodate something else. The current JSON export is for MapMyFitness / Under Armour mainly, and the CSV is for Schwinn AC Performance+ Blue Carbon pkg (which is an ANT+ only console) import mainly, but I already tinkered with the CSV for my own activity transfer. The bottom line is I can add something, but it'd be great to leverage something existing, for example imagine if you could take advantage of Concept2's portal, if it can store it for you and display. I didn't explore C2 extensively yet though. (I normally like CSV and JSON better, I've had my bouts with FIT, it's a flexible messaging format, I haven't looked into custom data chunks yet, but that's doable too I guess. I branched the FIT two ways anyway for SUUNTO.) |
Beta Was this translation helpful? Give feedback.
-
That little white module is "dope" with the Hungarian tags such as "SZENZOR". What's DINAMO BTW, could the gadget be recharged from the flywheel? That would be nice! KayakPro's Genesis Port console is also headless, and I need to recharge time to time. It doesn't even have any LEDs, and the Bluetooth Battery Level always reports 100%. Do you have some data logged coming off of that box BTW? I'd still consider to support it if I have enough data. |
Beta Was this translation helpful? Give feedback.
-
That would be an AC power source, but has not been implemented in the orange one we have. I think they got the idea from the Concept2 that is able to power its monitor from the flywheel rotation (but it uses a different approach).
I will get back to you by the end of next week.
Its supposed to be distance (meter) per stroke, but I swapped the two. It should be m/stk :) thanks for noticing I will fix it.
This is a very good question. So this depends on several factors like the number of magnets on the flywheel and the length of the drive phase. For instance on the C2 it could go even higher than 200 data points (data type is float). On the KF blue (6 magnets, so 6 impulses per rotation, but shorter drive than on a row erg) I've never seen going higher than 150 (averaging around 80) In order for this to work over BLE fragmentation needs to be implemented (I just realized this when you asked the question). In this sense C2 protocol is good as it does exactly this, but also the BLE Serial (Serial Port Profile, which you have done for the KF I suppose) could be used. KF approach of polling is lame, I would take advantage of the already available BLE notify that puts the server into the driver seat and makes life on the consumer side much simpler. I will need to see which is a better approach. In the Open Rowing Monitor project the C2 protocol is implemented in javascript and it is a pain.... If need be, I can bring down the size to 2 bytes (an unsigned short) per data point (assuming that negative values are not possible, max value for a data point is around 1,300 and the resolution is lets say 50), but my preference is to keep the proper 3 decimal precision as well as a higher theoretical max value (though I see a potential good compromise here). So when you say slim database, I see your point. For a 5k paddle where there could be 3k strokes that is like 3k * 80 * 2 byte plus the other metrics.... Here probably writing the data directly to the file (json, xml, fit which ever) and persist that can meet your slim DB requirement.
Their export from the log book is junk though they can do FIT, CSV and TCX. There are several reasons 1) they dont record proper metrics during recovery 2) dont record metrics on every second only on per stroke basis 3) force curves are not persisted only displayed (i.e. its not possible to download from log book) and there are many other issues like its recorded like having data for the 15th minute then the next data point is the 17th (assuming there is a recovery in between) and no data nothing between (like HR for instance), it also does not give a shit if you do moderate rowing in between intervals. Here are two exmpales for the same workout (an interval session). concept2-logbook-workout-85891846.fit.txt - note the extension git does not allow FIT so I added txt but its a fit file :) Next steps:
|
Beta Was this translation helpful? Give feedback.
-
Interesting! It'd be great though because then no cable needed. The newer console has a rechargeable battery, which if I recall correctly is either two 18650, or one longer?
Not urgent.
I see. So essentially this is redundant and I can derive it from the speed and the spm (cadence), no need to transmit it.
We'd need to think about the extremes, so if someone is sprinting with high spm and the flywheel has many magnets, then how many bauds would that mean? I'm not sure how easy is it for you, but I'd consider sending binary format data, because then you can save yourself from serializing (printf) all the values, and then I would not have to parse a string either. Not sure if you are hitting the computation limits of the ESP. (BTW I remember back in the day I studied the DOOM source code, John Carmack used some pretty neat techniques, since at that time GPU weren't a thing (nVidia Voodoo just came out and could perform some vertex operations, triangle mesh handling and some texturing). So he resorted to use pre-computed lookup tables to compute certain things such as trigonometric functions combined with some interpolation (your lookup table just has to be good enough resolution), and also for square root or logarithm (https://stackoverflow.com/questions/64355426/how-does-doom-determine-its-cosine-lookup-table-from-an-existing-sine-lookup-tab and https://www.doomworld.com/forum/topic/126997-classic-doom-source-code-questions/). Another technique: you may consider even float -> integer logic, such as some Mandelbrot generators were utilizing (https://math.stackexchange.com/questions/3455100/is-there-a-way-to-perform-calculations-of-mandelbrot-set-using-only-integer-numb), resulting in significant speedup because integer arithmetic is so much faster than floating point. And fractal equation has some floating operations in there.)
Try to use BLE notify! Polling is inferior. As you say it gives the control to my hand, and what if I'm flood you with polls? With notify you can control the flow.
It's just an idea, if it's too cumbersome then ditch it (a.k.a. "Nem erőszak a disznótor"). My thought was if it's possible to implement C2 compatible protocol then maybe even C2 software can connect and be compatible.
If we don't follow C2 or other protocol, then we can decide on our own. There are also techniques to qunitize a floating point mantissa and value separately. Whatever fits better.
I switched to Isar because it is 100x faster than SQFlite. So I'd still persist there, because it'll be probably faster than if I serialize strings into JSON or XML. Especially with SQFlite my main goal was to do as little processing as possible during recording so I would lag the devices. Since I don't have iOS build especially Apple people tend to find a super old 10 year old Android tablet at a bottom of a drawer and - shocker - it's super slow :) The slim would mean that if I add these special fields to the main Record entity, they will be not utilized for 99% of the users and would just take up space. So I can already envision that there will be let's say a And even if your force curve will be different from C2's, I think I'll try to find a common format so I can utilize force curve graphs for C2s as well. So then the feature wouldn't be only for Kayak First.
Interesting! BTW somewhere in the far future I'd want to develop workout profile feature for my app, where you can configure a series of HIIT, fartlek, intervals, and rest sections. This was requested before, but I don't have time.
Whenever you have time. So you need to turn on that feature "Paddling with cycling sensors".
As an end-user usually only more expensive heart rate monitors are able to handle simultaneous connections. Such as my Polar Vereity Sense, or WHAOO TICKR wrist HR can, but the cheap wrist HR I got for my wife's Schwinn IC4 cannot. So I think it boils down to the BT chip first, and then your software stack.
Ok! The best would be if you can supply via standard CSC and Power and then one extra characteristic for custom data (or two characteristics, one for "simple" extra data like recover and such, and a dedicated one for power curve. I guess it'll matter of your software stack). If I were you I'd try to ditch serial, especially for the power curve due to bandwidth. And have enough MTU, not just 20 bytes. What I'll do at some point is probably I'll split our conversation into multiple tickets. For example me implementing the white box's protocol is very low priority, and trying to work with you is higher priority. Maybe even until then we can open up a discussion. I'm excited, but I'll need free time. |
Beta Was this translation helpful? Give feedback.
-
The charger is not implemented in the hardware we have. So it only works of external power. The new does not have this feature it has two 26650 or even bigger.
The metrics shown on the pictures are not a 1 to 1 to what is broadcasted. There are metrics that are calculated on the client side. Currently these are the data that are being via the websocket (its json string that is converted to a byte array)
The first 4 items are seemingly equal to the data that is braodcasted via CPS but the format is different (CPS and CSC uses different resolution and can overflow, these are unsigned long long (i.e. 64bit unsigned ints). From the above the stroke rate or the distance per stroke, as well as pace can be calculated. I think the best would be as you suggested that there are multiple characteristics (the CPS, then on extended, then one for the force curve). I just have to check whether such setup takes more time to complete the notifications on the MCU side compared to having packed everything into one. There is one issue that needs consideration and that is the power data. It is possible to set ESPRM to CSC mode where no power is broadcasted. So I think power will be a permanent element of the extended data characteristics (which makes it redundant when CPS is mode is set but never mind, its a singed short anyway so 2 bytes).
I was able to do multiple simultaneous connection (mobile and watch) and subscribe to the same characteristic. I dont know the performance impact yet I will do more testing but this is promising. Its difficult to predict extremes, but I think 200-250 should be the cap. Any way I think handle forces data will need a fragmentation in place (e.g. if MTU cannot be set to a sufficiently high value). I have some ideas, like setting up the structure where the first two bytes are the total number of fragments and the actual fragment number and the fragments are sent via notify after each other. Client will know that all fragments have arrived once the first two bytes are equal. I think this is something similar to what C2 has but the documentation is not clear on this. I will need to do benchmarks and I have to think through what is the best approach. I see three ways currently:
In all 3 cases the handling of data quality and the fragments needs to be considered. Fragment order can be ensured with the frament number as well as validation whether all fragments are received. Any way I will do some testing and see. I lean toward the indicate option if it does not have a significant performance overhead and ensures data fragment quality and "self" rate limiting by waiting for an answer from the client.
The issue is the frequency of the incoming impulses via the interrupt. The MCU should be able to complete the fitting of the regression curves by the time the next impulse comes in. The limit is not BLE that currently broadcasts once per sec (which will be increased to once per stroke), or the wifi. The problem is that if something blocks the CPU for more than like 5ms (which seems an eternity but some of the iterative calculations are for the theil Sen quadratic regression fitting are pretty involving) it is possible that a new impulse comes in before the processing of the previous is completed that messes up the model.
Dont worry about this, for me this is also a hobby project. Even without your app, I would be trying to implement the new BLE profile and keep using the webGUI , if it allows me to provide alternative to the websocket server (which does not require wifi) :). So I have already profited from this conversation as I have not once thought of using BLE for extended data metrics. |
Beta Was this translation helpful? Give feedback.
-
Hi Csaba! Please find attached the data you requested: These are the commands that are sent from the app These are the responses for each command
Now all metric calculation is done by the app. It uses the start and end times of the drive and recovery (i.e. how long does one rotation take at the start of the drive and at the end of the drive) to calculate metrics. I assume it determines drive and recovery based on whether the impulse delta times are increasing (signalling deceleration i.e. recovery) or decreasing (signalling acceleration i.e. drive). Basically it calculates the drag factor during the recovery - declaration - that is used to calculate the torque, that is used together with the "magic number" - that is calculated based on the weight of the paddler and 12kg that is the assumed boat weight - the speed. Pull force is calculated based on torque and the radius of the flywheel puly. Does not give watts (think). Supporting this machine would mean that you need to implement the full model in your app as per the Physics of Ergometers. Its using a numerical approach which is not accurate as it relies too much on the recorded timing of the input signals and a few microsecond difference could result in high errors (more on this here). This is how I test. Basically I have an MCU that is connected to the sensor pin, which simulates impulses. Then I have an FTDI connect to the BLE UART module TX and RX pins that reads the serial. This is the code that I run on the MCU. Basically it simulates a very steady paddling by faking impulses in varying periods (this is the main reason I know what the response means) Let me know if you have any questions. Also I read it that you have contacted KayakFirst. I was wondering whether their blue machines has the same flywheel as the orange. Specifically, if the inertia of the blue is the same as for the orange. This would be a very valuable piece of information for me. I was able to find inertia value for the orange in the decompiled code (as that is needed for calculating the metrics that is done in the app). Until recently, I assumed they are the same as I assumed that metrics are calculated on the app too, but now I am no longer sure... Thanks. |
Beta Was this translation helpful? Give feedback.
-
Thanks, that's great info for the future - in case I'd want to support the orange ergo straight -, I'll branch it off into a low priority ticket later. Apparently the blue erg's console has only the "6" command as a poll, and the commands "1" - "5" are all related to initialization, configuration and startup. Command "9" is the stop. I got some info about the flywheels: the newest blue erg are codnamed BULL, they have different flywheel characteristics than the earlier models. On top of that there was a version change between the older models around 2020. The orange ones in the UTE are pre 2020, so two generations earlier than the blue BULLs. This also means you cannot apply the same constants and characteristics. The old Android app should be compatible with all of them, so reverse engineering that could yield the required constants. |
Beta Was this translation helpful? Give feedback.
-
I have good news on the above topic. Namely, that I was able to implement a proof of concept of the extended BLE protocol (actually the NimBle stack of the ESP32 is pretty efficient and the high-level implementation of this was simpler than I have thought). I have also done high-level benchmarking and the BLE stack is more performant than the Wifi meaning that disabling the Wifi and broadcasting to multiple clients even with extending data performs better. But still it is possible to enable both services at the same time (so broadcast via Wifi as well as BLE extended). I was also able to manage the additional memory requirement efficiently so that is not a bottleneck either (even with both services enabled). Based on the testing it is clear that only the notify version (proposed option 1. above) is capable of achieving the needed results. Basically, I’ve added two new services (lets call them extended metrics, that has additional data like recovery time, drag factor etc. and handle forces, that has the data for the curve). Extended metricsThe first one is simple, very similar to the standard BLE, and uses a byte sequence to broadcast (e.g. the first two bytes are the average power – as a short –, then the next 4 bytes represent the recovery time – that is an unsigned int – etc.). I will need to broadcast settings and my original intend is/was to include the settings as the first byte in the extended metrics. I have thought about setting up once again a new characteristic (or even a new service) for this, but I don’t see that this makes much sense right now as there are only 3 settings. Changing of settings are done via control point OpCodes. I am convincible though 😊 as this approach does not support future expansion of the settings object (I have plans to make all rower settings editable at some point but that is a very very long term plan 😊). Handle forcesThe handle force was a bit trickier as it needs to be flexible so I ended up using a fragmentation logic (which is similar to the C2 approach), meaning that it will chunk up the data to separate notifications (its important its not indicate, more on that below) where the first byte is always the total number of chunks to be expected, the second is the sequence number of the chunk (so client can track them efficiently). The rest within the chunk are the data in sequences of 4 bytes as a 32bit float. The chunks are calculated based on the MTU negotiated by the client (ESP32 stack support up to 512bytes for MTU). So, for instance the EFR connect android app sets MTU to 247 (this is max), while on my PC web browser BLE stack sets it to 255. So, when determining the chunk size and number of chunks needed to broadcast the data set this will be the base size. Note that only full floats (i.e. 4bytes) are broadcasted, meaning that partial bytes not (so it cannot happen that the first 2 bytes of the float is one notify message and the second is in the next). So basically every byte in the chunk can be converted to its proper float value. Actually, I have thought about allowing partial floats since it could be more efficient (reducing the number of chunks needed) especially for lower MTU sizes, but I think it adds extra complexity to the client as it would need to wait for all bites before it can convert as well as make handling missed packets more complicated. This way the client can be sure that when it receives a package it can be converted and added to an array. If something is missing that data will be missing in the array but would not jeopardize subsequent package conversion (note that actually based on my testing if a package was not received/registered by the client all subsequent once were lost until the next burst, so technically this is less of an issue but still). I think this provides the necessary flexibility. Other considerations and testsI have stress tested the app by setting the chunk size to 23 (basically 23 - 3 for BLE headers - 2 for the first two bytes that actually results in sending up to 18 bytes of useful data per notification, which is technically 4 float number – total of 16 bytes). Android EFR connect app was able to handle up to 15-17 packages without loss. Above that it became unstable, and the end was lost. I think this is fine. MTU of that low is not going to work anyway, I might add some checks for a minimum MTU size that the client needs to be able to handle (or check for the number of packages to be sent) otherwise handle forces are not sent but even IOS can handle higher MTU than this 23 (which the minimum per the BLE specs). I have tested which notification type (indicate or notify) would be the most appropriate. Indicate has so high overhead on the response that indications of more than 4-5 was lost. So that is not a viable option. We need to also decide on the UUID. I am leaning toward using the Concept2 UUIDs for this purpose, but since these are not on spec I think it may create issues along the way. So having something total different might make sense. |
Beta Was this translation helpful? Give feedback.
-
I just saw your response
I never tried the BULL but the blue once before that I have. Just to clarify: I understand that the orange are pre 2020, and anything that is blue is after 2020. Does that mean that anything that is after 2020 has different flywheel right? Actually, it would make sense as the metrics calculated by ESPRM on these blue machines seemed materially lower compared to on water tests so I suspected that something must be different. Thanks for asking around about this. In terms of the ESPRM BLE API, I will be implementing this regardless whether you will support or not as I think it will be simpler on the long term to connect via BLE than to set up a hotspots and all that... probably in the up coming weeks I will create a devel branch with the proof of concept version. Also I will update its WEB Gui to support extended BLE connection, which could also serve as an example how to consume the data. |
Beta Was this translation helpful? Give feedback.
-
Great, so your protocol will be notification based and also binary!
I implemented resistance level, soon will implement step count / revolution count display, and I think within a month or so I'll also move towards your extra values. Yours will be also persisted. I want to display the force curve. I may actually utilize the UI and persistence of this for C2 as well. As for Kayak First goes: it has at least 3 flywheel generations, all of them different. The blue ergs are the latest ones, and the orange ones in UTE are the earliest one. Constants should be hidden somewhere in the depth of the Android app's apk. |
Beta Was this translation helpful? Give feedback.
-
Yes
Yes this is the case (all BLE protocol data is LE so it made sense to keep using that)
I agree, I will generate one.
Using the same UI for C2 makes sense. Essentially they are the same
I dont think the blue one is in the APK. The calculations are made on the MCU (I it has a STM32 chip that has probably sufficient computing power to handle UART the interrupt and the calculations. Also by looking at the data that is broadcasted (6;1711448904253;0;0;0;0.00;0;1;0;1759;4.28;4.51;67;65;46;44;116;110;233;220;17837;316;411;0) This does not have the necessary delta Times data that can be used to calculate the metrics (rather the metrics are calculated on the MCU). This indicates that the APK does not need to have the inertia value for the Blue machines. (it has this for the orange, and I found it already). Also, In the APK there is a firmware bin file (I think its dated to 2019). When I tried to connect with the KF official app in the DB log I was able to find the following: ergo update: "NO ergo update needed - oldDriver: 210206 (available: 191118), oldDisplay: 201008(available: 0). errodDriver: 0, errorDisplay: 0" the firmware bin file's name is 191118_driver.bin in the ergo_firmware folder. I am not sure if its not possible to decompile such firmware file unfortunately... dfasd |
Beta Was this translation helpful? Give feedback.
-
I think I used the developer console (which is blue one) with the APK, however if it does not login any more then I may not be able to confirm. One note for your web app: take a look at Web Bluetooth: https://caniuse.com/web-bluetooth (As you see some browsers don't support it. The biggest problem is the stupid mobile Safari, since on the walled garden Apple ecosystems all of the mobile browsers (even if you install mobile Firefox or Chrome on iOS) are actually Safaris. There were some 3rd party Apple Store apps which would allow Web Bluetooth for mobile Safari, but that could be complicated for an average user. The good news is that finally EU forces Apple to allow alternative browser engines https://www.theregister.com/2024/01/25/apple_eu_dma/. And that can open up easier Web Bluetooth usage via Chrome for Apple users as well. Unfortunately as a side effect of this enforcement is that some PWA features are hurt: https://www.macrumors.com/2024/02/15/ios-17-4-web-apps-removed-apple/) If I could I'd convert my app into a PWA (Progressive Web App) so I wouldn't have to deal with the cumbersome app store release cycles and tackle the iOS and Apple platforms easier (#34). However I learned that the persistence is important and hurt the performance, so there are several things with a web app:
I'm still very attracted to a PWA, if the performance issues can be overcome then the only issue would be to provide nice enough UI, which Flutter is quite good at. There are some special things such as the graphs, however if I think again: I use Syncfusion Flutter Charts, and that is based on the HTML widget under the hood, so probably I can migrate it to the HTML widget. As I'm writing this I'm thinking more and more of a pilot project! |
Beta Was this translation helpful? Give feedback.
-
Just a note from off-band conversations: there are three major type of machines based on flywheel characteristics / mechanics:
|
Beta Was this translation helpful? Give feedback.
-
This is the draft of the BLE API documentation of ESPRM. Feel free to make any suggestions to the structure of the API. Its not published yet so there is flexibility. I have a working example for the client side in JS (technically the code is TS and uses RxJs for the streams) for the WebGUI: ble-data.service.ts.txt. please see the functions Both the client and the server are under testing (which is a bit slow as the weather is good so indoor training is no longer priority, but I have long simulated sessions already) but I consider them as stable for now. BluetoothESP Rowing Monitor provides a BLE interface to get the data and metrics it measures. It supports two concurrent BLE connection, meaning that one may connect a smart watch as well as connect to the WebGUI via BLE. ESP Rowing Monitor supports two standard BLE profiles that allows it to be connected to smart watches and 3rd party apps:
Switching between the profiles can be done through the WebGUI or via BLE Control Point using a specific OpCode (18). The implementation of these profiles complies with BLE standards, although the OpCode used is not standard. Both BLE profiles are fully compliant with BLE standards, making them accessible by compatible devices such as smartwatches. They have been tested with Garmin smartwatches, including FR235, FR645, and FR255. This also means that the device advertisement data is either CPS or CSC (based on the selected profile). Please note that in order for Garmin watches to work with ESP Rowing Monitor, a cycling activity must be selected. Otherwise, due to limitations of Garmin, the watch will connect but will not use the sensor data for metrics. To obtain accurate speed and distance data, the wheel circumference must be set to 10mm when pairing the device with ESP Rowing Monitor. In addition as of version 5.1 experimental supports for custom BLE profiles/services were added.
Extended Metrics ServiceThis Service currently contains two characteristics:
Uses Notify to broadcast the following metrics (which may be extended in the future) as an array of consecutive bytes (i.e. currently a total of 11 bytes, Little Endian):
New metrics are broadcasted on every stroke (after the drive ends) or at least 4 seconds (which ever happens earlier).
Uses Notify to broadcast the handle forces measured during the last drive phase. Full data is broadcasted once per stroke (after the drive ends) or at least 4 seconds (which ever happens earlier). Considering that the number of measurements vary from stroke to stroke (since, among others, it depends on the number of impulses per rotation, the machine etc.) this characteristics may be chunked into consecutive notifies ("bursts") until all data is flushed. The chunk size (consequently the number of consecutive notifies within a burst) will depend on the MTU (max data size per broadcast) negotiated with the client (ESP32 supports 512 bytes, but for instance on android based on experience this is around 250). The first byte in every Notify is the expected total number of chunks within the burst, the second is the current chunk number. Rest of the bytes in one Notify are 32bit floats in Little Endian. Every chunk can be parsed individually without data loss (i.e. the bytes of one float is never broken into two notifies, which prevents data loss on missed packages/notifies). Basically the last Notify within the burst is signaled by the fact that first two bytes of the data package are equal. Below is an example of data where the MTU is 23 bytes (which practically means that 20 bytes would be available to transfer actual data). Considering the first two bytes are reserved, that leaves 18 bytes for the data, but since into that only 4 32bit float can be fitted basically a maximum of 4 floats per Notify can be sent. Handle Forces values: Notifies:
The last Notify (4/4) has the data of only two floats while the rest has 4 each. Settings ServiceThis Service currently contains two characteristics:
Uses Notify to broadcast and allow Read the current settings (which may be extended in the future) as an array of consecutive bytes. It notifies when a setting is changed. Currently the Notify includes only one byte where every two bit represents the status of the logging related settings: _Delta Time logging - (whether broadcasting the deltaTimes is enabled or not) DeltaTimeLoggingNotSupported = (0x00 << 0U);
DeltaTimeLoggingDisabled = (0x01 << 0U);
DeltaTimeLoggingEnabled = (0x02 << 0U); SD Card logging - whether logging to SC Card is enabled or not LogToSdCardNotSupported = (0x00 << 2U);
LogToSdCardDisabled = (0x01 << 2U);
LogToSdCardEnabled = (0x02 << 2U); Log level setting - current log level for the monitor LogLevelSilent = (0x00 << 4U);
LogLevelFatal = (0x01 << 4U);
LogLevelError = (0x02 << 4U);
LogLevelWarning = (0x03 << 4U);
LogLevelInfo = (0x04 << 4U);
LogLevelTrace = (0x05 << 4U);
LogLevelVerbose = (0x06 << 4U);
Uses Indicate and allow Write to change the settings. The structure corresponds to the standard BLE profile Control Point with the difference that custom OpCodes are used for each setting: SetLogLevel = 17U,
ChangeBleService = 18U,
SetDeltaTimeLogging = 19U,
SetSdCardLogging = 20U, The response to the Write is sent via Indicate and the structure follows the BLE Control Point standard (i.e. starts with the ResponseCode - 32 -, followed by the request OpCode, than the ResponseOpCode - e.g. 1 for success or 2 for an unsupported request OpCode). Also a Notify is sent by the Settings characteristic including the new settings. |
Beta Was this translation helpful? Give feedback.
-
Added one more characterstic to the Extended Metrics. This is to allow broadcasting the measured delta times between impulses, so they can be visualised/analysed as well as replayed/simulated for calibration purposes. This is not a general use characteristic so there is no immediate need for it, rather this is a convenience method to record calibration and debugging data. Below is the quote from the draft README
Uses Notify to broadcast the measured delta times if enabled. This serves mostly calibration/debugging purposes as the recorded delta times can be replayed and test various settings efficiently. This feature is disabled by default (meaning that this characteristic may not be visible). It can be enabled by defining The measured delta times are broadcasted once sufficient elements to fill the max negotiated MTU (minus 3 for the header i.e. when the max data capacity) is reached or if 1 second since the last broadcast has passed. Basically if the negotiated MTU is 255 then 63 delta times can be broadcasted ((255 - 3)/4 - assuming that unsigned integer is 4bytes on the system like on the ESP32). Actual frequency will depend on the number of impulses and the speed of the flywheel since. In practice once the system measured 63 delta time value it will send Notify (or if 1 second elapses since the last Notify) to the connected clients. Please note that in certain cases this could be rather resource intensive (e.g. when there are a lot of impulses per rotation), the client should support and negotiate a minimum MTU of 100 (ESP32 NimBle stack supports up to 512bytes). If the MTU is below 100, no Notify will be sent. The data in the Notify are 32bit unsigned integers in Little Endian. |
Beta Was this translation helpful? Give feedback.
-
Opened #505 |
Beta Was this translation helpful? Give feedback.
-
Quick note: I made my mind up about the extended metrics recovery and drive duration and went with the 16bit ushort and a resolution of 4096. Extended Metrics ServiceThis Service currently contains three characteristics:
Uses Notify to broadcast the following metrics (which may be extended in the future) as an array of consecutive bytes (i.e. currently a total of 7 bytes, Little Endian):
New metrics are broadcasted on every stroke (after the drive ends) or at least 4 seconds (which ever happens earlier). |
Beta Was this translation helpful? Give feedback.
-
I'll convert this issue into a discussion. |
Beta Was this translation helpful? Give feedback.
-
We have a rather old KF machine that is orange. I wonder whether that is supported?
Thanks
Beta Was this translation helpful? Give feedback.
All reactions