joestella28
Posts: 3
Joined: Mon Jan 05, 2015 9:30 pm

Stationary Cycle Display

Mon Aug 31, 2015 7:25 pm

Hi All,

A bit of background, I am not a programmer, not by a long-shot but have always been intrigued and have had a Pi for some time now though I've done very little with it!

I've long thought it would be good to make a dashboard to display live data from bicycle whilst on the turbo trainer (for those unsure, something like this:
Image

Initially I would just start with the obvious of displaying speed and cadence but would really like to calculate power...

My turbo trainer has 7 strength settings for which I have the power data (for a given wheel speed) so doesn't seem beyond the realms of possibility though I know it won't be 100% accurate.

Later on I'd like to get into data logging though I'm not fussed about that now.

So, for a complete novice this is all quite daunting though, don't laugh, I use VBA within Excel quite a lot and have had a play with the GPIO pins and python to calculate speed based on pressing a push button in terminal.

I've done quite a lot of searching on here but haven't found quite what I'm looking for in terms of examples as a lot cycle projects seem to use GPS and API which obviously is no good on a stationary bike...

What would be the best environment/language to create something like this? To start with I just want to create a screen that will display current speed and then build on that (later on cadence, power and then displayed using dials etc).

Any help would be much appreciated,

Many Thanks,

Joe

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Mon Aug 31, 2015 11:20 pm

Python will be okay!


Use the edge detection to calculate the time between each revolution. Maybe an running average of three revolutions will be good enough.

ex:

Code: Select all

GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback)



What kind of display? Do you want to use 4.5 digits display or a full LCD?

A small nokia LCD 5110 will do.

https://learn.adafruit.com/nokia-5110-3 ... y/overview

Python is simple and it is easy to experiment since it compiles on the fly. Not as fast than C but it will do what you want.

User avatar
morphy_richards
Posts: 1603
Joined: Mon Mar 05, 2012 3:26 pm
Location: Epping Forest
Contact: Website

Re: Stationary Cycle Display

Tue Sep 01, 2015 6:59 am

How do you intend to measure wheel speed and your strength setting? Speed should be straightforward, you could use an LED and receiver on either side of the wheel and count how many times per sec. the beam is broken by spokes (for example) but the position of a lever on your handlebar might be a little bit more challenging.
I think writing the code in Python or whatever will be fun, nothing over complicated but lots of smaller, challenges that when put together do something really good.

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Tue Sep 01, 2015 11:20 am

I did a class to read the free running clock in Python. It's on the second post. Then the lapse time will be the difference between each wheel revolution.

viewtopic.php?p=677313#p677313

You could use also a reed switch with a magnet attach to the wheel instead of a light beam. Maybe you could find a old byke speedometer and use the sensor. 95% of the time it is a reed switch.

Maybe it will be possible to add a potentiometer on the friction setting. Check if at the control or at the other end of the cable you could attach a potentiometer to follow the wire movement. Than add a A/D converter to read the position. Or reed switches on each level position with a magnet on the friction lever.

joestella28
Posts: 3
Joined: Mon Jan 05, 2015 9:30 pm

Re: Stationary Cycle Display

Tue Sep 01, 2015 8:12 pm

Thanks for all the replies, much appreciated.

Just to clear up a few things, I intend on using a reed switch but in my experiments to date, just used a switch as it was easier to use indoors (she who must be obeyed insists bikes are to be kept in the garage :(). Using the switch I got it to calculate speed based on my pressing the button as uniformly as my un-coordinated finger would allow. I managed to get it to calculate speed though it was simply returning it line at a time in terminal. Where I'm at a loss is how to get it to show as one, changing number, on a screen (preferably a monitor)

I don't want to get ahead of myself but the first step to calculate power would be crude, simply using the data in the pdf below (by using a line of best fit for each strength setting as the equation of a straight line).

http://www.minoura.jp/data/trainer/deta ... e-data.pdf

To start with I'd just look to get it working on one strength setting as I can easily do a work out using just one setting but using my gears more.

Thanks,

Joe

User avatar
morphy_richards
Posts: 1603
Joined: Mon Mar 05, 2012 3:26 pm
Location: Epping Forest
Contact: Website

Re: Stationary Cycle Display

Wed Sep 02, 2015 7:03 am

I think you can just use a comma,
IE.

Code: Select all

print speed,
Not tried it though.

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Tue Sep 08, 2015 2:17 am

Your post inspire me!

I never play with tkinter and I decide to give a try. I know you want a console but this is a GUI version with python

2015-09-07-220530_270x372_scrot.png
Python Tkinter cyclometer version
2015-09-07-220530_270x372_scrot.png (11.41 KiB) Viewed 3711 times
this is the code.

Code: Select all

try:
    # for Python2
    import Tkinter  as tk
except ImportError:
    # for Python3
    import tkinter as tk
import tkFont
import time
import RPi.GPIO as GPIO





#Wheel diameter in mm
WheelDiameter = 700
WheelCircumference = WheelDiameter * 3.141592654

#how many count in moving average
dynamicMovingAverageMaxCount=8

WheelSensorGPIO=  21


class App():


   def __init__(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
       self.sensorTimer=0
       self.root = tk.Tk()
       self.root.wm_title('Cyclo meter')
       self.deci=0
       self.odometerValue=0.0
       self.speedValue=0.0
       self.speedFrame(self.root)
       self.timerFrame(self.root)
       self.odometerFrame(self.root)
       self.avgSpeedFrame(self.root)
#       self.settingsFrame(self.root)
       self.resetFrame(self.root)
       self.startTime=time.time()
       self.update_clock()
       


   def mainloop(self):
       self.root.mainloop()


   def subFrame(self,mFrame,Title):
       sFrame = tk.Frame(mFrame)
       sFrame.pack(side= tk.TOP,fill=tk.X)
       tFrame = tk.Frame(sFrame)
       tFrame.pack(side=tk.TOP,fill=tk.X)
       lFrame = tk.Frame(tFrame)
       lFrame.pack(side=tk.LEFT,fill=tk.X)
       titleLabel = tk.Label(lFrame,text=Title)
       titleLabel.pack(side=tk.LEFT)
       aFrame = tk.Frame(sFrame)
       aFrame.pack(side=tk.TOP,fill=tk.X)
       self.separator(sFrame,tk.BOTTOM)
       return aFrame
       

   def speedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'speed')
       sFont = tkFont.Font(root=mFrame,family="Courier",size=72)
       self.speedvar = tk.StringVar()
       speedLabel = tk.Label(aFrame,anchor=tk.W,textvariable=self.speedvar,font=sFont)
       self.speedvar.set("{:2.1f}".format(self.speedValue))
       speedLabel.pack(side=tk.LEFT)
       kmhLabel = tk.Label(aFrame,text="km/h",justify=tk.LEFT)
       kmhLabel.pack(side= tk.LEFT)

   def addDistance(self, value):
       self.setOdometer(self.odometerValue + value)
   

   def setOdometer(self, value):
       self.odometerValue =value
       self.odometerVar.set("{:.1f}".format(value))
  
   def setTimer(self, value):
       self.TimerValue =value
       hour= int(value / 3600)
       min = int((value - (hour * 3600)) / 60)
       sec = (value - (hour * 3600) - (min * 60)) 
       self.TimerVar.set("{:02}:{:02}:{:04.1f}".format(hour,min,sec))

   def setAvgSpeed(self, value):
       self.avgSpeedValue =value
       self.avgSpeedVar.set("{:.1f}".format(value))
       


   def timerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Timer')
       self.TimerValue = 0
       self.TimerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=32)
       TmLabel = tk.Label(aFrame,textvariable=self.TimerVar,font=sFont)
       TmLabel.pack(side= tk.BOTTOM)
       self.setTimer(0)

   def avgSpeedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Average speed')
       self.avgSpeedValue = 0.0
       self.avgSpeedVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km/h").pack(side=tk.RIGHT)
       avgLabel = tk.Label(aFrame,textvariable=self.avgSpeedVar,font=sFont)
       avgLabel.pack(side= tk.RIGHT)
       self.setAvgSpeed(0)


   def odometerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Odometer')
       self.odometerValue = 0.0
       self.odometerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km   ").pack(side=tk.RIGHT)
       odometerLabel = tk.Label(aFrame,textvariable=self.odometerVar,font=sFont)
       odometerLabel.pack(side= tk.RIGHT)
       self.setOdometer(0)

   def settingsFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Resistance settings')
       bFrame = tk.Frame(aFrame)
       bFrame.pack(side=tk.TOP)
       self.settings = tk.IntVar()
       for i in range(8):
         tk.Radiobutton(bFrame,text = "{}".format(i),variable=self.settings,value=i).grid(row = i/4,column= i % 4)

   def ResetAll(self):
       self.odometerValue=0;
       self.startTime=time.time()
       self.setTimer(0)
       self.setAvgSpeed(0)

   def setZeroSpeed(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
   

   def resetFrame(self,mFrame):
       tFrame = tk.Frame(mFrame)
       tFrame.pack(side = tk.BOTTOM,fill=tk.X)
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       b = tk.Button(tFrame, text ="Reset", command= self.ResetAll,font=sFont)
       b.pack()
       
      
   def separator(self,master,s_side=tk.BOTTOM):
       Fr = tk.Frame(master,height=2,bg="black")
       Fr.pack(side=s_side,fill=tk.X)

   def update_clock(self):
       self.deci = self.deci + 1
       self.ctime = time.time()
       self.setTimer(self.ctime - self.startTime)       

       if self.deci > 9:
        self.deci=0
        if self.dynamicMovingAverageCount > 2:
          self.setAvgSpeed(self.odometerValue / (self.TimerValue / 3600.0))

       if self.sensorTimer >0:
         self.sensorTimer =self.sensorTimer - 1
         if self.speedValue > 99.9 :
          self.speedValue=0.0
       else:
#         self.setZeroSpeed()
         self.speedValue=0.0

       self.speedvar.set("{:4.1f}".format(self.speedValue))
       self.root.after(100,self.update_clock)
      

   def gotPulse(self):
       self.sensorTimer=20
       ctime= time.time()
       self.dynamicMovingAverage[dynamicMovingAverageMaxCount]=ctime
       deltaTime = ctime - self.dynamicMovingAverage[dynamicMovingAverageMaxCount - self.dynamicMovingAverageCount]
       for i in range(dynamicMovingAverageMaxCount):
         self.dynamicMovingAverage[i]=self.dynamicMovingAverage[i+1]       
       if deltaTime == 0.0:
         speed=0.0
       else:
         speed = self.dynamicMovingAverageCount * WheelCircumference / deltaTime
       self.dynamicMovingAverageCount = self.dynamicMovingAverageCount+1
       if self.dynamicMovingAverageCount > dynamicMovingAverageMaxCount:
         self.dynamicMovingAverageCount = dynamicMovingAverageMaxCount
       #convert to kmh
       speed = speed * 3600.0 / 1000000.0
       self.speedValue = speed
       self.addDistance(WheelCircumference / 1000000.0)

 

GPIO.setmode(GPIO.BCM)
GPIO.setup(WheelSensorGPIO, GPIO.IN,pull_up_down = GPIO.PUD_UP)


app=App()

def gotWheelTurn(channel):
       app.gotPulse()

GPIO.add_event_detect(WheelSensorGPIO, GPIO.FALLING,callback = gotWheelTurn,bouncetime=100) 


app.mainloop()
Don't forget to change the wheel diameter and the WheelSensorGPIO

joestella28
Posts: 3
Joined: Mon Jan 05, 2015 9:30 pm

Re: Stationary Cycle Display

Thu Sep 10, 2015 12:54 pm

Incredible!

Can't wait to try this at the weekend!

Thanks in advance.


Joe

wilblack
Posts: 7
Joined: Thu Nov 21, 2013 4:05 am
Location: Toledo, Oregon
Contact: Website

Re: Stationary Cycle Display

Sat Jan 09, 2016 7:40 pm

Thanks for the post danjperron. Can you explain how you are getting the signal for wheel rotatation. Like how it's connected to the Pi GPIO pins and where it's coming from? I have a stationary bike with an electronic odometer I want to hook up to my Pi, so any advise would be helpful.
danjperron wrote:Your post inspire me!

I never play with tkinter and I decide to give a try. I know you want a console but this is a GUI version with python

2015-09-07-220530_270x372_scrot.png
this is the code.

Code: Select all

try:
    # for Python2
    import Tkinter  as tk
except ImportError:
    # for Python3
    import tkinter as tk
import tkFont
import time
import RPi.GPIO as GPIO





#Wheel diameter in mm
WheelDiameter = 700
WheelCircumference = WheelDiameter * 3.141592654

#how many count in moving average
dynamicMovingAverageMaxCount=8

WheelSensorGPIO=  21


class App():


   def __init__(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
       self.sensorTimer=0
       self.root = tk.Tk()
       self.root.wm_title('Cyclo meter')
       self.deci=0
       self.odometerValue=0.0
       self.speedValue=0.0
       self.speedFrame(self.root)
       self.timerFrame(self.root)
       self.odometerFrame(self.root)
       self.avgSpeedFrame(self.root)
#       self.settingsFrame(self.root)
       self.resetFrame(self.root)
       self.startTime=time.time()
       self.update_clock()
       


   def mainloop(self):
       self.root.mainloop()


   def subFrame(self,mFrame,Title):
       sFrame = tk.Frame(mFrame)
       sFrame.pack(side= tk.TOP,fill=tk.X)
       tFrame = tk.Frame(sFrame)
       tFrame.pack(side=tk.TOP,fill=tk.X)
       lFrame = tk.Frame(tFrame)
       lFrame.pack(side=tk.LEFT,fill=tk.X)
       titleLabel = tk.Label(lFrame,text=Title)
       titleLabel.pack(side=tk.LEFT)
       aFrame = tk.Frame(sFrame)
       aFrame.pack(side=tk.TOP,fill=tk.X)
       self.separator(sFrame,tk.BOTTOM)
       return aFrame
       

   def speedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'speed')
       sFont = tkFont.Font(root=mFrame,family="Courier",size=72)
       self.speedvar = tk.StringVar()
       speedLabel = tk.Label(aFrame,anchor=tk.W,textvariable=self.speedvar,font=sFont)
       self.speedvar.set("{:2.1f}".format(self.speedValue))
       speedLabel.pack(side=tk.LEFT)
       kmhLabel = tk.Label(aFrame,text="km/h",justify=tk.LEFT)
       kmhLabel.pack(side= tk.LEFT)

   def addDistance(self, value):
       self.setOdometer(self.odometerValue + value)
   

   def setOdometer(self, value):
       self.odometerValue =value
       self.odometerVar.set("{:.1f}".format(value))
  
   def setTimer(self, value):
       self.TimerValue =value
       hour= int(value / 3600)
       min = int((value - (hour * 3600)) / 60)
       sec = (value - (hour * 3600) - (min * 60)) 
       self.TimerVar.set("{:02}:{:02}:{:04.1f}".format(hour,min,sec))

   def setAvgSpeed(self, value):
       self.avgSpeedValue =value
       self.avgSpeedVar.set("{:.1f}".format(value))
       


   def timerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Timer')
       self.TimerValue = 0
       self.TimerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=32)
       TmLabel = tk.Label(aFrame,textvariable=self.TimerVar,font=sFont)
       TmLabel.pack(side= tk.BOTTOM)
       self.setTimer(0)

   def avgSpeedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Average speed')
       self.avgSpeedValue = 0.0
       self.avgSpeedVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km/h").pack(side=tk.RIGHT)
       avgLabel = tk.Label(aFrame,textvariable=self.avgSpeedVar,font=sFont)
       avgLabel.pack(side= tk.RIGHT)
       self.setAvgSpeed(0)


   def odometerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Odometer')
       self.odometerValue = 0.0
       self.odometerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km   ").pack(side=tk.RIGHT)
       odometerLabel = tk.Label(aFrame,textvariable=self.odometerVar,font=sFont)
       odometerLabel.pack(side= tk.RIGHT)
       self.setOdometer(0)

   def settingsFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Resistance settings')
       bFrame = tk.Frame(aFrame)
       bFrame.pack(side=tk.TOP)
       self.settings = tk.IntVar()
       for i in range(8):
         tk.Radiobutton(bFrame,text = "{}".format(i),variable=self.settings,value=i).grid(row = i/4,column= i % 4)

   def ResetAll(self):
       self.odometerValue=0;
       self.startTime=time.time()
       self.setTimer(0)
       self.setAvgSpeed(0)

   def setZeroSpeed(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
   

   def resetFrame(self,mFrame):
       tFrame = tk.Frame(mFrame)
       tFrame.pack(side = tk.BOTTOM,fill=tk.X)
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       b = tk.Button(tFrame, text ="Reset", command= self.ResetAll,font=sFont)
       b.pack()
       
      
   def separator(self,master,s_side=tk.BOTTOM):
       Fr = tk.Frame(master,height=2,bg="black")
       Fr.pack(side=s_side,fill=tk.X)

   def update_clock(self):
       self.deci = self.deci + 1
       self.ctime = time.time()
       self.setTimer(self.ctime - self.startTime)       

       if self.deci > 9:
        self.deci=0
        if self.dynamicMovingAverageCount > 2:
          self.setAvgSpeed(self.odometerValue / (self.TimerValue / 3600.0))

       if self.sensorTimer >0:
         self.sensorTimer =self.sensorTimer - 1
         if self.speedValue > 99.9 :
          self.speedValue=0.0
       else:
#         self.setZeroSpeed()
         self.speedValue=0.0

       self.speedvar.set("{:4.1f}".format(self.speedValue))
       self.root.after(100,self.update_clock)
      

   def gotPulse(self):
       self.sensorTimer=20
       ctime= time.time()
       self.dynamicMovingAverage[dynamicMovingAverageMaxCount]=ctime
       deltaTime = ctime - self.dynamicMovingAverage[dynamicMovingAverageMaxCount - self.dynamicMovingAverageCount]
       for i in range(dynamicMovingAverageMaxCount):
         self.dynamicMovingAverage[i]=self.dynamicMovingAverage[i+1]       
       if deltaTime == 0.0:
         speed=0.0
       else:
         speed = self.dynamicMovingAverageCount * WheelCircumference / deltaTime
       self.dynamicMovingAverageCount = self.dynamicMovingAverageCount+1
       if self.dynamicMovingAverageCount > dynamicMovingAverageMaxCount:
         self.dynamicMovingAverageCount = dynamicMovingAverageMaxCount
       #convert to kmh
       speed = speed * 3600.0 / 1000000.0
       self.speedValue = speed
       self.addDistance(WheelCircumference / 1000000.0)

 

GPIO.setmode(GPIO.BCM)
GPIO.setup(WheelSensorGPIO, GPIO.IN,pull_up_down = GPIO.PUD_UP)


app=App()

def gotWheelTurn(channel):
       app.gotPulse()

GPIO.add_event_detect(WheelSensorGPIO, GPIO.FALLING,callback = gotWheelTurn,bouncetime=100) 


app.mainloop()
Don't forget to change the wheel diameter and the WheelSensorGPIO
Wil Black

Check out Lilybot - An system to connect Raspberry Pi, robots, and sensors over the Internet https://github.com/wilblack/lilybot

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Sat Jan 09, 2016 8:32 pm

it's just a reed switch with a magnet attach to the wheel. The kind you use for alarm system.

The GPIO is in input with pull up resistor and the switch will ground the signal.


gnd ====== reed swicth ======= GPIO in INPUT + pull up .

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Wed Sep 06, 2017 7:47 pm

danjperron wrote:
Sat Jan 09, 2016 8:32 pm
it's just a reed switch with a magnet attach to the wheel. The kind you use for alarm system.

The GPIO is in input with pull up resistor and the switch will ground the signal.


gnd ====== reed swicth ======= GPIO in INPUT + pull up .
With Magnetic Reed Switches, they can simply be added to the spoke of the wheel correct? also is there a formula for averaging the speed on both wheels or do you only need the reed switch on wheel?

Many Thanks :)

User avatar
bensimmo
Posts: 2624
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: Stationary Cycle Display

Wed Sep 06, 2017 8:19 pm

For the setup, look at a cheap (Aldi/Lidl etc) cyclocomputer. Cheaper the better. (Though a rear mounted setup is not as common unless you get one capable of cadence then you also the crank arm setup too.*

Chop the wire near the mount and you have you mounting kit, for the reed switch and magnet.

You'll need the true travelled diameter (tyre and pressure with you sat on it) if you want a more accurate result. It'll not be a lot different

look at the inbuilt 'math' module, see if it'll help.
You do get math.pi


*Iirc wireless version should work to and you just tap into the two prongs on the mount (or pull it apart) to get the 'pulse'.
It's been a long time (pre Pi since I haven't around with them).

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:00 pm

Can I ask what ports are you using for this, as im using reed switches as well. Where in the code is the info on ports? many thanks :D
danjperron wrote:
Tue Sep 08, 2015 2:17 am
Your post inspire me!

I never play with tkinter and I decide to give a try. I know you want a console but this is a GUI version with python


2015-09-07-220530_270x372_scrot.png

this is the code.

Code: Select all

try:
    # for Python2
    import Tkinter  as tk
except ImportError:
    # for Python3
    import tkinter as tk
import tkFont
import time
import RPi.GPIO as GPIO





#Wheel diameter in mm
WheelDiameter = 700
WheelCircumference = WheelDiameter * 3.141592654

#how many count in moving average
dynamicMovingAverageMaxCount=8

WheelSensorGPIO=  21


class App():


   def __init__(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
       self.sensorTimer=0
       self.root = tk.Tk()
       self.root.wm_title('Cyclo meter')
       self.deci=0
       self.odometerValue=0.0
       self.speedValue=0.0
       self.speedFrame(self.root)
       self.timerFrame(self.root)
       self.odometerFrame(self.root)
       self.avgSpeedFrame(self.root)
#       self.settingsFrame(self.root)
       self.resetFrame(self.root)
       self.startTime=time.time()
       self.update_clock()
       


   def mainloop(self):
       self.root.mainloop()


   def subFrame(self,mFrame,Title):
       sFrame = tk.Frame(mFrame)
       sFrame.pack(side= tk.TOP,fill=tk.X)
       tFrame = tk.Frame(sFrame)
       tFrame.pack(side=tk.TOP,fill=tk.X)
       lFrame = tk.Frame(tFrame)
       lFrame.pack(side=tk.LEFT,fill=tk.X)
       titleLabel = tk.Label(lFrame,text=Title)
       titleLabel.pack(side=tk.LEFT)
       aFrame = tk.Frame(sFrame)
       aFrame.pack(side=tk.TOP,fill=tk.X)
       self.separator(sFrame,tk.BOTTOM)
       return aFrame
       

   def speedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'speed')
       sFont = tkFont.Font(root=mFrame,family="Courier",size=72)
       self.speedvar = tk.StringVar()
       speedLabel = tk.Label(aFrame,anchor=tk.W,textvariable=self.speedvar,font=sFont)
       self.speedvar.set("{:2.1f}".format(self.speedValue))
       speedLabel.pack(side=tk.LEFT)
       kmhLabel = tk.Label(aFrame,text="km/h",justify=tk.LEFT)
       kmhLabel.pack(side= tk.LEFT)

   def addDistance(self, value):
       self.setOdometer(self.odometerValue + value)
   

   def setOdometer(self, value):
       self.odometerValue =value
       self.odometerVar.set("{:.1f}".format(value))
  
   def setTimer(self, value):
       self.TimerValue =value
       hour= int(value / 3600)
       min = int((value - (hour * 3600)) / 60)
       sec = (value - (hour * 3600) - (min * 60)) 
       self.TimerVar.set("{:02}:{:02}:{:04.1f}".format(hour,min,sec))

   def setAvgSpeed(self, value):
       self.avgSpeedValue =value
       self.avgSpeedVar.set("{:.1f}".format(value))
       


   def timerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Timer')
       self.TimerValue = 0
       self.TimerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=32)
       TmLabel = tk.Label(aFrame,textvariable=self.TimerVar,font=sFont)
       TmLabel.pack(side= tk.BOTTOM)
       self.setTimer(0)

   def avgSpeedFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Average speed')
       self.avgSpeedValue = 0.0
       self.avgSpeedVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km/h").pack(side=tk.RIGHT)
       avgLabel = tk.Label(aFrame,textvariable=self.avgSpeedVar,font=sFont)
       avgLabel.pack(side= tk.RIGHT)
       self.setAvgSpeed(0)


   def odometerFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Odometer')
       self.odometerValue = 0.0
       self.odometerVar = tk.StringVar()
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       tk.Label(aFrame,text="km   ").pack(side=tk.RIGHT)
       odometerLabel = tk.Label(aFrame,textvariable=self.odometerVar,font=sFont)
       odometerLabel.pack(side= tk.RIGHT)
       self.setOdometer(0)

   def settingsFrame(self,mFrame):
       aFrame = self.subFrame(mFrame,'Resistance settings')
       bFrame = tk.Frame(aFrame)
       bFrame.pack(side=tk.TOP)
       self.settings = tk.IntVar()
       for i in range(8):
         tk.Radiobutton(bFrame,text = "{}".format(i),variable=self.settings,value=i).grid(row = i/4,column= i % 4)

   def ResetAll(self):
       self.odometerValue=0;
       self.startTime=time.time()
       self.setTimer(0)
       self.setAvgSpeed(0)

   def setZeroSpeed(self):
       self.dynamicMovingAverage= [0] * (dynamicMovingAverageMaxCount +1)
       self.dynamicMovingAverageCount=0
   

   def resetFrame(self,mFrame):
       tFrame = tk.Frame(mFrame)
       tFrame.pack(side = tk.BOTTOM,fill=tk.X)
       sFont = tkFont.Font(root=mFrame,family="Courier",size=24)
       b = tk.Button(tFrame, text ="Reset", command= self.ResetAll,font=sFont)
       b.pack()
       
      
   def separator(self,master,s_side=tk.BOTTOM):
       Fr = tk.Frame(master,height=2,bg="black")
       Fr.pack(side=s_side,fill=tk.X)

   def update_clock(self):
       self.deci = self.deci + 1
       self.ctime = time.time()
       self.setTimer(self.ctime - self.startTime)       

       if self.deci > 9:
        self.deci=0
        if self.dynamicMovingAverageCount > 2:
          self.setAvgSpeed(self.odometerValue / (self.TimerValue / 3600.0))

       if self.sensorTimer >0:
         self.sensorTimer =self.sensorTimer - 1
         if self.speedValue > 99.9 :
          self.speedValue=0.0
       else:
#         self.setZeroSpeed()
         self.speedValue=0.0

       self.speedvar.set("{:4.1f}".format(self.speedValue))
       self.root.after(100,self.update_clock)
      

   def gotPulse(self):
       self.sensorTimer=20
       ctime= time.time()
       self.dynamicMovingAverage[dynamicMovingAverageMaxCount]=ctime
       deltaTime = ctime - self.dynamicMovingAverage[dynamicMovingAverageMaxCount - self.dynamicMovingAverageCount]
       for i in range(dynamicMovingAverageMaxCount):
         self.dynamicMovingAverage[i]=self.dynamicMovingAverage[i+1]       
       if deltaTime == 0.0:
         speed=0.0
       else:
         speed = self.dynamicMovingAverageCount * WheelCircumference / deltaTime
       self.dynamicMovingAverageCount = self.dynamicMovingAverageCount+1
       if self.dynamicMovingAverageCount > dynamicMovingAverageMaxCount:
         self.dynamicMovingAverageCount = dynamicMovingAverageMaxCount
       #convert to kmh
       speed = speed * 3600.0 / 1000000.0
       self.speedValue = speed
       self.addDistance(WheelCircumference / 1000000.0)

 

GPIO.setmode(GPIO.BCM)
GPIO.setup(WheelSensorGPIO, GPIO.IN,pull_up_down = GPIO.PUD_UP)


app=App()

def gotWheelTurn(channel):
       app.gotPulse()

GPIO.add_event_detect(WheelSensorGPIO, GPIO.FALLING,callback = gotWheelTurn,bouncetime=100) 


app.mainloop()
Don't forget to change the wheel diameter and the WheelSensorGPIO

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:06 pm

Where in the code is the info on ports? many thanks :D
Only one GPIO port! GPIO21

Code: Select all

WheelSensorGPIO=  21
Everything is triggered by GPIO.add_event_detect.

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:10 pm

Thanks, sorry for any confusion, my reed switch requires 2 ports
danjperron wrote:
Mon Jan 22, 2018 6:06 pm
Where in the code is the info on ports? many thanks :D
Only one GPIO port! GPIO21

Code: Select all

WheelSensorGPIO=  21
Everything is triggered by GPIO.add_event_detect.

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:20 pm

Thanks, sorry for any confusion, my reed switch requires 2 ports
What kind of reed switch ? Does it senses direction ?

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:24 pm

Well its a door sensor, i would show a picture but i dont know how to post them on here

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Mon Jan 22, 2018 6:32 pm

Use a multimeter and check if you could use one of the reed switch.

it is possible that door switch use one of the inputs for intrusion sensing?

User avatar
bensimmo
Posts: 2624
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: Stationary Cycle Display

Mon Jan 22, 2018 8:46 pm

A standard reed will have two prongs
So gpio and either +ve or gnd pin depending on how you connect it for he Pi. Exactly the same as you would a normal switch, two bits of wire, etc.
They are usually Normally Open (so 'off') with no magnet near them.


You can get two way reed switches, so one end in the 'reed' and the other has two connections for each way (dual pole).
One is (N.O.)
Normally Open (off)
The other is (N.C.) Normally Closes (so on).

And will switch over with a magnet near them.


Of it is in a white box, so you cannot see inside.
Rip it open and see how it works :-)

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Thu Mar 29, 2018 4:51 pm

Just a question which circumference of the wheel do you use, the inner or out circumference

User avatar
bensimmo
Posts: 2624
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: Stationary Cycle Display

Thu Mar 29, 2018 5:47 pm

Not sure what you mean by inner,.
But for distance (and hence speed) you would use the circumference the tyre would travel. Which is slightly less than circumference around the wheel and tyre.
700c/20mm will be around 2110mm
26"/1.9" will be around 2060mm
Tyre vary even at those widths.

There is a website out there that does some maths, (bikecalc) but easiest to mark it, sit on it trundle along, mark it and then measure... Sort of thing.

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Thu Mar 29, 2018 6:00 pm

Ah okay thanks, I was over thinking what i was doing.

I have another question (sorry to pester) I was going through the code above and trying to find out how you calculated the current speed.

User avatar
bensimmo
Posts: 2624
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: Stationary Cycle Display

Thu Mar 29, 2018 7:16 pm

Not my code and I can't remember it , but the usual one is to time one revolution (say in seconds), from that you know how far it has traveled (circumference in mm). Then just use maths to get mm/second.
Convert to whatever units you are using.

Read up on basic maths/physics for speed and distance and velocity.

danjperron
Posts: 2854
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Stationary Cycle Display

Thu Mar 29, 2018 7:47 pm

Not my code and I can't remember it , but the usual one is to time one revolution (say in seconds), from that you know how far it has traveled (circumference in mm). Then just use maths to get mm/second.
Convert to whatever units you are using
It is mine. ;-)

Yes! it is exactly how I done it but with a twist. I added a dynamic running average to minimize the fluctuation!

StefanGriffiths
Posts: 25
Joined: Sun Aug 06, 2017 12:14 pm

Re: Stationary Cycle Display

Sun Apr 08, 2018 9:48 am

I've got to ask,

I want to record current speed with my program, I'm using a reed switch but my data is getting displayed to Swift App with the timer running off there, how would I work out the current speed that way?

Return to “Automation, sensing and robotics”

Who is online

Users browsing this forum: No registered users and 8 guests