Introduction
I have to apologise for the title but I just couldn't resist. The idea of this project is actually exactly that, connecting a crosstrainer (physical device) to the internet, or making it "smart" in other words. I use quotes here, because it doesn't really make the device (or anything else) smart. The main point of this project is to make the device a little bit more interesting to use, but the reasoning can be split in two parts.
The first reason is to quantify and record the exercises done on the device. Recording makes it easier to see any progress or lack thereof. Recording in a digital format also makes it possible to generate nice graphs. It could be done by hand, but it's a bit annoying to do it manually.
Another reason is something called gamification. It's an idea that you get some gratification for getting a reward even if it's an imaginary one. My brain clearly doesn't understand that I need physical exercise to such an extent that some virtual points needed to be created. I'm clearly not an expert on this (gamification) topic and I think that this concept is quite stupid but I kinda sorta see what's the point.
At least one result went exactly as expected. Obviously I had to use the crosstrainer while developing, testing and calibrating my device. Of course that's not enough but it's at least something.
Background
Here's the info about the crosstrainer:
- model is SPORTOP E820
- bought it used for a very good price
- probably cost ~300€ when it was new in 2006 (?)
- has magnetic load and a "computer" (to control the load)
The main problem with this crosstrainer is that it's mostly used as decoration. The good thing is that at least it's not used as a clothes rack, like at my parents place. So I guess I'm already a bit better at that, but there's still some room for improvement.
Research
I did some research for similar projects or commercial products. I did it only after finishing the design, but it doesn't really matter since I didn't find anything useful anyway. I did find some DIY projects. Open Cross Trainer is trying to rebuild a crosstrainer with some extra features, a reasonably intrusive project and doesn't log to cloud, so not exactly what I had in mind. Also I don't need to rebuild mine, it works quite fine for now. Another project is trying to improve the display part of the crosstrainer (including the heart rate). Again not something what I'm after, but there are some things that I could inspect in that project.
Then there's the commercial part. It seems that most of these are designed for gyms like ActiveLinxx and Advagym. They are designed for multiple machines and multiple users trying to improve a whole gym. There seems to be also some for private use (existing in a device you can actually buy) like the iConsole+ but there's no clear explanation anywhere on what any of these actually do. The linked pages seem to be only advertisements.
I also found some funny articles like this one mentioning "Quantified Self" and an app called "Life Fitness Connect" whatever it is. I also found this master thesis that I didn't read, but might read at some point. ActiveLinxx was involved in that thesis and it also mentioned "velocity based training" which sounds silly but apparently is a commonly accepted concept.
Requirements
The most important requirement is to make my device completely autonomous (not the crosstrainer). I originally wanted to add a user selection, but then decided not to, because it would just make the device so much simpler. Also the device could try to guess the user or the selection could simply be done afterwards. Additionally the device shouldn't have any display. The crosstrainer already has a display for the "computer" and there's no (real) need or place for another one.
I wanted to make this device wireless right from the start. The reason is that there's no PC nearby and I'm not installing one just for this reason. Originally I thought about bluetooth, but then I would have to have the PC running and deal with the serial ports, which I really don't like. I then thought about using some nRF radio, but then I would also have to build the receiver. I then remembered the existence of ESP32 which eventually fit this purpose perfectly.
I also wanted to make this device battery powered. The reason for it is that I felt that it could be battery powered and because I simply don't want to make mains powered (USB charger powered) devices unless really necessary. USB chargers are not really intended for constant use (even though I have used them in some projects).
However there lies the problem. The ESP32 is quite power hungry and wouldn't last long using batteries. Even without WiFi enabled, the power usage is noticeable and the device would drain the batteries quite fast. Or at least that would be the case unless the ULP (Ultra Low Power) co-processor is used. I also thought about some ESP32 + AVR combination, but that would be unnecessarily complicated.
I was a bit afraid of using the ULP core, because in the original ESP32 it has to be programmed using assembly. However there are plenty of good examples available and because the assembly part should be really simple. Eventually the ULP part of this project was quite easy. Here are some examples that I've found: esp-idf, wardjm/esp32-ulp-i2c and duff2013/ulptool.
As a bit of a spoiler here, I later realised that this device can be implemented without batteries whatsoever. No, I'm not making a "zero power WiFi device". I simply realised that the crosstrainer itself is battery powered and that my device could be so low-power that it can take the necessary power from the crosstrainer itself. However more about that later on.
One other target was to not break any existing functionality in the device. There was a small hiccup related to this, but eventually it was quite easy. The point is, after all, to just read some values.
Preparation
I started this project by partially disassembling the crosstrainer. I knew already that there's a connector between the "computer" and the rest of the machine (so that it can be disassembled for easier transport) but I didn't know what signals are there. By the way, the connector is a 12-pin JST XH which is used as a wire-to-wire connector. It's not really meant for that but there's heat shrink tubing and something similar to hot melt glue so I would say it's fine. Just unnecessary manual work for the device manufacturer. However at that point I took a multimeter and carefully measured the signals. I also had to use a small mirror to see inside the device to make sense to some of the details. The pinout is listed below after a picture. I made an inline adapter for the connector so that I can measure some values in situ.
Wire to wire connector with an inline adapter |
- dc jack- (violet)
- dc jack switch (cyan)
- dc jack+ (pink)
- reed- (brown)
- reed+ (white)
- ?? (white)
- ?? (yellow)
- pot+ (green)
- pot wiper (blue)
- pot- (yellow)
- motor (black)
- motor (red)
I'll briefly explain the list. The motor is a DC motor that adjusts the (spring loaded) load. There's no polarity since it has to turn into both directions. The potentiometer is there to indicate the value of the load. The pot+ voltage is ~1V. The ?? is probably some kind of calibration trimmer in the frame. I actually have a picture of it (see bonus at the bottom), but I don't really have any proof of what it is. The reed is the magnetic switch for detecting rotation of the big wheel. I only guess that it's a reed switch, because I can't see how it could be a hall sensor. But at least there doesn't seem to be any noise (bouncing) so I guess it's all the same. The voltage at the reed+ is ~4V for some reason. The last thing is the DC jack, which allows the crosstrainer to be used with a wall wart (instead of batteries).
This list is really good news. The load can be read without any adaptation and the reed switch can be read with minimal effort. Well there was some effort involved because it took some time to realize what I'm dealing with. I didn't really want to reverse engineer the "computer" PCB, because it's old and messy. I didn't even want to disassemble the "computer" because I was afraid that the LCD assembly would fall apart or the plastic screw holders would crumble to pieces or something. I guess mostly I didn't want to reverse engineer the PCB. :)
Implementation
The final implementation has three parts. The first one is the ULP, which records the exercise data using as little energy as possible. After that there's the main ESP32 code, which simply generates an HTTP request and sends it (completely discarding the response, which is btw a redirect, more about that later on). The URL in question is a Google Scripts URL which has access to my Google Drive and directly updates a single Google Sheet which was created for this exact purpose.
ULP
I started with the ULP implementation, because I thought it would be the most difficult one. There are good examples in the ESP-IDF github. One example shows how to use GPIO and another shows how to use the ADC. I needed both features so I kind of combined parts of these. However I removed the debounce code from the GPIO example and added another layer for the "activity" detection. I actually had to think this part quite thoroughly in advance because this is my first program using assembly and I didn't really know how to "just start prototyping" like I do in C. The final (rough) idea of the ULP program is shown below.
- wait for a falling edge
- measure activity
- count falling edges
- measure the load
- count the time
- wait for a timeout
- wake up the cpu
I actually had to make a diagram of this program, so I'll also show it here. I usually don't make these, but in this case it really helped.
Diagram of the ULP program |
The last part was simply writing the assembly program. The writing was actually pretty uneventful. I made some small mistake with the edge detection and then I made few other small mistakes. However to debug the issues, it was enough to simply stare at the code and compare it with the diagram shown above. Also I don't really know (yet) how else it could be debugged. There aren't really any interesting parts in the code so I will not share it here.
ESP32
The non-ULP part of the ESP32 programming was also very simple. I took the https-request example as the basis for this project and added the ULP part there. The main part is to read the ULP variables, make some adjustments and generate the URL based on the measured values. Some adjustments are necessary for the ADC measurements. The ULP doesn't have multiplication or division and the variables are 16-bit long, so it was easier to implement averaging and such in the main code.
Google Scripts
The Google Scripts part is also done based on an example. The basic idea is to open a Google Sheet, check the last used line number, increment it and add the received data. There are only a few special things. Some data is adjusted, for example the duration is recorded in seconds but displayed in minutes and the conversion is done in the script so that the ESP32 doesn't need to deal with floats. Another thing is that the script will record some metadata that is only used for debugging. This includes the timestamp, added line number, RSSI and the MAC of the last device that made the update. I'm only going to make one device, but I might have another for debugging purposes.
Google Sheets
There's not much to do in this part. The purpose of the script is to make a nice log of the exercises. This data can then be used for making nice graphs. I've decided to plot the energy usage, so that needs to be calculated first. I've concluded that it's simply number of revolutions * load * some constant. The constant was chosen to match the energy values calculated by the "computer" of the crosstrainer (which is a value that my device cannot read). After that the whole columns can be selected as the data for the graph, which means that the graph will be automagically updated every time a new line is added.
The last thing is to make meaningful types of graphs. I've decided to add a bar graph that would represent every exercise, so there is no grouping and the bars have variable gaps between them (depending on when the device was used). I've also added another graph that represents a monthly sum. These two graphs require two new columns (for the x axis formatting), one that only has the date (without time of day) and another that only has a month and a year. I don't know how these graphs will look with a lot of data, but I guess I will find out after some time.
Bonus
At some point I realised that the device could also measure the battery voltage. Obviously the voltage should be reported to the Google Scripts since the device itself doesn't have any display. I then found out that the Google Scripts could also send emails. As a result this might be the first crosstrainer in the world to send notification emails about dying batteries. And die they will, since apparently the crosstrainer itself has ~1.7mA current consumption when idle and it uses C batteries (capacity ~8Ah). Which means that the battery life of the crosstrainer itself, in stand-by mode, is <200 days. However the minimum voltage needs to be calibrated using real batteries, since the internal resistance of the batteries might have more effect than the cell voltage (so cannot really be simulated with a PSU, also I'm not carrying a PSU to the crosstrainer or vice versa).
The voltage could obviously be reported whenever the crosstrainer is used, but then again, it could also monitor the voltage constantly and report the low voltage even if the device is not used. The success rate of that obviously depends on whether the batteries can still support the WiFi functionality at the point where the crosstrainer decides they are empty (it refuses to work at all). For this feature to work, the ESP32 and the Google Script code should detect that we're only reporting the battery voltage and only send a notification email without updating the Google Sheet. The question of which will fail first (the crosstrainer or the ESP32) is still open.
I also realised that the ESP32 has a built in temperature sensor and I thought that it could also be logging the temperature. That's quite a stupid idea, but for some reason the crosstrainer itself also has a temperature display. However I thought that I could add a temperature threshold to use it as a remote fire alarm. It's a nasty topic, but it would be a small feature to ease my mind when not at home. Even if I couldn't necessarily do anything about the fire. However I couldn't make the simplest temperature measurement examples work, so I had to drop this idea at least for now. The ULP code would simply get stuck at the TSENS command on the second run of the ULP code and no one else in this world seems to have had the same problem.
Another dropped feature (at least for now) is the OTA support. The device should fit nicely inside the frame of the crosstrainer, so updating the firmware is not very convenient. For this reason I wanted to add OTA support using the Google Drive, but for some reason it doesn't seem to work. I spent quite a lot of time with it without any success and had to give up so that I can get this project done in any sensible time. After all, the OTA is not really necessary if the device is done "first time right" (which is bullshit, but I had to learn about it so now you need to hear about it too).
PCB
The PCB design is almost as simple as it gets considering the things explained until now. There should be a 12-pin "in-line" connector, an ESP32 and a voltage regulator. I've chosen to use the "stacking" pin header as the single connector on the PCB so that the male and female JST connectors can be both connected to it. The only downside is the lack of polarity, but I guess I will just have to mark it clearly and remember to connect correctly. This is not a very nice solution, but I believe that the female JST PCB connector doesn't exist. As mentioned earlier, I've chosen the original ESP32 (not S2 or C3). And I've chosen the MIC5239 as the regulator because the quiescent current consumption is low enough and because I have it in stock.
The rest of the PCB design is very simple. There should be a (compact) programming connector, there should be an indicator LED, some buttons, test points and proper markings. One more thing is a specific diode circuit for reading the reed switch. The reed output is 4V, which is not nice for the ESP32. The pull-up resistor for the reed switch is 100k, so I couldn't use any zener diode to ground to clamp the voltage to 3V3 (from 4V) because the "computer" could no longer read steps. Instead I wanted to use a zener diode in series, to adjust the voltage to a voltage of ~ 1V .. 3V but I couldn't find one in a small enough package. Instead I'm using two normal diodes in series and in both directions. That way the voltage drop should be approximately 1V in both directions, so the resulting voltage should be also ~ 1V .. 3V. At this point I can say that it didn't really work as expected. But the ESP32 is still functional so I'll leave it at that.
PCB design in EasyEDA |
The resulting PCB design is shown below. The PCB is double sided and should be as compact as possible while including the ESP32, the connector and two buttons. The chosen connector was a great idea, but unfortunately it made the device too large to fit into the slot that it was supposed to. I had to desolder the connector, bend a new one to 90 degrees and solder that instead. After that the device could fit into the slot with the wires connected to it.
Assembled PCB |
Another thing is to add an external temperature sensor in case I ever decide to implement the temperature measurement feature. An external sensor would also have much better accuracy than the integrated one. Eventually I didn't do this, perhaps some other time.
Case
As explained in some previous post, I don't really have the means for making enclosures, but that's okay in this case. I've realised that the device could be simply inserted into the frame of the crosstrainer where the wire and the inline connector should normally be. In this case there should be some enclosure, but a simple piece of tape should be plenty enough. The case also doesn't need to look good, because no one will have to see it. I would say this solution is "good enough".
Installation
Installation of the device is very simple. First the "computer" needs to be detached. Then the wire-to-wire connector has to be disconnected. Then the ESP32 board should be inserted between the connectors (with the correct polarity of course, unfortunately using simple headers allows connecting the device the wrong way around). Next the ESP32 board should simply be tucked inside the frame and the "computer" should be installed on top of that. Below is a single picture to visualise the installation process. Yes, I specifically wanted to write this long piece of text for something that can be explained with one picture.
Installation of the device |
Note that the cable connectors are now at an angle of 90 degrees compared to each other. Also note the markings (arrows) that I figured I could add to both the connector and the device.
Final thoughts
I had a lot of fun and luck with this project. The ESP32 was perfect for this project, the Google Scripts is easy to use and there are plenty of examples for both on the internet. Also the crosstrainer had very nice signals for this purpose. All in all, it was quite an easy project to make and it didn't take much time (apart from all the finishing touches). Which is good compared to the previous project which took more than a year to finalise. Here is the link to the EasyEDA project. I will try to add the codes to gitlab at some point, but it might still take time.
Unfortunately testing this project took a lot of time, because I got sick right after making a functional prototype and then there was an impossible heat wave and there's no proper cooling in the apartment that I live in. However the testing was done and the data was calibrated (apart from the battery voltage when the device refuses to work any further, that would take much more time to test).
It wasn't all that successful though. The first issue was already mentioned connector that I had to replace. The second issue are the side buttons that might get pressed regardless of the used tape, because the black cable is so rigid. The next issue is that even though the device has an activity LED (for easier debugging and development) it cannot really be seen when it's inside a metal frame of the crosstrainer. Related to this issue, the ESP32 might get stuck and drain the batteries without anyone noticing anything. This actually happened once and I still don't know why. Below are some things that still need to be added assuming I have energy to do so:
- WiFi connection timeout
- Upload timeout
- Battery low detection before the upload