From ca058725e8980b2c285315ad12fc0b5ea77aa553 Mon Sep 17 00:00:00 2001 From: Nils Schulte Date: Thu, 19 Mar 2020 22:34:50 +0100 Subject: [PATCH] added Screens --- Button.py | 2 +- Buzzer.py | 69 +++++++++++++++++++++++++++ HousingLEDs.py | 31 +++++++++++++ LED.py | 40 ++++++++++++++++ Screens.py | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ Settings.py | 2 + StepperClock.py | 2 - StepperL298M.py | 61 +++++++++++++----------- main.py | 96 +++++++++++++------------------------- 9 files changed, 331 insertions(+), 93 deletions(-) create mode 100644 Buzzer.py create mode 100644 HousingLEDs.py create mode 100644 LED.py create mode 100644 Screens.py create mode 100644 Settings.py diff --git a/Button.py b/Button.py index 3c77236..8458470 100644 --- a/Button.py +++ b/Button.py @@ -39,4 +39,4 @@ class Button(): pass def isPushed(self): - return self.pin.value() ^ self.inverted \ No newline at end of file + return not (self.pin.value() ^ self.inverted) \ No newline at end of file diff --git a/Buzzer.py b/Buzzer.py new file mode 100644 index 0000000..73ff9b8 --- /dev/null +++ b/Buzzer.py @@ -0,0 +1,69 @@ +import machine +import uasyncio as asyncio + + +c = 261 #Hz +d = 294 #Hz +e = 329 #Hz +f = 349 #Hz +g = 392 #Hz +a = 440 #Hz +b = 493 #Hz +C = 523 #Hz + +# (Duration,Freq) +# If Last Duration is None play endlessly +# 0: Silent +# 1: BeepBeep +# 2: Alle Meine Entchen +SOUNDS = [((0,None),),\ + ((700,1000),(100,None),(600,1000)),\ + ((400,c),(100,None),(400,d),(100,None),(400,e),(100,None),(400,f),(100,None),\ + (900,g),(100,None),(900,g),(100,None),(400,a),(100,None),(400,a),(100,None),\ + (400,a),(100,None),(400,a),(100,None),(1900,g),(100,None),(400,a),(100,None),\ + (400,a),(100,None),(400,a),(100,None),(400,a),(100,None),(1900,g),(100,None),\ + (400,f),(100,None),(400,f),(100,None),(400,f),(100,None),(400,f),(100,None),\ + (900,e),(100,None),(900,e),(100,None),(400,d),(100,None),(400,d),(100,None),\ + (400,d),(100,None),(400,d),(100,None),(2000,c))]#,(None,None) + +class Buzzer(): + def __init__(self,pin,duty = 100): + self._pwm = machine.PWM(machine.Pin(2), freq=0, duty=0) + self.sound = None + self.duty = duty + self.newSound = True + loop = asyncio.get_event_loop() + loop.create_task(self._update_async()) + + async def _update_async(self): + while True: + if self.sound == None: + await asyncio.sleep_ms(200) + self._pwm.duty(0) + else: + self.newSound = False + for i in range(len(self.sound)): + s = self.sound[i] + if s[0] == None: + i = 0 + if (s[1] != None): + self._pwm.freq(s[1]) + self._pwm.duty(self.duty) + else: + self._pwm.duty(0) + await asyncio.sleep_ms(s[0]) + if self.newSound: + break + if (not self.newSound): + self.sound = None + self.newSound = False + + def playSound(self,sound): + self.newSound = True + self.sound = sound + + def stop(self): + self.sound = None + + def isPlaying(self): + return self.sound == None \ No newline at end of file diff --git a/HousingLEDs.py b/HousingLEDs.py new file mode 100644 index 0000000..27da5af --- /dev/null +++ b/HousingLEDs.py @@ -0,0 +1,31 @@ +import uasyncio as asyncio +import machine +import neopixel + +class LEDs(): + def __init__(self, pin): + self.neop = neopixel.NeoPixel(machine.Pin(pin, machine.Pin.OUT),5) + self.rate = 0 + self.clear() + loop = asyncio.get_event_loop() + loop.create_task(self._update_async()) + + async def _update_async(self): + while True: + if self.rate <= 0: + await asyncio.sleep_ms(200) + else: + self.toggle() + await asyncio.sleep_ms(int(500 / self.rate)) + + def fill(self,color): + self.neop.fill(color) + self.neop.write() + + def clear(self): + self.fill((0,0,0)) + + def upper(self,color): + self.neop[3] = color + self.neop.write() + diff --git a/LED.py b/LED.py new file mode 100644 index 0000000..93788ee --- /dev/null +++ b/LED.py @@ -0,0 +1,40 @@ +import uasyncio as asyncio +import machine + +class LED(): + def __init__(self, pin): + self.pin = machine.Pin(pin,machine.Pin.OUT) + self.rate = 0 + loop = asyncio.get_event_loop() + loop.create_task(self._update_async()) + + async def _update_async(self): + while True: + if self.rate <= 0: + await asyncio.sleep_ms(200) + else: + self.toggle() + await asyncio.sleep_ms(int(500 / self.rate)) + + def toggle(self): + self.pin.value(not self.pin.value()) + + def flash(self, rate): + """rate:\t flashing speed in Herz""" + self.rate = rate + + def on(self,overwriteFlashing=True): + self.pin.on() + if overwriteFlashing: + self.rate = 0 + + def off(self,overwriteFlashing=True): + self.pin.off() + if overwriteFlashing: + self.rate = 0 + + def value(self,value=None): + if value!=None: + self.pin.value(value) + return self.pin.value() + diff --git a/Screens.py b/Screens.py new file mode 100644 index 0000000..3b8349f --- /dev/null +++ b/Screens.py @@ -0,0 +1,121 @@ +import StepperClock +import uasyncio as asyncio +import Buzzer +import Settings + +class ClockScreen(): + + def __init__(self,stepperhour,stepperminu,buttons,leds): + self.clock = StepperClock.StepperClock(stepperhour,stepperminu) + self.running = False + self.buttons = buttons + self.leds = leds + + def stopIfAllPressed(self,button): + self.leds[button].on() + for b in self.buttons: + if not b.isPushed(): + break + else: + self.running = False + + def __await__(self): + for i,b in enumerate(self.buttons): + b.setCallbacks(onPushDown=lambda i=i:self.stopIfAllPressed(i),onPushUp=lambda i=i:self.leds[i].off()) + self.running = True + self.clock.start() + while self.running: + await asyncio.sleep_ms(200) + self.clock.stop() + __iter__ = __await__ # https://github.com/micropython/micropython/issues/2678 + +class SettingsScreen(): + + def __init__(self,stepperhour,stepperminu,buttons,leds,housingLEDs,buzzer): + self.stepperminu = stepperminu + self.stepperhour = stepperhour + self.running = False + self.buttons = buttons + self.leds = leds + self.housingLEDs = housingLEDs + self.buzzer = buzzer + + # TODO load these form file + + def onButtonPressed(self,button): + self.leds[button].on(overwriteFlashing=False) + for b in self.buttons: + if not b.isPushed(): + break + else: + self.running = False + return + if not self.buttons[1].isPushed(): + if button == 0 : + if self.mode == 0: + self.stepperminu.rotateTo(direction=1) + if self.mode == 1: + self.stepperhour.rotateTo(direction=1) + elif button == 2: + if self.mode == 0: + self.stepperminu.rotateTo(direction=-1) + if self.mode == 1: + self.stepperhour.rotateTo(direction=-1) + + def onButtonReleased(self,button): + self.leds[button].off(overwriteFlashing=False) + if button == 0:#left + if self.mode == 0 or self.mode == 1: + self.stepperminu.stop() + self.stepperhour.stop() + if self.mode == 3: + Settings.selectedSound =(Settings.selectedSound -1) % len(Buzzer.SOUNDS) + self.buzzer.playSound(Buzzer.SOUNDS[Settings.selectedSound ]) + elif button == 2:#right + if self.mode == 0 or self.mode == 1: + self.stepperminu.stop() + self.stepperhour.stop() + if self.mode == 3: + Settings.selectedSound =(Settings.selectedSound +1) % len(Buzzer.SOUNDS) + self.buzzer.playSound(Buzzer.SOUNDS[Settings.selectedSound ]) + elif button == 1: #middle + self.mode=(self.mode+1)%4 + if self.mode == 3: + self.buzzer.playSound(Buzzer.SOUNDS[Settings.selectedSound ]) + else: + self.buzzer.stop() + + if self.mode == 2: + self.leds[0].flash(0.7) + self.leds[1].flash(0.7) + self.leds[2].flash(0.7) + else: + self.leds[0].off() + self.leds[1].off() + self.leds[2].off() + + if self.mode == 0 or self.mode == 1: + self.housingLEDs.upper((50,50,50)) + else: + self.housingLEDs.clear() + + def __await__(self): + self.mode = 0 + self.stepperminu.rotateTo(0) + self.stepperhour.rotateTo(0) + while not self.stepperhour.isAtTarget() or not self.stepperminu.isAtTarget(): + await asyncio.sleep_ms(200) + self.housingLEDs.upper((50,50,50)) + for led in self.leds: + led.off() + for i,b in enumerate(self.buttons): + b.setCallbacks(onPushDown=lambda i=i:self.onButtonPressed(i),onPushUp=lambda i=i:self.onButtonReleased(i)) + self.running = True + while self.running: + await asyncio.sleep_ms(200) + for led in self.leds: + led.off() + self.housingLEDs.clear() + self.stepperhour.reset() + self.stepperminu.reset() + __iter__ = __await__ # https://github.com/micropython/micropython/issues/2678 \ No newline at end of file diff --git a/Settings.py b/Settings.py new file mode 100644 index 0000000..9d0ebef --- /dev/null +++ b/Settings.py @@ -0,0 +1,2 @@ + +selectedSound = 0 \ No newline at end of file diff --git a/StepperClock.py b/StepperClock.py index 2d2fa6c..491c48b 100644 --- a/StepperClock.py +++ b/StepperClock.py @@ -12,8 +12,6 @@ class StepperClock: def start(self): if not self.started_async: self.started_async = True - self._stepperMinute.start() - self._stepperHour.start() loop = asyncio.get_event_loop() loop.create_task(self._update_async()) diff --git a/StepperL298M.py b/StepperL298M.py index 912b4c7..b5f4bc4 100644 --- a/StepperL298M.py +++ b/StepperL298M.py @@ -7,21 +7,19 @@ stepping = [[1,0,1,0],[1,0,0,0],[1,0,0,1],[0,0,0,1],[0,1,0,1],[0,1,0,0],[0,1,1,0 class Stepper: def __init__(self,pins,stepsPerRev=400,stepDurationMs=10,inverted=False): self._pins = [Pin(p, Pin.OUT) for p in pins] - self.stepnum = 0 - self.rotDirection = 1 - self._rotTarget = 0 + self.reset() self.stepDurationMs = stepDurationMs self._speedMs = 0 self._lastStepTime = time.time() self.stepsPerRev=stepsPerRev - self.started_async = False self.inverted = inverted + loop = asyncio.get_event_loop() + loop.create_task(self._update_async()) - def start(self): - if not self.started_async: - self.started_async = True - loop = asyncio.get_event_loop() - loop.create_task(self._update_async()) + def reset(self): + self.stepnum = 0 + self._rotTarget = 0 + self.rotDirection = 0 def _setPins(self,level): self._pins[0].value(level[0]) @@ -33,25 +31,33 @@ class Stepper: #return ((self._rotTarget - self.stepnum)*self.rotDirection)%self.stepsPerRev return ((self._rotTarget - self.stepnum)*self.rotDirection) - def rotateTo(self, target,duration=0,direction=0,normalise = True): + def rotateTo(self, target=None,duration=0,direction=0,normalise = True): """sets the rotation target and (if direction == 0) calculates if - it should rotate left or right""" - self._rotTarget = int(target*self.stepsPerRev) - if normalise: - rotTargetNorm = self._rotTarget%self.stepsPerRev - stepsNorm = self.stepnum%self.stepsPerRev - self._rotTarget = self.stepnum+rotTargetNorm-stepsNorm - if (self._rotTarget-self.stepnum) > self.stepsPerRev/2: - self._rotTarget-=self.stepsPerRev - elif(self._rotTarget-self.stepnum) < -self.stepsPerRev/2: - self._rotTarget+=self.stepsPerRev - - if not self.isAtTarget(): - if (direction == 0): - self.rotDirection = 1 if (self._rotTarget > self.stepnum) else -1 - else: - self.rotDirection = direction - self._speedMs = max(int(self.stepDurationMs),duration*1000/self.getStepsToTarget()) + it should rotate left or right. + If target is None moves infinitely in direction + (if direction is 0 then hold position with power).""" + if target == None: + self._rotTarget = None + self.rotDirection = direction + else: + self._rotTarget = int(target*self.stepsPerRev) + if normalise: + rotTargetNorm = self._rotTarget%self.stepsPerRev + stepsNorm = self.stepnum%self.stepsPerRev + self._rotTarget = self.stepnum+rotTargetNorm-stepsNorm + if (self._rotTarget-self.stepnum) > self.stepsPerRev/2: + self._rotTarget-=self.stepsPerRev + elif(self._rotTarget-self.stepnum) < -self.stepsPerRev/2: + self._rotTarget+=self.stepsPerRev + if not self.isAtTarget(): + if (direction == 0): + self.rotDirection = 1 if (self._rotTarget > self.stepnum) else -1 + else: + self.rotDirection = direction + self._speedMs = max(int(self.stepDurationMs),duration*1000/self.getStepsToTarget()) + + def stop(self): + self._rotTarget = self.stepnum async def _update_async(self): while(True): @@ -59,6 +65,7 @@ class Stepper: if not self.isAtTarget(): self._update() else: + self.rotDirection = 0 self.disablePower() def _update(self): diff --git a/main.py b/main.py index d3fe059..9057c80 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,14 @@ import time import machine +import uasyncio as asyncio import DS3231 import Button -import uasyncio as asyncio +import LED +import HousingLEDs +import Buzzer import StepperL298M -import StepperClock -import neopixel + +import Screens #Async loop = asyncio.get_event_loop() @@ -17,70 +20,37 @@ try: rtc.init(dsRtc.DateTime()+[0]) except: print("Error on DS3231-Inititalisation") - -housingLEDs = neopixel.NeoPixel(machine.Pin(16, machine.Pin.OUT),5) -housingLEDs.fill((0,0,0)) -housingLEDs.write() -Buzzer = machine.PWM(machine.Pin(2), freq=10000, duty=0) +#Init Buzzer +buzzer = Buzzer.Buzzer(pin=2) #Initalise the Motors -stepDurationMs = 8 -stepperminu = StepperL298M.Stepper([19,21,22,23],inverted=False,stepDurationMs=stepDurationMs) -stepperhour = StepperL298M.Stepper([12,27,26,25],inverted=True, stepDurationMs=stepDurationMs) +stepperminu = StepperL298M.Stepper([19,21,22,23],inverted=False) +stepperhour = StepperL298M.Stepper([12,27,26,25],inverted=True) + +#Init LEDs +housingLEDs = HousingLEDs.LEDs(pin=16) + +leftButtonLED = LED.LED(13) +middleButtonLED = LED.LED(33) +rightButtonLED = LED.LED(32) + +#Init Buttons +leftButton = Button.Button(pin=34,inverted=True) +middleButton = Button.Button(pin=39,inverted=True) +rightButton = Button.Button(pin=36,inverted=True) -leftButtonLED = machine.Pin(32,machine.Pin.OUT) -middleButtonLED = machine.Pin(33,machine.Pin.OUT) -rightButtonLED = machine.Pin(13,machine.Pin.OUT) +buttons = (leftButton,middleButton,rightButton) +leds = (leftButtonLED,middleButtonLED,rightButtonLED) +modus = 0 +screens = (Screens.ClockScreen(stepperhour,stepperminu,buttons,leds),\ + Screens.SettingsScreen(stepperhour,stepperminu,buttons,leds,housingLEDs,buzzer)) +async def run_screens(): + global modus,screens + while True: + await screens[modus%len(screens)] + modus += 1 -clock = StepperClock.StepperClock(stepperhour,stepperminu) - - -def leftButtonClickDown(): - leftButtonLED.on() -def leftButtonClickUp(): - leftButtonLED.off() -def leftButtonClick(): - if(clock.isRunning()): - stepperminu.stepnum = (stepperminu.stepnum+8)%stepperminu.stepsPerRev - else: - stepperminu.rotateTo(stepperminu.getOrientation()+0.25) -leftButton = Button.Button(pin=36,onPushDown=leftButtonClickDown,onPushUp=leftButtonClickUp,onClick=leftButtonClick,inverted=True) - -def rightButtonClickDown(): - rightButtonLED.on() -def rightButtonClickUp(): - rightButtonLED.off() -def rightButtonClick(): - if(clock.isRunning()): - stepperminu.stepnum = (stepperminu.stepnum+8)%stepperminu.stepsPerRev - else: - stepperminu.rotateTo(stepperminu.getOrientation()-0.25) - -rightButton = Button.Button(pin=34,onPushDown=rightButtonClickDown,onPushUp=rightButtonClickUp,onClick=rightButtonClick,inverted=True) - -def middleButtonClickDown(): - pass -def middleButtonClickUp(): - pass -def middleButtonClick(): - middleButtonLED.value(not middleButtonLED.value()) - if middleButtonLED.value(): - clock.stop() - stepperminu.rotateTo(0) - stepperhour.rotateTo(0) - housingLEDs.fill((255,0,255)) - housingLEDs.write() - else: - clock.start() - housingLEDs.fill((0,0,0)) - housingLEDs.write() -middleButton = Button.Button(pin=39,onPushDown=middleButtonClickDown,onPushUp=middleButtonClickUp,onClick=middleButtonClick,inverted=True) - - - -clock.start() - -loop.run_forever() +loop.run_until_complete(run_screens()) \ No newline at end of file