Wednesday, 24 February 2021

Building an Cadence Display for a BLE Cycling Sensor with ESP32 / MicroPython

With lockdowns and winter I converted my bike into a "turbo trainer" to cycle indoors. A cheap eBay cadence sensor can then report on my RPM, however when doing guided exercises on my Phone, I cant use the app to see my cadence. This makes it difficult when the coaches say "now hit upper 90s on your RPM".

I decided to try and build a cheap display to show the cadence using an ESP32 and a SSD1306 OLED screen: total cost ~£10

 It turned out to be one of the harder projects I've completed. You know those projects where someone solves something with bold code, smart electronics and a nifty 3D print? This isn't that, this is someone struggling through a bunch of areas and learning as they went. The code is probably wrong.

Some of the challenges covered in this blog:

  • Micropython on ESP32
  • Bluetooth Low Energy (BLE)
  • BLE in micropython
  • Understanding the sensor
  • Getting it all together

Adventures in Micropython

I knew this project would be a lot of experimentation and as I'm more comfortable with Python, figured it was best to use that. Also, you can have a live "shell" on the ESP32 with Python which would allow experimentation without uploading new code.

There are a number of blogs of coding python on the ESP32. I decided on the Adafruit method, as it seemed to require the least installed on my windows computer, and I can use VS Code with normal linting to write the code.

A good blog was: https://www.digikey.co.uk/en/maker/projects/micropython-basics-load-files-run-code/fb1fcedaf11e4547943abfdd8ad825ce

Then to develop I would just use VS Code, and when deploying: run the ampy module in the directory where I was developing to move main.py onto the esp32.

The guides will recommend that you add in switches etc to switch into REPL (python shell) mode, but instead I just used Putty as my Serial interface, which lets you send Ctrl-C to kill the python script and drop you into the shell.

A quick warning was this GitHub issue, you may need to use an older micropython firmware if you want BT LE to work.

Another note here: If you installed python using the Windows App Store then anything you pull with Pip is in a horrible chain of subdirectories under your user's AppData\Local\Packages\PythonSoftware.. directory.

Bluetooth Low Energy

There are a number of blogs on Bluetooth Low Energy in micropython but none of them did the full job helping me understand so I'll try here. Some of the theory in this Blog is a good start https://learn.adafruit.com/bluetooth-bicycle-speed-cadence-sensor-display-with-clue/understanding-ble

A real struggle with this project is that I couldn't find many examples or docs on how to do this in Python, so will attempt to detail here.

But here are the key bits:

Devices will advertise themselves

In the advertisement:

  • all devices will have a MAC address of the form 11:22:33:44:55:66
  • they may also have extended information like a common name

Once you connect to a device, you can discover it's services. These will have GUIDs and potentially that GUID maps to a name such as "Generic Access".

A Service can be probed for it's "Characteristics" which will be a "handle" (small integer), details on whether the handle can be read/written etc and maybe a name.

As an example:

A device advertises its address: 11:22:33:44:55:66 and name: "FitBob".

You connect to it and its services are:

  • 19b3530b-c999-4184-92b5-2c3b60a7e926
  • 44bd726c-3d6d-4073-993f-c487437917ab

You query the characteristics for the first GUID and see that it has two characteristics

  • Handle 4 - read only - name: "Steves FitBob"
  • Handle 6 - read only - Version: 4

There are many recommendations in blogs of apps you can use to explore BLE, however they all suffer from a similar issue: its hard to get technical details out like the MAC address, and specific handles. I found that the "Bluetooth LE Explorer" program in the Windows Store on Win10 from Microsoft was by far the best for exploring devices and understanding them. Highly Recommended.


Connecting to BLE in Micropython

So, you understand your device a bit, lets try connecting to it

In Micropython starting a connection means 

bt = ubluetooth.BLE() #create an object
bt.active(True) #activate BT, this wierdly takes upto 30 seconds each time

bt.gap_scan(3000) # scan for advertising devices for 3000ms, you can give more specific numbers if needed like how long the scan window is, see documentation at https://docs.micropython.org/en/latest/library/ubluetooth.html
Now this was where I initially struggled quite a lot. The ubluetooth module doesn't return data like a function in python, instead it raises an interrupt that you have to handle. So you fire off something (like a scan) and each result is raised as an interrupt.
You therefore need a function to handle it like:
def bt_irq(event, data):

    print(str(event))
Which you pass in to the bt instance (before you do anything like a scan) using:
bt.irq(bt_irq)
Everything has its own irq number: a scan result is 5, a scan complete is 6 for example. A good list is and how to handle each event code can be found near the top of https://docs.micropython.org/en/latest/library/ubluetooth.html although I also found useful this GitHub Issue

Once you have irqs handled (I slowly built them up as I needed them), you can start connecting
adr = binascii.unhexlify(b'112233445566') #puts the address in a format that the next function can handle
bt.gap_connect(1,adr,10000) # connects
bt.gattc_discover_services(0) # the 0 is for the connection handle, starts at 0 if you have only connected to 1 device, it will return the services and their handles
bt.gattc_discover_characteristics(0, 14, 24) # give the handle ranges from the last call to discovered characteristics.

Understanding your target device

Exploring the device in "BT Low Energy Explorer" I found its MAC address and the cycle profile. This is covered a bit by https://learn.adafruit.com/bluetooth-bicycle-speed-cadence-sensor-display-with-clue/cycling-speed-and-cadence-service and the full documentation is available at https://www.bluetooth.com/specifications/specs/cycling-speed-and-cadence-profile-1-0/

Looking at the Cycling Cadence service, you can see it has a "Measurement" characteristic. But how to read it? 


I assumed I could poll it, but this service is a notification service. I.e. you write to it telling it you want it to notify you, it starts to notify you with each notification raising an irq event.
Clicking on the Measurement service in BT LE Explorer gives you the handle to write to in order to trigger notifications, 27 in this case.


So the python to activate notifications in python is:

bt.gattc_write(0,27, struct.pack('<h', 1), 1) # from https://github.com/micropython/micropython/issues/6185#issuecomment-650848053

From then, sweet sweet measurements start coming in.

I used excerpts from some of the code from a related AdaFruit project to translate the data 
However, the data only gives you:

  • Count of how many revolutions of the pedal
  • Time in 1/1024 of a second from last crank to this 

Which you then need to convert to RPM. I am particularly bad at Python and maths so this wasn't easy, but the below gives me reasonable output. I'm using globals because remember it gets called by the irq event and so you can't pass in variables.

global last_crank_count
global last_crank_time
global rpm
if crank_time >= last_crank_time:
    if crank_count-last_crank_count == 0:
        rpm = 0
    rpm = (60/((crank_time - last_crank_time)/1024))*(crank_rev_count-last_crank_count)
last_crank_count = crank_count
last_crank_time = crank_time

Putting it all together

I built a quick breadboard with an ESP32 on and my I2C SSD1306 OLED screen. I discovered way too late into this project that there is not good micropython SSD1306 support so the text is tiny as you cant customise text size. 
I uploaded the code and turned it on. It entirely failed to find the BLE cycle cadence sensor.

A few days of debugging followed:
  • taking battery out of sensor and replacing worked about 10% of the time
  • I could see it on my phone
  • I could often see it on Windows
  • I could rarely see it on the ESP32
Eventually a kind soul told me that once a BLE device had been connected to, it often stops advertising. And so my phone was the culprit, seizing it whenever it saw it appear, before the ESP32 could. Once I unpaired it from phone, it worked perfectly. It was time to build it out on a project board and use scrap wood to give it a home as need practice at woodworking:

Future Plans


Now I know what I'm doing (a bit) I will rewrite in C/Arduino. This is for a couple of reasons

  • Better OLED support so dont have to squint
  • The 30s to boot Bluetooth is a pain
  • Practice C
Hopefully some of this was useful for you in avoiding some of the traps. It was a good project and pushed me, it's rare I have a project where there isn't a basically finished project on GitHub to be inspired by/copy paste from. When most of the tips are coming from GitHub issues or reading source, rather than StackOverflow is a sign you're not on easy mode anymore too. Crucially, this worked and is already thoroughly useful: 

Total cost about £10 and a few evenings of entertainment / frustration.


Tuesday, 18 June 2013

The Google-Seeking Weapon In Windows 8.1

The browser wars were the dirty and hotly fought skirmishes between companies aiming to be the portal through which people accessed the wilderness of the internet. Millions have been spent, billions if you take Antitrust fines into account, to be the gateway through which people shop, socialize and conduct every aspect of their digital lives.

Google are obviously now major players in the browser wars but also have the benefit of a firm front in search. To many people Google is "the Internet" and ad revenue is still their bread and butter. A great article I can no longer find raised Siri as a potential new front in this war. Suddenly it was easier to ask Siri than to open a browser and type into the search bar. The article warned that this may, with the iPhone's popularity, start to impact Google.

With Windows 8.1 comes a new, flashy, search ( see link ). Press the Windows key and start typing and a flashy, metro-ified app shows files, applications, settings and crucially internet search results. The screenshots available (see below, from above link) look great. Searching, but for the tablet/app generation:

But imagine if Microsoft can get it right. You're using Excel and have trouble with a formula. In the old days you open Firefox and Google the answer. Now you just press the start button and type. If they can manage a good app experience and speed, it could lure people away from going to Google.

So what does this mean? If Windows 8.1 can become popular enough, and do search right, they can lure people away from Google and towards Bing. They can start to affect both product's ad revenue. With more users and more click-through data, combined with fancy user metrics from Windows they can even improve Bing's accuracy, currently a sticking point for many users. They can take a lot of the internet experience out of the browser entirely, and take the fight into Google's stronghold.

I don't expect this to go smoothly. I'm certain there will be a flurry of posts, some independent, some less so, deriding the new feature. We may even hear at some point that the search is doing something anti-privacy like sending excess data to Microsoft. At the very worst we may even see a renewed Antitrust suit: if packaging a browser with the OS got Microsoft into all that trouble previously, will packing search in with the OS do the same?

@publidave

Friday, 2 November 2012

Windows 8: Hyper-V Issues Solved

A quick post to detail my struggles with Hyper-V installation


I recently upgraded to Windows 8 Pro and wanted to take use of Hyper-V, Microsofts hypervisor / virtual machine host.

Enabling Hyper-V in Windows was tricky however, here are the steps I went through:

- Search start screen for "windows features" in the "settings" search and click "turn windows features on or off"
- Turn on "Hyper-V"
- Machine will need to reboot
- For me, Hyper-V wasnt working when it came back up, reboot again. Guides say you may need to pull out power cable when it's off and remove battery.
- Hyper V worked but trying to start a VM or install a network switch gave "cannot connect to localhost" issues
- I went to network adapters, my ethernet adaptor and then installed the "hyper-v" protocol. This disabled my ethernet access
- In frustration I removed Hyper-V
- I calmed down and re-enabled Hyper-V
- This time everything worked fine

Summary:

"localhost" issues with Hyper-V are networking related, your networking may not have installed cleanly
Re-installing can help

Friday, 3 August 2012

Severe Security Issues with MSChapV2

*Introduction*

Those of you who went to or follow the Defcon conference in vegas may have seen Moxie Marlinspike's work on MSChapV2. This is the auth scheme i recommended for the raspberry pi pptp vpn. Ie, when the client connects to the VPN, it is the scheme by which the client proves it knows the password without sending the password in cleartext.

A problem i was aware of with MSChapV2 was that the password is bruteforceable, ie if an attacker can capture you connecting to your VPN (ie in a wifi cafe) then they can attempt to crack your password. If the password is weak and they are successful, they can decrypt all of your traffic and also connect to the the network themselves.

However, Moxie analysed MSChapV2 and found a significant design flaw, that is worth reading about: https://www.cloudcracker.com/blog/2012/07/29/cracking-ms-chap-v2/
in short, he found that the whole protocol can be reduced down to a single (56bit) DES encryption, and if you can crack that you can crack MSChapV2. He then worked with David Hulton, an FPGA wiz, to crack DES in under 24 hours. He has made this service available on his website https://www.cloudcracker.com.

*Summary*

So, in summary, if you use a PPTP VPN with MSChapV2 (as i described), someone could intercept that traffic and decrypt all of it 100% reliably and potentially provide access to your network. This is obviously very bad.

*Risk*

Now the risk talk. If you're an average individual, you're unlikely to be on a network with an attacker capable and motivated enough to do it. The risk is very real, but i would say fairly low for now. The VPN will certainly make you safer against a certain level of attacker (ie someone just sniffing open wifi points). This is clearly not a perfect strategy so i will be working on a second guide to use the RasPi as a IPSEC VPN endpoint as this is also supported by the iDevices and is currently believed to be stronger.

Thanks to Moxie for making this public and providing such an entertaining talk!

Thursday, 21 June 2012

RaspberryPi as a PPTP VPN Server - HOWTO

*Introduction*


IMPORTANT UPDATE: Severe security issues found in MSChapV2, ie the scheme described below, see http://jmparound.blogspot.com/2012/08/severe-security-issues-with-mschapv2.html for discussion of how it relates to the RasPi VPN.

Having received a Raspberry Pi, the best usage I saw for it was as a £35, 5W VPN endpoint. Maybe at some point in the future I'll add other features (SMB, Radius Auth, Log files from router) but for now, that would rock.

I looked at VPN schemes. OpenVPN crops up a few times on blogs, but with a significant problem that you generally need a client for it that isnt always available. My goal is to have a VPN that works "out of the box" with windows 7 (laptop) and iPad. The best option seems to be a PPTP VPN. There are a couple of guides to getting this working but none which were idiot-proof to my level of idiocy so I am currently trying to work it out.

First things first, I copied the RasPi Debian image onto an SD card with unetbootin. This didnt work. The Raspberry Pi just sits there with a power led and no activity leds.The diskimagerpro or whatever is recommended on the RasPi site is needed.

I copied over the debian image and changed the name of boot_enable_ssh.rc to boot.rc to enable ssh. I figured at some point i would need to run iptables so tried to fire it up but no luck. It seems the RasPi debian kernel is configured without the modules needed for iptables.

One note before you start, if you havent discovered the linux "screen" command, do it before you start, theres a bunch of stages that either take ages or make your connection drop so running commands within screen will save your bacon as when the connection drops itll just detach and your commands keep running. You can then reattach when the connection comes back up.

*Getting SSH, HDMI and pacman working on Arch on the Pi*

I decided to compile my own kernel (first time!) and so followed the guide at http://elinux.org/RPi_Kernel_Compilation exactly. This meant installing Arch. Arch linux on the Raspberry Pi didnt start an SSH server and wouldnt recognise my HDMI connection. Some people in the #raspberrypi room at freenet pointed me at some docs that showed editing the config.txt file on the SD card to add "hdmi_safe" instead of "hdmi_mode=19" worked. The SSH host key had apparently dissapeared so recreated it with ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key After this i could administer it by SSH. I needed to install things like "screen" so tried "pacman -S screen" but got 404 errors. Some googling suggested i needed to synchronize the repositories so "pacman -Sy pacman" and then pressing "n" when it asked seemed to magically make it work. After that I followed the kernel compilation guide but discovered it wasnt fully n00b friendly. 


*Kernel Compilation for PPTP*
I git cloned the raspberry pi kernel but did need to use "--depth 1" to not get a memory error. I cloned it to /root/raspberrypi. i used zcat to create the .config file at /root/raspberrypi/linux/.config and then in the ../linux directory ran "menuconfig"
I followed the notes at http://en.gentoo-wiki.com/wiki/PPTP to give me an idea as to the kernel options to select. Following that i ran "make" (in a screen session!!) and left it overnight
The kernel image needed to be prepared for the pi and the page at elinux said to use the tools download. GIT cloning them always resulted in out of memory errors so i downloaded the files manually from a windows box, copied them over and ran dos2unix on them to make sure they were ok. Note here, the tutorial suggests you just need the python file, you need all of them. Once i had prepared the image, i copied it as per the tutorial and rebooted. Win.
<edit: i have since found out that iptables is already enabled in the Arch image, still, im not sure the other bits needed for PPTP is so it may be worth following this>

*Installing PPTP Server in Arch*
I followed the page at https://wiki.archlinux.org/index.php/PPTP_Server which was generally pretty good. Installed pptpd using pacman -S pptpd and then followed the guide. The only changes i made was to change the DNS servers from googles (8.8.8.8) to my main router.I also didn't need to do the ufw-config bit as had configured iptables already. The only bit that required some understanding was the remoteip bit. It seems that localip is the ip address of the RasPi and remoteip is the addresses you want VPN clients to assume.

*Client Config*
So, i supposedly had the Pi configured as a VPN server. I set up dynamic DNS so i could track my IP as it changed and then port forwarded tcp 1723 to the Pi. Thankfully my router worked out forwarding GRE itself as there were no obvious options for it. For Win7, open "Network and Sharing" centre and click "Set up a new Connection or Network". Select "Connect to a Workplace" and then choose "no, create a new connection". Click "Use my internet connection" and for the internet address use your home IP or the dynamic DNS name you have set up. Select, "dont connect now, just create", you can then skip the next stage without putting in credentials.

It will have created the adapter so go back to Network and sharing centre and click "manage network adapters". Right click on the newly created VPN adapted and choose "properties". On the security tab, change "type of VPN" to "PPTP" and underneath uncheck the boxes so that only MS-CHAP v2 is allowed. Click ok and try to connect. It should work once you have entered the username and password that correspond to what you put in the "ms-chap-secrets" file.

Now i got a wierd problem here. I could connect to www.google.com but not any complex site. Ping worked but anything more complex didnt seem to. Some very smart people helped me debug it and it found that there had been something screwy in the MTU (maximum transmissable unit) set by windows. Ie the RasPi was forwarding the packets but they were never making it to my windows box. After (much) trial and stress, I eventually found this solution http://support.microsoft.com/kb/826159 which (following a reboot) seems to work like a charm. If this doesnt work for you then one thing I did do that might have affected it was change the MTU on the RasPi's ethernet connection to 1395. I did this through ifconfig eth0 mtu 1395 and then ifconfig eth0 down && ifconfig eth0 up. Not certain this is necessary though.

*iPad config*
Ipad wasnt too bad, goto the network settings page, in there find the vpn part and configure a new connection. Put in all the basic details and away you go! Worked first time for me.