import BLE_GATT from gi.repository import GLib import pdb import struct import sys import time from datetime import datetime, timedelta import os import pygame import pygame.freetype from pygame.locals import * class CadenceListener: prevCtime = 0 prevWtime = 0 prevWrevs = 0 prevCrevs = 0 wheelSpeed = 0 crankSpeed = 0 crankSpeedMax = 120 elapsedTime = None firstMessage = True def getWheelSpeed(self): return self.wheelSpeed def process(self, value): flags = value[0] offset = 1 if flags & 1: wheelRevolutions,wheelTime = struct.unpack(' self.prevCtime): self.crankSpeed = 60 * 1024 * ((crankRevolutions - self.prevCrevs) / float(crankTime - self.prevCtime)) print("crank", self.crankSpeed) self.prevCtime = crankTime self.prevCrevs = crankRevolutions if(wheelTime > self.prevWtime): millis = float(wheelTime - self.prevWtime) self.rolling = True revs = (wheelRevolutions - self.prevWrevs) / millis self.wheelSpeed = revs * 2205 * 3600 / 1024.0 print("wheel", millis, self.wheelSpeed) self.prevWtime = wheelTime self.prevWrevs = wheelRevolutions self.elapsedTime = (self.elapsedTime or 0) + (millis / 1024.0) class Wobble: listener = CadenceListener() def __init__(self): pygame.init() self.surface = pygame.display.set_mode((600,300), pygame.RESIZABLE) self.width = self.surface.get_width(); self.height = self.surface.get_height(); self.font = None def on_update(self): scale = int(min(self.width/7, (self.height-20)/3)) if(self.font == None): self.font = pygame.freetype.Font(os.environ['FONT'], scale) self.surface.fill((10,20,10)) self.font.render_to(self.surface, (scale/16,10), "{: 5.1f} km/h".format(self.listener.wheelSpeed), (0,255,0)) self.font.render_to(self.surface, (scale/16,scale + 20), "{: 5.1f} rpm".format(self.listener.crankSpeed), (0,255,0)) if self.listener.elapsedTime != None: runTime = timedelta(seconds=self.listener.elapsedTime) timelabel = str(runTime)[0:9] else: timelabel = datetime.now().strftime("%H:%M:%S") self.font.render_to(self.surface, (scale, scale * 2 + 30), timelabel, (0,255,0)) pix = self.width *((self.listener.crankSpeed-40)/ self.listener.crankSpeedMax) pix = max(pix, 0) pygame.draw.rect(self.surface, (180,0,0), pygame.Rect(0,self.height/2-60, pix, 50)) pygame.draw.rect(self.surface, (0,0,180), pygame.Rect(pix,self.height/2-60, 20, 50)) pygame.display.flip() bike = BLE_GATT.Central(sys.argv[1]) bike.connect() app = Wobble() csc_measurement = '00002a5b-0000-1000-8000-00805F9B34FB' bike.on_value_change(csc_measurement, app.listener.process) running = True def handle_pygame_event(event): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYUP and event.key == pygame.K_q: running = False if event.type == pygame.KEYUP and event.key == pygame.K_SPACE: if app.listener.startTime == None: app.listener.startTime = datetime.now() else: app.listener.startTime = None if event.type == pygame.VIDEORESIZE: app.width = event.w app.height = event.h app.font = None def on_timer(): handle_pygame_event(pygame.event.poll()) app.on_update() return True GLib.timeout_add(100, on_timer) try: # Time to go live print("Listening for events...") bike.mainloop.run() finally: pygame.quit()