Instructions below and current code do not result in working system. No timetable for a fix.
This repo is my attempt to set up a headless PI Zero W with no additional dongles that can continuously scan the proprietary M3i broadcasts and implement the BLE services for:
- Cycling Power (including cadence info)
- Cycling Speed and Cadence (only cadence info)
- Heart Rate
I blatantly appropriated some of the work other folks are doing (see hypermoose/Keiser2Zwift and ptx2/gymnasticon) and was able to implement all of the BLE services using only the single on-board PIZeroW bluetooth adapter.
What is currently working:
- Cycling Power Service (including cadence)
- Works with Zwift, FulGaz, Kinomap, RTG Cycling
- Peloton on iPad does not look for power service, so does not find cadence.
- Cycling Speed and Cadence Service (cadence only)
- Once this service was implemented, cadence was found by Peloton running on the iPad
- Heart Rate Service
- Shown to work with Zwift, Kinomap, FulGaz, and Peloton iPad
- is not found by RTG Cycling
- For Peloton HR functions fine with HR Strap that talks to M3i. With no HR stap, Peloton seems to see random "ghost" heart rate values. As documented in closed issue #3, there are five lines to comment out to disable HR Service.
Note that for the Cycling Power Service and for the Cycling Speed and Cadence service, the cadence information (crank count and timestamp) is calculated from the instantaneous rpms reported by the Keiser M3i.
A side benefit of using only the on-board bluetooth adaptor is that the PiZeroW without the dongle runs fine on the power from the USB port on the back of my HDTV. So I just have the PIZeroW velcroed to the back of the TV and powered by a short USB power cable which gives me nice clean "invisible" install.
I also created a Fitness Machine Service that is currently disabled (via commments in code) because it appears that most Apps do not need it. Prior to disabling, it was verfied that for power, instantaneous cadence, and heart rate:
- all three successfully demonstrated to work with Kinomap on ipad
- power and cadence work with Zwift on iPad; no HR
These installation steps are based on starting with a Raspberry PI Zero W, a blank miniSD card, and a laptop running Windows 10.
- Install the latest 32-bit Raspbian PI OS Lite image on the miniSD card. I used the Raspberry Pi Imager v1.6.2 running on a Windows 10 laptop. Once the image is written, the Imager automatically ejects the miniSD card. I physically removed the miniSD card, reinserted it into the laptop, then went to Step 2.
- Pre-setup for headless operation and to enable SSH connection:
- To configure for headless WiFi connection, create a text file named wpa_supplicant.conf and place it in the root directory of the miniSD card. Add the following text to the file:
country=US ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ scan_ssid=1 ssid="your_wifi_ssid" psk="your_wifi_password" }
- To automatically enable SSH (Secure Socket Shell) connection to the Rapberry PI, create an empty file "ssh" (no file extenstion) in the root directory of the miniSD card.
- To configure for headless WiFi connection, create a text file named wpa_supplicant.conf and place it in the root directory of the miniSD card. Add the following text to the file:
- Eject the miniSD card, insert it into the Raspberry Pi Zero W, and power the Pi Zero up. If all goes well, the Pi Zero will show up on your network. You may need to use your router tools to determine the IP address assigned to the Pi Zero W.
- Once the Pi Zero W is up and running on your WiFi network, connect via SSH using the IP address noted in the previous step and log in using the pi account (default password "raspberry"). FYI, for SSH connection from my laptop, I used Windows Power Shell and the ssh command. At that point I executed the following two commands (note that the upgrade command may take awhile to complete):
pi@raspberrypi:~ $ sudo apt-get update pi@raspberrypi:~ $ sudo apt-get upgrade
- Disable the default bluetooth service and move the file to a backup location to make sure it does not come back on reboot:
sudo systemctl stop bluetooth sudo systemctl disable bluetooth sudo mv /usr/lib/bluetooth/bluetoothd bluetoothd.bak
- Install the bluetooth development requirements:
sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
- Install NVVM to manage Node.js versions:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
- At this point, I rebooted the Raspberry pi and started a fresh SSH session:
sudo shutdown -r now
- Install a binary build of Node.js that supports Pis (I just used version 10.22.0 as I know it works). After install I copied all the files to a directory that sudo looks for:
nvm install 10.22.0 sudo cp -r /home/pi/.nvm/versions/node/v10.22.0/* /usr/local/
- Install git, create directory /home/pi/code, make this the working directory and clone this repository:
sudo apt update sudo apt install git mkdir /home/pi/code cd /home/pi/code git clone https://github.com/djwasser/M3iPiZeroW.git
- Change to the directory created by the git clone command and install the app:
cd M3iPiZeroW npm install
- Enable the PI Zero's on-board bluetooth adaptor and verify that you can see it:
sudo hciconfig hci0 up hcitool dev
- Start the app. You should see messages to the console about powering on and starting scan:
sudo npm start
- Start pedaling the Keiser bike. After about 5-10 seconds you should see messages about attaching to the Keiser M3i and data from the M3i should be displayed. Start Zwift and verify that you can see and connect to the power and cadence services.
At this point, if everything is working, you can set things up to run without sudo and also install the app as a service that runs whenever the Rapsberry Pi starts up. Enter Control-C to stop the running app and execute the following commands:
sudo apt-get install libcap2-bin
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)
This enables running without sudo. To set up as a service to always run, execute the following commands:
sudo cp M3iPiZeroW.service /etc/systemd/system
sudo systemctl enable M3iPiZeroW
sudo systemctl start M3iPiZeroW
Verify the the service started:
systemctl status M3iPiZeroW
Now reboot the Raspberry Pi, connect to the console using SSH and confirm the service is running:
systemctl status M3iPiZeroW
Now pedal the Keiser and confirm you can connect to power and cadence from Zwift