From 54babcede80cf8e28a9ecc52cbbf8464333d2e43 Mon Sep 17 00:00:00 2001 From: GuyCarver Date: Mon, 24 Nov 2014 22:40:17 -0500 Subject: [PATCH] Add JYMCU, IRDistance, Relay and SR04Distance Bunch of driver and code format changes --- DistanceDisplay.py | 122 +++++++++++++++++++++++++++++++++++ Lib/IRDistance.py | 33 ++++++++++ Lib/JYMCU.py | 69 ++++++++++++++++++++ Lib/L298N.py | 29 ++++----- Lib/PIR.py | 16 ++--- Lib/PWM.py | 20 +++--- Lib/Relay.py | 30 +++++++++ Lib/SR04Distance.py | 71 +++++++++++++++++++++ Lib/ST7735.py | 20 +++--- Lib/ultrasonic.py | 82 ------------------------ SonarDisplay.py | 144 ------------------------------------------ TreatThrower.py | 2 +- _SYNCAPP/metadata.xml | Bin 18078 -> 20928 bytes bombs.py | 4 +- level.py | 4 +- main.py | 64 ++++++++++++------- motion.py | 2 +- 17 files changed, 413 insertions(+), 299 deletions(-) create mode 100644 DistanceDisplay.py create mode 100644 Lib/IRDistance.py create mode 100644 Lib/JYMCU.py create mode 100644 Lib/Relay.py create mode 100644 Lib/SR04Distance.py delete mode 100644 Lib/ultrasonic.py delete mode 100644 SonarDisplay.py diff --git a/DistanceDisplay.py b/DistanceDisplay.py new file mode 100644 index 0000000..87cc5e3 --- /dev/null +++ b/DistanceDisplay.py @@ -0,0 +1,122 @@ +#Display distance in inches on ST7734 LCD. +#Distance is taken from the given distance sensor. + +import pyb +import terminalfont + +ZeroPoint = (0, 0) +DISPLAY_DELAY = 100 +FONT_HEIGHT = terminalfont.terminalfont["Height"] +NUM_DISTANCES = 4 #The number of _distances to use for throwing away anomalies +THRESHOLD = 0.5 + +#100-15 = blue +#15-10 = green +#10-5 = yellow +#5-0 = red + +COLORS = [(0, 255, 0, 0), + (.35, 255, 255, 0), + (.50, 0, 255, 0), + (.75, 0, 255, 255), + (1.0, 0, 0, 255) + ] + +def round( aValue ) : + '''Round float value to 2 decimal places''' + return (aValue - (aValue % 0.01)) + +def getrgb( aDisplay, aDistance, maxdist ) : + '''Get an interpolated color based on distance. + Uses the COLORS list.''' + clr = aDisplay.NAVY + + def interp( l, v0, v1 ) : + return int(v0 * (1.0 - l) + (v1 * l)) + + for i in range(1, len(COLORS)) : + c = COLORS[i] + if c[0] * maxdist >= aDistance: + rng0, r0, g0, b0 = COLORS[i - 1] + rng1, r1, g1, b1 = c + rng0 *= maxdist + rng1 *= maxdist + #interpolate between rng0 and rng1 + l = (aDistance - rng0) / float(rng1 - rng0) + r = interp(l, r0, r1) + g = interp(l, g0, g1) + b = interp(l, b0, b1) + clr = aDisplay.color(r,g,b) + break + return clr + +class RangePoint(object): + """Display a point on the screen""" + + def __init__( self, size, maxrange ) : + self._size = (50, size) + self._pos = (-1, 0) + self._prevdistance = -1 + self._maxrange = maxrange + + def update( self, aDisplay, aDistance, aTime ) : + if (self._prevdistance != aDistance): + self._draw(aDisplay, 0) + clr = getrgb(aDisplay, aDistance, self._maxrange) + y = min(1.0, aDistance / self._maxrange) + self._pos = (int((aDisplay.size()[0] / 2) - (self._size[0] / 2)), int(y * aDisplay.size()[1] - self._size[1])) + self._draw(aDisplay, clr) + self._prevdistance = aDistance + + def _draw( self, aDisplay, aColor ) : + if self._pos[0] >= 0: + aDisplay.fillrect(self._pos, self._size, aColor) + +def wrap( aVal, aMax ) : return aVal if aVal < aMax else 0 + +class Display(object): + """Display distance on ST7735 LCD with text and a box""" + def __init__( self, display, ranger ): + self._display = display + self._ranger = ranger + self._rangepoint = RangePoint(4, ranger.maxinches) + self._curdistance = 0.0 + self._distances = [0.0] * NUM_DISTANCES + self._distindex = 0 + + def printdistance( self, aDistance ) : + s = "I:" + str(round(aDistance)) + self._display.fillrect(ZeroPoint, (self._display.size()[0], FONT_HEIGHT * 2), 0) + self._display.text(ZeroPoint, s, self._display.CYAN, terminalfont.terminalfont, 2) + + def _getdistance( self ) : + '''Throw away changes that are not averaged. This introduces + a slight delay in update but gets rid of most bad _distances''' + + d = self._ranger.inches +# self._curdistance = d + good = 0 + for c in self._distances : + if abs(c - d) < THRESHOLD: + good += 1 + if good > 2: + self._curdistance = d + break + + self._distances[self._distindex] = d + self._distindex = wrap(self._distindex + 1, NUM_DISTANCES) + return self._curdistance + + def run( self ) : + self._display.fill(0) + sw = pyb.Switch() + lasttime = pyb.millis() + while sw() == False : + pyb.delay(DISPLAY_DELAY) + distance = self._getdistance() + + thistime = pyb.millis() + t = thistime - lasttime + self.printdistance(distance) + self._rangepoint.update(self._display, distance, t / 1000.0) + lasttime = thistime diff --git a/Lib/IRDistance.py b/Lib/IRDistance.py new file mode 100644 index 0000000..98a4984 --- /dev/null +++ b/Lib/IRDistance.py @@ -0,0 +1,33 @@ + +from pyb import Pin, ADC + +class IRDistance(object): + """ Driver for Sharp Gp2y0a IR distance sensor. The distance + range is around 3 to 40 inches. """ + + maxinches = 31.5 #Maximun range of IR board in inches. + _v2i = -1.02 #Voltage to inches power. + + def __init__( self, pin ) : + """pin may be name or pin object. It must be able to handle ADC input.""" + + if type(pin) == str: + p = Pin(pin) + elif type(pin) == Pin: + p = pin + else: + raise Exception("pin must be pin name or pyb.Pin able to support ADC") + + self._adc = ADC(p) + + @property + def distance( self ) : return self._adc.read() + + @property + def inches( self ) : + volts = self.distance * 0.0048828125 + return 65.0 * pow(volts, IRDistance._v2i) + + @property + def centimeters( self ) : return self.inches * 2.54 + diff --git a/Lib/JYMCU.py b/Lib/JYMCU.py new file mode 100644 index 0000000..0146d7d --- /dev/null +++ b/Lib/JYMCU.py @@ -0,0 +1,69 @@ + +from pyb import UART, repl_uart, udelay + +# JY-MCU Bluetooth board ---------------------------------------- + +# This opens connection with Bluetooth module connected to Y1, Y2 (UART 6) +# Then it sets the repl output to this UART. +#COMMANDS AT - does nothing but get an ok. +# The posible baudrates are: +# AT+BAUD1-------1200 +# AT+BAUD2-------2400 +# AT+BAUD3-------4800 +# AT+BAUD4-------9600 - Default for hc-06 +# AT+BAUD5------19200 +# AT+BAUD6------38400 +# AT+BAUD7------57600 - Johnny-five speed +# AT+BAUD8-----115200 +# AT+BAUD9-----230400 +# AT+BAUDA-----460800 +# AT+BAUDB-----921600 +# AT+BAUDC----1382400 +# AT+VERSION +# AT+NAMEnewname This is the name that will show up in windows. +# AT+PIN???? set 4 digit pairing pin. + +class JYMCU(object): + """JY-MCU Bluetooth serial device driver. This is simply a light UART wrapper + with addition AT command methods to customize the device.""" + + def __init__( self, uart, baudrate ): + """ uart = uart #1-6, baudrate must match what is set on the JY-MCU. + Needs to be a #1-C. """ + self._uart = UART(uart, baudrate) + + def __del__( self ) : self._uart.deinit() + + def any( self ) : return self._uart.any() + + def write( self, astring ) : return self._uart.write(astring) + def writechar( self, achar ) : self._uart.writechar(achar) + + def read( self, num = None ) : return self._uart.read(num) + def readline( self ) : return self._uart.readline() + def readchar( self ) : return self._uart.readchar() + def readall( self ) : return self._uart.readall() + def readinto( self, buf, count = None ) : return self._uart.readinto(buf, count) + + def _cmd( self, cmd ) : + """ Send AT command, wait a bit then return result string. """ + self._uart.write("AT+" + cmd) + udelay(500) + return self.readline() + + def baudrate( self, rate ) : + """ Set the baud rate. Needs to be #1-C. """ + return self._cmd("BAUD" + str(rate)) + + def name( self, name ) : + """ Set the name to show up on the connecting device. """ + return self._cmd("NAME" + name) + + def pin( self, pin ) : + """ Set the given 4 digit numeric pin. """ + return self._cmd("PIN" + str(pin)) + + def version( self ) : return self._cmd("VERSION") + + def setrepl( self ) : repl_uart(self._uart) + diff --git a/Lib/L298N.py b/Lib/L298N.py index d5e996a..de8c78b 100644 --- a/Lib/L298N.py +++ b/Lib/L298N.py @@ -1,28 +1,26 @@ #Driver for the L298N Dual HBridge motor controller. from PWM import PWM -from pyb import Pin - -#motordata -#Forward pin -#Back pin -#Speed pin (PWM) +from pyb import Pin, delay class Motor( ): - """docstring for Motor""" + """Control a motor connected to the L298N Dual motor controller.""" - def __init__(self, forward, backward, speed): - """Speed = (pin name, timer#)""" + def __init__( self, forward, backward, speed ) : + """forward pin name, backward pin name, speed = (pin name, timer#) + Need to make sure the given timer # is associated with the speed + pin or an exception will be raised. The speed pin must support + PWM.""" self._forward = Pin(forward, Pin.OUT_PP) self._backward = Pin(backward, Pin.OUT_PP) self._speedControl = PWM(speed[0], speed[1]) self._speed = 0 @property - def speed(self): return self._speed + def speed( self ) : return self._speed @speed.setter - def speed(self, value): + def speed( self, value ) : self._speed = value if (value == 0): self._forward.low() @@ -34,11 +32,12 @@ class Motor( ): self._forward.high() self._backward.low() - self._speedControl.pulse_width_percent = abs(value) + self._speedControl.pulse_width_percent = min(100, abs(value)) def brake( self ) : - """ """ + """ Brake the motor by sending power both directions. """ self._forward.high() self._backward.high() - self._speedControl.pulse_width_percent(1.0) - + self._speedControl.pulse_width_percent = 100 + delay(1000) + self.speed = 0 diff --git a/Lib/PIR.py b/Lib/PIR.py index df00209..d83a6fa 100644 --- a/Lib/PIR.py +++ b/Lib/PIR.py @@ -32,38 +32,38 @@ class PIR(object): self._power.low() @property - def power(self): return True if (self._power == None) else self._power.value() + def power( self ) : return True if (self._power == None) else self._power.value() @power.setter - def power(self, value): self._onoff(value) + def power( self, value ) : self._onoff(value) def on( self ) : self.power = True def off( self ) : self.power = False @property - def trigger(self): return self._trigger.value() + def trigger( self ) : return self._trigger.value() @property - def interrupt(self): return self._interrupt + def interrupt( self ) : return self._interrupt @interrupt.setter - def interrupt(self, func): + def interrupt( self, func ) : self._interrupt = None; self._func = func if (func != None): self._interrupt = pyb.ExtInt(self._trigger, pyb.ExtInt.IRQ_RISING_FALLING, pyb.Pin.PULL_DOWN, self._inthandler) self._inton = True - def _inthandler(self, line): + def _inthandler( self, line ) : '''Function to handle interrupts and pass on to callback with on/off trigger state.''' if (self._func != None): self._func(self.trigger) @property - def inton(self): return self._inton + def inton( self ) : return self._inton @inton.setter - def inton(self, value): + def inton( self, value ) : self._inton = value if self._interrupt != None: if value : diff --git a/Lib/PWM.py b/Lib/PWM.py index c008108..da0d542 100644 --- a/Lib/PWM.py +++ b/Lib/PWM.py @@ -30,11 +30,11 @@ class PWM(object): } class PWMException(Exception): - def __init__(self, msg): + def __init__( self, msg ) : self.msg = msg @staticmethod - def timerandchannel( pinname, timernum ): + def timerandchannel( pinname, timernum ) : try: a = PWM.PinChannels[pinname] if timernum <= 0: @@ -48,7 +48,7 @@ class PWM(object): raise PWM.PWMException("Pin {} cannot use timer {}".format(pinname, timernum)) - def __init__( self, p, timernum, afreq = 100 ): + def __init__( self, p, timernum, afreq = 100 ) : isname = type(p) == str pinname = p if isname else p.name() timernum, channel = PWM.timerandchannel(pinname, timernum) @@ -59,23 +59,23 @@ class PWM(object): self._channel = self._timer.channel(channel, Timer.PWM, pin = p) @property - def pulse_width(self): return self._channel.pulse_width() + def pulse_width( self ) : return self._channel.pulse_width() @pulse_width.setter - def pulse_width(self, value): self._channel.pulse_width(value) + def pulse_width( self, value ) : self._channel.pulse_width(value) @property - def pulse_width_percent(self): return self._channel.pulse_width_percent() + def pulse_width_percent( self ) : return self._channel.pulse_width_percent() @pulse_width_percent.setter - def pulse_width_percent(self, value): self._channel.pulse_width_percent(value) + def pulse_width_percent( self, value ) : self._channel.pulse_width_percent(value) @property - def freq(self): return self._timer.freq() + def freq( self ) : return self._timer.freq() @freq.setter - def freq(self, value): self._timer.freq(value) + def freq( self, value ) : self._timer.freq(value) - def callback(self, value): + def callback( self, value ) : self._channel.callback(value) diff --git a/Lib/Relay.py b/Lib/Relay.py new file mode 100644 index 0000000..aba705e --- /dev/null +++ b/Lib/Relay.py @@ -0,0 +1,30 @@ +#Control a relay board. + +from pyb import Pin + +class Relay(object): + """Control a relay board with an output pin. Set on to True to drive the relay pin low + which turns the relay on.""" + + def __init__( self, pin ) : + """Pin may be a pin name or pyb.Pin object set for output.""" + + if type(pin) == str: + self._pin = Pin(pin, Pin.OUT_PP, Pin.PULL_DOWN) + elif type(pin) == Pin: + self._pin = pin + else: + raise Exception("pin must be pin name or pyb.Pin") + + self.on = False + + @property + def on( self ) : return self._pin.value() + + @on.setter + def on( self, value ) : + if value: + self._pin.low() + else: + self._pin.high() + diff --git a/Lib/SR04Distance.py b/Lib/SR04Distance.py new file mode 100644 index 0000000..05448bf --- /dev/null +++ b/Lib/SR04Distance.py @@ -0,0 +1,71 @@ + +from pyb import Pin, Timer, udelay + +# WARNING: Do not use PA4-X5 or PA5-X6 as the echo pin without a 1k resistor. + +class SR04Distance(object): + """ """ + + maxinches = 20 #maximum range of SR04. + + def __init__( self, tpin, epin, timer=2 ) : + """ """ + + if type(tpin) == str: + self._tpin = Pin(tpin, Pin.OUT_PP, Pin.PULL_NONE) + elif type(tpin) == Pin: + self._tpin = tpin + else: + raise Exception("trigger pin must be pin name or pyb.Pin configured for output.") + + self._tpin.low() + + if type(epin) == str: + self._epin = Pin(epin, Pin.IN, Pin.PULL_NONE) + elif type(epin) == Pin: + self._epin = epin + else: + raise Exception("echo pin must be pin name or pyb.Pin configured for input.") + + # Create a microseconds counter. + self._micros = Timer(timer, prescaler=83, period=0x3fffffff) + + def __del__( self ) : + self._micros.deinit() + + @property + def counter( self ) : return self._micros.counter() + + @counter.setter + def counter( self, value ) : self._micros.counter(value) + + @property + def centimeters( self ) : + start = 0 + end = 0 + + self.counter = 0 + + #Send 10us pulse. + self._tpin.high() + udelay(10) + self._tpin.low() + + while not self._epin.value(): + start = self.counter + + j = 0 + + # Wait 'till the pulse is gone. + while self._epin.value() and j < 1000: + j += 1 + end = self.counter + + # Calc the duration of the recieved pulse, divide the result by + # 2 (round-trip) and divide it by 29 (the speed of sound is + # 340 m/s and that is 29 us/cm). + return (end - start) / 58 + + @property + def inches( self ) : return self.centimeters * 0.3937 + diff --git a/Lib/ST7735.py b/Lib/ST7735.py index 377c59c..e894340 100644 --- a/Lib/ST7735.py +++ b/Lib/ST7735.py @@ -95,11 +95,11 @@ class TFT(object) : GRAY = TFTColor(0x80, 0x80, 0x80) @staticmethod - def color(aR, aG, aB): + def color( aR, aG, aB ) : '''Create a 565 rgb TFTColor value''' return TFTColor(aR, aG, aB) - def __init__(self, aLoc, aDC, aReset) : + def __init__( self, aLoc, aDC, aReset ) : """aLoc SPI pin location is either 1 for 'X' or 2 for 'Y'. aDC is the DC pin and aReset is the reset pin.""" self._size = ScreenSize @@ -115,7 +115,7 @@ class TFT(object) : self.colorData = bytearray(2) self.windowLocData = bytearray(4) - def size( self ): + def size( self ) : return self._size # @micropython.native @@ -448,7 +448,7 @@ class TFT(object) : self._writedata(TFTRotations[self.rotate] | rgb) @micropython.native - def _reset(self): + def _reset( self ) : '''Reset the device.''' self.dc.low() self.reset.high() @@ -458,7 +458,7 @@ class TFT(object) : self.reset.high() pyb.delay(500) - def initb(self): + def initb( self ) : '''Initialize blue tab version.''' self._size = (ScreenSize[0] + 2, ScreenSize[1] + 1) self._reset() @@ -556,7 +556,7 @@ class TFT(object) : self.cs.high() pyb.delay(500) - def initr(self): + def initr( self ) : '''Initialize a red tab version.''' self._reset() @@ -653,7 +653,7 @@ class TFT(object) : self.cs.high() @micropython.native - def initg(self): + def initg( self ) : '''Initialize a green tab version.''' self._reset() @@ -741,21 +741,21 @@ class TFT(object) : self.cs.high() -def maker(): +def maker( ) : t = TFT(1, "X1", "X2") print("Initializing") t.initr() t.fill(0) return t -def makeb( ): +def makeb( ) : t = TFT(1, "X1", "X2") print("Initializing") t.initb() t.fill(0) return t -def makeg( ): +def makeg( ) : t = TFT(1, "X1", "X2") print("Initializing") t.initg() diff --git a/Lib/ultrasonic.py b/Lib/ultrasonic.py deleted file mode 100644 index 8771b6b..0000000 --- a/Lib/ultrasonic.py +++ /dev/null @@ -1,82 +0,0 @@ -## -# Ultrasonic library for MicroPython's pyboard. -# Compatible with HC-SR04 and SRF04. -# -# Copyright 2014 - Sergio Conde Gómez -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -## - -import pyb - -# Pin configuration. -# WARNING: Do not use PA4-X5 or PA5-X6 as the echo pin without a 1k resistor. - -def wait( aCount ): - j = 0; - for i in range(aCount): - j += i - -class Ultrasonic: - def __init__(self, tPin, ePin): - self.triggerPin = tPin - self.echoPin = ePin - - # Init trigger pin (out) - self.trigger = pyb.Pin(self.triggerPin) - self.trigger.init(pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) - self.trigger.low() - - # Init echo pin (in) - self.echo = pyb.Pin(self.echoPin) - self.echo.init(pyb.Pin.IN, pyb.Pin.PULL_NONE) - - def distance_in_inches(self): - return (self.distance_in_cm() * 0.3937) - - def distance_in_cm(self): - start = 0 - end = 0 - - # Create a microseconds counter. - micros = pyb.Timer(2, prescaler=83, period=0x3fffffff) - micros.counter(0) - - # Send a 10us pulse. - self.trigger.high() - pyb.udelay(10) - self.trigger.low() - - # Wait 'till whe pulse starts. - while self.echo.value() == 0: - start = micros.counter() - - j = 0 - - # Wait 'till the pulse is gone. - while self.echo.value() == 1 and j < 1000: -# print("wait end") -# wait(1000) - j += 1 - end = micros.counter() - - # Deinit the microseconds counter - micros.deinit() - - # Calc the duration of the recieved pulse, divide the result by - # 2 (round-trip) and divide it by 29 (the speed of sound is - # 340 m/s and that is 29 us/cm). - dist_in_cm = ((end - start) / 2) / 29 - - return dist_in_cm diff --git a/SonarDisplay.py b/SonarDisplay.py deleted file mode 100644 index 3812be0..0000000 --- a/SonarDisplay.py +++ /dev/null @@ -1,144 +0,0 @@ -#Display distance reported by the HC-SR04 on the ST7735 LCD. - -import ultrasonic -import pyb -import terminalfont - -ZeroPoint = (0, 0) -SONAR_DELAY = 100 -MAX_RANGE = 25.0 -FONT_HEIGHT = terminalfont.terminalfont["Height"] -NUM_DISTANCES = 4 #The number of distances to use for throwing away anomalies -THRESHOLD = 1.0 - -#100-15 = blue -#15-10 = green -#10-5 = yellow -#5-0 = red - -COLORS = [(0, 255, 0, 0), - (5, 255, 255, 0), - (10, 0, 255, 0), - (15, 0, 255, 255), - (20, 0, 0, 255) - ] - -def round( aValue ) : - '''Round float value to 2 decimal places''' - return (aValue - (aValue % 0.01)) - -def getrgb( aDisplay, aDistance ) : - '''Get an interpolated color based on distance. - Uses the COLORS list.''' - clr = aDisplay.NAVY - - def interp(l, v0, v1): - return int(v0 * (1.0 - l) + (v1 * l)) - - for i in range(1, len(COLORS)) : - c = COLORS[i] - if c[0] >= aDistance: - rng0, r0, g0, b0 = COLORS[i - 1] - rng1, r1, g1, b1 = c - #interpolate between rng0 and rng1 - l = (aDistance - rng0) / float(rng1 - rng0) - r = interp(l, r0, r1) - g = interp(l, g0, g1) - b = interp(l, b0, b1) - clr = aDisplay.color(r,g,b) - break - - return clr - -class RangePoint(object): - """Display a point on the screen""" - - def __init__(self, aSize): - self.size = (50, aSize) - self.pos = (-1, 0) - self.prevdistance = -1 - - def update( self, aDisplay, aDistance, aTime ) : - if (self.prevdistance != aDistance): - self._draw(aDisplay, 0) - clr = getrgb(aDistance) - y = min(1.0, aDistance / MAX_RANGE) - self.pos = (int((aDisplay.size[0] / 2) - (self.size[0] / 2)), int(y * aDisplay.size[1] - self.size[1])) - self._draw(aDisplay, clr) - self.prevdistance = aDistance - - def _draw( self, aDisplay, aColor ) : - if self.pos[0] >= 0: - aDisplay.fillrect(self.pos, self.size, aColor) - -def wrap( aVal, aMax ) : - return aVal if aVal < aMax else 0 - -class SonarDisplay(object): - """Display HC-SR04 distance on ST7735 LCD with text and a box""" - def __init__( self, aDisplay, aTrigger, aEcho ): - self.display = aDisplay - self.triggerpin = aTrigger - self.echopin = aEcho - self.rangepoint = RangePoint(4) - self.curdistance = 0.0 - self.distances = [0.0] * NUM_DISTANCES - self.distindex = 0 - self.hc = ultrasonic.Ultrasonic(self.triggerpin, self.echopin) - - def printdistance( self, aDistance ) : - s = "I:" + str(round(aDistance)) - self.display.fillrect(ZeroPoint, (self.display.size[0], FONT_HEIGHT), 0) - self.display.text(ZeroPoint, s, CYAN, terminalfont.terminalfont) - - def _getdistance( self ) : - '''Throw away changes that are not averaged. This introduces - a slight delay in update but gets rid of most bad distances''' - - d = self.hc.distance_in_inches() - good = 0 - for c in self.distances : - if abs(c - d) < THRESHOLD: - good += 1 - if good > 2: - self.curdistance = d - break - - self.distances[self.distindex] = d - self.distindex = wrap(self.distindex + 1, NUM_DISTANCES) - return self.curdistance - - def run( self ): - self.display.fill(0) - sw = pyb.Switch() - lasttime = pyb.millis() - while sw() == False : - pyb.delay(SONAR_DELAY) - distance = self._getdistance() - - thistime = pyb.millis() - t = thistime - lasttime - self.printdistance(distance) - self.rangepoint.update(self.display, distance, t / 1000.0) - lasttime = thistime - -# sensor1_trigPin = pyb.Pin.board.X8 -# sensor1_echoPin = pyb.Pin.board.X7 - -# sensor1 = ultrasonic.Ultrasonic(sensor1_trigPin, sensor1_echoPin) - -# switch = pyb.Switch() - -# # function that prints each sensor's distance -# def print_sensor_values(): -# # get sensor1's distance in cm -# distance1 = sensor1.distance_in_inches() - -# print("Sensor1", distance1, "inches") - -# # prints values every second -# while True: -# print("Sensing") -# print_sensor_values() -# # ultrasonic.wait(10000) -# pyb.delay(100) diff --git a/TreatThrower.py b/TreatThrower.py index f517811..cbce35c 100644 --- a/TreatThrower.py +++ b/TreatThrower.py @@ -12,7 +12,7 @@ class TreatThrower(object): servoTime = 1450 ledNum = 3 - def __init__(self, sensor, servonum = 3): + def __init__( self, sensor, servonum = 3 ) : self._sensor = sensor self._servo = pyb.Servo(servonum) mn, mx, _, a, s = self._servo.calibration() diff --git a/_SYNCAPP/metadata.xml b/_SYNCAPP/metadata.xml index 19bb3c19835e2ad567fbe9dc916d7739a3eb1d50..a0fe76f8ac20ceca712c5edc05b89d1d1ef474a4 100644 GIT binary patch literal 20928 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 XMvxs&m6=1X;`h((fgKO3hyMQp#^*y$ literal 18078 zcmeHv_g5-ev?fsnL{KCu0ulttf&@W81qA`gIVUAaR1iV<*Jo2zecPUXZ|2R+`*BvS z+xA))wd&M4dw*%)LSiv!+>J)!QLWKyHO1L*NIajJ+od3-p^Ja&dg90PaEUwGioGpEoa7SH*SMR0oE&JN5l@8!jj-cb@ zeee6J6g`qPqY@Dghm`ortANSEd5`RW#m1qV;Lvff%NezVNIVu z)GCbwo$o7eZOc98lrbvv?fbjWPP~+R!M&(unisDzw&LirlRqtsa>HUr%`f(}y~|#F zRH>C5C!_F?P!hB$i^IF?rv^a%on^6h~3MXS>r zmxEAWa6vW_Jd40OKz9%v3(u!<$2ShFf^^%;H-DOVKSJx`G(6^7i?s*c*c`RSC7*(% zqjD3Vg$6JBBLmq=G~lmEf9^7=c(PUmYyOI%W$6PQf?KE>>ds31(rM{kC%Qb-_2OM+ zUwNh$Di0+`l4`d9GGJUWrEJO3g-FYHoP7D-WJ}wI-aq}gNg0zf{|Db5ABM#nP0Ep_ z4TOW;_D+PRqHOM~$)iL=+!WWu%tSrYNO1Jr)M2WTuBQ6MVNobQG@YKGePvAtn!B@t zJA3;g_;e%La5`__9~TZr7rPEfIMX5D$!&r#8oxw<2IUmq_7HoBW;$D8oiTq}HHFLRubcnn$L;%vEh zd8~XHkZl6pd#WB|iyQH(f~9CF4kb-7OLlnvmZ>M+;;eWt(Am4@%8nFlDeL^$!hy}C znq5zDwZnxq>&(F~KQ>qD9_>VuwWM|$%m%eZyN4|{iM;0o=WInib1dh^nz5tUYnqj5 zCSFraFw2MwM4JbD=cz1yYg52 z_iaR1woJJr_N({%QH<-QKdjRSoOj}#879w@N1Scp*n8f8S^LLk^4;VfuILC3^6j31 zd`&WuFLs-`rR`1DI2_^Hih$PNO}5GMmubw|Fa$2V+zEbi@*zlYui1_A%DoKkg}y^; z+xPwV%bsCu!rhY|XP$+V9{7HSD?1N-=A8sr_6uI5jT%_oe%+DpW)FDdhjA}>aJzBt z`@imRccg3l!zez154kC~2R4e0s@&V&cw5~++c4f#kLORV!W;GSZdEq*-H*y3@8{s> zc*!pEZUp_<^y$;GbHD5Qc2S(`7uw6*Jif<0Os%`FjcfV5b~3aCC+_u8%e<=Ke3D0L zl+=?y>w>oLTamlmxchjVc;+wPFZ~dly{TrX%sYb8S3(OnjSUmm?Daz}^;Q;XPcM7Q zZi1`pCpijck{x@EAEz4UCz^p?VB;;kuc=0+8Lvj$&s~a{lcEL=R>F63Q09^E+R1Id z^voGMD9N5x-=e-~#-F>svtI|MLpfJBEcNpzS%LJed@6fQS7j{;=e#M04OKj=bp3C+ z7E$W|%-poZUg_g??ZBfC>Pn*QciyohJCt(eN0)pxMfE&SaRSt^yR3U8zTsLNGu09u zohV3#PXoa_fBAAQg3IuMZU1`5{q3Zii9V8Penq>2xBXgJJg@xA@cISr2)^TqS2!Io zHr$*gZe^>v-+4I652q1J*3qV~=}lTo15bD=AzCL^^1~>wajZkDXNsG4g}41Us3y`q z(LB1|zb1$oMtUy@4I@tXYv7hCugWKQfVtu53Tp2nxCwtl9cX&t>@8();<)KSJ)pkt zEpu;$Tj4C&DV-Ji*u5^tF|Uu(y~ z&yyeV)1x}otBzdmS*UMMnnP0=A6NSnpUehx!TB*wRyg`r)^bJ3Du%fr#Q^8B-|ud_pUtA zigfR#Zf=+#BvEd9!pXEUU zyuT}kmai4)&+`3(NGa3^v+ug0wxfX+)${Vh3_Hn*PD+i6oezZ$hjy`o8PLP=o4oF+ z!uqKBT9NWp&N3dtCv@Yh&)rwf+EKBUbo*Ba$idmWS^Ag3MTBCfM7~0sTguIWpYEo( zR^G|;^lAiy;n>_sc5y<@rd@*h*@ElRB#6$NZuV8hJ0(Vd!Bwhw4F|ujCa5m%Ri#$&|e%-ztN0 zuhcE`bi-mZ^O-VJ)I6O)B~l%#Xlgj;$HBeGZBgT;w(!lzFRzLMzlRw)ZPD6}vbJo^ zS~d>8Ju-f7P!21ZTn?5i_b3goJr{56G2yR=3p>_nPfUOiK_HX&CByn|r*`y7rO3vle+rE*tmP}}^9cWpG zoF#jhnrHqiKiR(PDE)BMl%``XfLnG)&&KcocPJc-`~;Ta@8F`aE!VKB6;y5iR|20JrZ2qx{aWdky1JoSkcIb}u1k*+ z^~7Q36Huh_5WwzUhBYykY&mQFDDO^4*Togp%1$3Cn6lUSTe7bdXoebgnkDAQ*y-0; zJy`=}!Mzs9cvg68EBHE&K`-}n6IWy57k_>p7i?G!*xB1EAAgND6-V$y?R;O~PjM2} zbTh?K9G)Lb*yrdy%mFVr|Np)G|8@C4+%H&}Y5*cFADb6njsdPC zf?f}8jVv!!@23yTz>jzKFbDJn_zZ8}zuq_FY=WCy^O2nA#|-xh_;WG1-0c6NEr%s@ z=BlWF=D+WM(b3a&j?*7%Jyn|Jc%krV{G|7T9(l$p3-oj?P*PEm9oyI8-NeC*oOhGi zdzA;A5l0zq0`t_j_ltcENKi8shTT!~-Fce*eYVUkfJ8|SC5OOx|0W*amWnfH^-4N> zOI8SlpAWT{QRtEi#Dx_S^{8v7@9+yM*7OJv6<-Dv$3(u~zh!9YgT(miUAJVfs85RY z$McM7fQtVLwc`yME>Ou6tx$I=XP)E459^}HL(I4m7TeT*AA^1K>6g-2hpB&_UfxT99OhA?a89zg%Y z7iv%IRfb*P?B$vBw43>aM!0xdVuW22CiV$wS=bRbRrb+WB->GO)uei{x}$)#9{E0i z759Rd-0K|!&O|ojpr@qT&y+O#PU#aTr1zQywYQ&zcSF}cE_3Fd{$;O5Z=VG1>FcPp z@>7DQPc{;*>>H9T{0{iLhb93mkmX&wnkmx-!+-?D`ZRduKkbdahwPY(<%`FR1 z<2hocl%wa!b`&6U@E^%B8#GH(+}RN=Z?U@H2igwI2ORN(0TAHVGpphY-+ z6gR_3aqq$}fn7i-ojprm)LCWVVuG*kCdQHJjsj|oyDIY$yFimYS0z_L3CCkAJ-K#d zv}+2cx|H1u=8E4{Jz6R^Vy&6i$~HAr7tR)9xfw5q5wF4g+4t0+zUHH;wwAmih?o#>~mP!LRc#L@l~K(lA>-5XXwdCqV8dAgFtoluJTi-yY<8PZCG|E-@S|Ea(>rG|Km*ZF79 zn*EPJFFz~~b8zN?+MgfGxDpU5ui3z6FF!4H+ADE{t1W7~G=iP=}|Oct=z1*hqh1?}rfhp)BZ#TNC3LC1Jn|03`Cvju|Gjji6%> z%AU?+XnmgFO}(Gtp=W(>%_BYCd1hpoPN5Z@xu6xbOZ`$0Zp=W} zpI!>KBp%dHIA>>fgtQx&h&kmL2v;YO_ry?e0~<`bvgJfGIM7Ai9UN{{!amV5#AoN3 zIY=o3?r}AcN`8;@Mi|rejIG%8R%ypN!DjxIWda_1`Du(E z^`JUG%TvxA$XdXITJd`RpP%4tSTpjS4iu^Fwnqffea+K%_PVQ!;0L&*B4UpkO9$vC zlS_w7{YnG1FbzAOxg z0dAvR?V(JDmnGvB;+CO-4S{uZw_3iCc5$L>+w7F z>npZn@2{q!>D3){=ZxhZP9X@y&2&B5DK!2F(fT`OnKeO#!1KSPX#OL+PpB!2_9S<6 z*56`xK%_>}EyYDQ1C)Bpe>s{LwY?_Dc3Dk8Ha{DNrxC0-Dtd%5p+^Q@0pfuD1SHPF zlBs3XZ(m$rhd;a-6q{QisK)lF=}x!aox&-$aaC;0tr%G3yUWuB{y{e#`QzJp@|nEl(oifkUYN|ox^@_E^D#vNgB24Go{<$Rh3 zC&t}}xo7OByBOh5-^ho199`ippN3rnD5?@XI494`@X9y!{4qt?E9R%7qRHMyiQN;x zxEQ#Pi)Q4RXpDA0q$Xl*;O2S1Md=e72k2?Lmqv8JAKJXy1278=`qH+0a?O25NkOvy zuVu(cGxCtGpa;`rTm2BufdMWrp|@M-Y>wuRysCsX-%aHEe&ioN&V8Tgm4V%K>IBPA z{!Lk2CT`389`*aEY=p)F1!BicGQ+C=(eM(^itAY#I)5idRn_~I2T{WeKj+XKj(?6; z*JD%KfNoa*zH{TCJ17zQHQl~>+VRe>r$)T>Ao%T~-@>&$S4cyhxT~hLJPl6e+%!3^ zqNTDfX#xd%t-Q)=(&Olxlm&FR2E?qF6RP-HHUety>i};GoB)*Z4QIhE3({6Elzn)V zRfKLPnMj^lWM&4BFSzF<8F#?-+qkJAX@t z&JeZ1=#pA{C$^sZBZfN=7@4@in!zfJc#_zwLAud&cZzRQH#BsC!IAmf$EYeI zV9^#V3DKIq0Go2#6AQiaYi3Y+FAmD+wS`SsJ3f!&zc{Ft{3&QYzQO_2iu5AAQ2q-_ zK;tf)srX8vjuU;29;e?juc^Mis}rf-)kg_#sxA|{MPSANb?L_IP`%nf_A`4ydUNmH z^#oYoe=;G0=#Fu{=iRNZ**Vbc1K>vB(4U(*{yF>*EU`-P&HfT}o~$qZho3vmJ`^DK zv#d^gBgAa?Ac=!j@#7!eHA0gYDG^Gmz^8sEwdo;S$d$SFm{AtGi_kl)Fjn}Z(*?ar z>5K2K9?@nEV5=UlB^#+`61yHdnH~nBd2PqNCbIW^(j!L1z+-^Sa}^?mNX`0V>pZHS z)fG$5`o-}%KcOx;Pv_5VdD{vwAK%{Xh?|NBIc9C}B-MB$b(~>q+A)^8lN+kLIzCw9 zr=@1%FpAJQc#7+x`L@<#(QOfc95@Uz$(oQiv8TMf+EsS zFwI?nOpzzZgQHV~CoO`tnjdNhC0>rFYO7Awo%|qsat@pgj1sU(`q_379Bq)TV+Ubc z{h()qm6g5-Kgbt+Ifnd0`1VN~KSb+RVqC5kF(Fo#JX&%mr$0<|f!@j0{pySBsRg2y zoD6!*>4UaM7|^oDAh@Z(gaOQ=6cJbmVga0ay6i3lx#ap6V;p@73o(j|rkk=Jl2@_OlkYB*Lm#z>({A}`= zW6t5umQ>ydU*9w_BXl zB+m$#SS4lxF*?(oX(3RcJW~mg0*h%lS>=+@XZUXl>IX&@o(!-`rj=>|;2nlRJBomY zgnUxj(F&Elvgq;%JdXyLuhl49%u|XA{X$zO)B=$Kr6yBLy~eAtKmOnrmYJUIVWp3= zIrw`Wo~TZ70BBJWDEmf^mtDFMq zhqaDK2Drp!1As6hN&=FdaGTuq;l?sCq)d&w8uxO~xp^8r>|TvM6EogCHxo@WfEz>u zK##jm4<6M(5aDMBvKHsDbC3XtPdn9P^bK<4M`Et@q%2f*i=F%sY(rl8Ey|Hpr6-zq zWvB8J??(~GPYiw`X-)#zDX(cmZj1+*XOWE``n8!JnLX?prcayaAIHJ-^lt2(+@s*O z4Mvm{D5C@qRMJZJ&P)^Vvhkj;AoXS6PH}=x>|eR}zzJMGqbSn4W5CC)fkzi2!oU@9|wv1vA#q=@`Nr0Jmb6iuzt2l%J??e9N9Ww*ZP*b;r2tqq*hY;?+iOUQmwp6L|;NqNRiQ zbR3j7z)YT>O*wgo-wA{nBn$1Cj;9prq3ww|3XX&&MY#P>3Ct$%Nt+kED!RX@y_2as zie)|0!}M?qvD5A35;W;W4(!)nyeh5d`;{}Dup(5SsKE8dhd6E)Blp(yoA zgKR(5hZF23h#St0J?hL+r?`*9mHzRU&%ZY1Re4kChGzpn{~H-{Uc~{qM;C!0##VE> z69+CR3*0%~#Hil`rY(@V8lNg8n(dCX~NP-(pR`8H3Vb%k6bcjjsiYPVw*sB+{?LH8UqH-n@Wg4R8VcXp$K+509Hi z((PVo2^tsPGVUkYr2jM7SghBs!2_`T|JzaZL0)i7pHQMr!J0uVO+sD8mbjOx6$EFF;TxX)6 z%IIZ+DY=L_P^V!V{a;)3-g0lqIbLqiZ-wGwIJl~8K;C(^XAZxP$K*b~sSMn_ph<>G zzu3(;Vx3qkQ;XHo)i@%BV=ll6^lE*Gw&1mKA!%t-feh2J0(QFW6!9aFRb5G|pzC)| z*36PKblOJ4G>O+tH8X^g{|@UOr@x$ho^~%DEytG}jVDfe?csP{tcshU^p z#F(i=*>SWD849{KG=?8B%y#pgKYY}g0#9p39v?3*AFi0XBdmcnbWi3dod*MZ+hxLn zTKMsK;W~ZTAWQ^lJ8b)He$c(+-npV8W#b4jaDYAk1V$FTeRD&xFCo1|kNkx8LSKIR z%lc_{M{*v(S=w3%_ut`7_y?d8{Ftwc=6Ueq{;+*Aq`sL;Thkpm$sW;&9SmifHKcsr`(f(>}?6fqDl_2~Wb)fGLM1PLMShm3oD`gb&#kVPQ}dR^BV`45><+t){xr^}E@4q68eD%BTWXmof?Q(p1ADiW@YgMLVawFY2rup?cLWc~u;=`C9yq^W)*vx~>^AkyBdg%* zx$OtDYpz5Lx3jn7D!J{5OaHbryB9CFFBbt6!G3NzHm33uCgzecc>_!hkx|?3Nm28g z3!#WnnuFg4P9(TeNLB|E!f#gW#c4@Z(m%>#a`0!jb`^ru9$x{AZ^VclclycuF-D~s zRC#**^iA&}j^~>{OwlX0_N*`wl!3%ek-$dQ3OE8B8=P^^NHLOs*<*lXcAiE)2=rMA zMqtlAylL0{6Ir}aod=Qm%a4#XaC6|KLl_1?`%VsNAVW^;@Pq#>jsIQhPSWm> zJ8eUG+V74uCHgcTdGPWHRSx-4f~JT_o>*=rLaFX|ZW7oIXI~s%kRgtR6)c2>T$V4h z#)Kk{p4HlK(qZ3lzC}I4kotI$6r*yBk#I#a6Iq9r@UTj@^+Vm31(ku@q$4HxxF~b4Y1Z-@n8*|L?L@`UQ7{#9@t zq1{qn7NMUI#kp~>M-4HMPCvUXAg~qxvIOz8`<{%bM_Qia!Is3WDVw z1k3|JKBPsLz`;bC?ep?}?BBnk7;z@zJ>ED9EF$wq^4;`d_DHwXI}q4I3;) z-VKC5ZNQ@uCY0+#H1b$hjW#4LX%#-w)||>X$%dkt1eB)x>pQgqHSZF5J8((;$n<$S zVn4b=wvwSi-5OpPXS&*OTAa-xTGtU1Br=3`ovHGdeR5~NP8`6q0iR`c`5We%Dog(U z%oDy%s$CT%sQ$>69RKSt#Yu78k<(;RF5b&6lif5n3V_^EZLft3Y0H-=mv`*hfQ8r@ z&OIA;ZB2mM9n_}m?guFY5^0k&-yKRrAaj9fvJ5N&U;a&a7y-F*r0;%WJW=s;xa$c9Ew<3_C^7 z3?>nwN9s!7)X#R5vE#2``@Ae}X;-uWKvT=--6FIEu=@ElzyI)}y>|ctwj4At#M?Gb zo)27<5FFy*(oT2Lx6t&74EvN0Zt3C{(sw|I40!v2n|8CWI{<&pI6!q2NnG)u`#Ekw zR^mT83BSpd*E~6yAFYHVIFgW^p$>&0WOBYwr^5fG9=Cb=_$_NAZh^<_xhmL0Hnh#r zewzb{txk?_ldGDr>lw8_3+*f%{$exQ#Pk`&{lmN)CI|$wC)|Y9pgk$mA4$l;ia?(c z>bXb<*amWuPqM{q>5+mZeTCq5x7^eA^SuN!)wnp0vgAj}qrZSwr_!r*0QrKYQ+2Pl z9t>qfoF8p8J~KyojBT`*^6u(RiPg|$4(2m3UbH)xl}u4?i}V51{;u%=lGC0YXUCbr zNC(OP7#W}eE9yHyb5rdU1L2x%okjLto7?3r$v3r(9;SB-#}9N#FbO9QOAs@^0JA~T zYOF&r9ItltN*vrKe}&Zh5Q_Ca}0e-a`WVKJPC)MGu1T z5v1SJ>@cFV6>j0YF2juHP3Y(4>y`nK5okH_ih9d%{2hwU8H)BUt9m>>jVLl;NnptA zEO8UKN!+5U1_BTo4*)yzx^i5^IB|-&8 zIaACr1L6Kig3=H^LiYU0@T7yQa~r5W8?Ac)NMla?-pfT$mp7Fib3kwC9YDvkFkCRL z;M%dE*EZLKQPAg`4s$KcD+BhJa00al^hTJfnm>Jp|JgrX8#@dH8Hr}RkzgJ_O=0(t z;T_^_i}2cUu$76nBE-kj*f)Lpvsai4HWYGI$(Gs~o&lQ)_Ta@%v=Ks&*CX`Nz7(4l z>t)Ou)FMM}$i&Sz8-O0Ds^_dr*5#Sv%=ugD)^@U|m#3=t9OMNGJneCeo2bUPab~g} zYn+4E^w5Bao{py$st#fCjzNV~e^|%7kHdSAigM<+gAGLJv4o+nL0wQAbks9OvN4$c z6mkwZUnfOf#GN_$fB_l^6g^CWli=nF$vjp5%2hiESGYTZY2us0@Xns$<7)03Jwc1G zObs8FT_dDkK-LJaywm%U_rJ@2O5rW|t6f!2On?zK#7bMr5I@qL)&Rx@(}O8v$q+XP zv%e9Air43LiM8V@y7GzNf)rv^<_sm<2mAF12r<+&q5)^wf$6!e_JUqp(upzBMyb@#QE9HX1NpE%gbeC#VC2tMsxfg}4gF%qwdo~eiA`>--x-=Pn~ z?)|q7Z-IX!}GVmt?xgl(mQBI0HKg+${SeCceoGS;<|zfrQw<34$hFOlVyq{AKHhsJ-y33*0J#eg7dB!ERG7+p z3bBrSe|_@u)m|w<-OApZA+zCZw(XD!E5}gP}lx?4;gyZYrnx7e$RIikMzf zK;wDX4@TBDDvO>6RYfJ_H?{#=Bvyl+Y9s=*P3k&Jx;%_RK!iC6<^Bb{SRf-`=#_^m z`KSL~lnv-CV!{b}KFl)E>=gT+je$sH!oTJVryc?*={P$KOSm?V(2&g&Gd=X}UOa#M zM^_&xko*a5LSKO9Yj@Do*OIy{Eb(#&!p=UR+wJtyrUKH;BY$bBXeyR0n#e@n0hZX| zcWxfEFO;RAOL&n0y#;*1ITvX8FipeIgZRaa?0fVfZO%|Ju0bBzPQf5ZUOF&BUTk-O zA1dl%pA=0)XgZp+Y%dNoC7HL_k&LMlNMFT=N>L7`qhL;o?*QEmxuI9}$Hl$zUhTbX zT)g5w(6%}A{x}cZ6hB}~5cKEQWYgR-@r|uB58lQI?;~IgDc+B?BqZI}Az}J3;c}G78 z&qzk4*t78lMfYlPtA;T-Y z$3N-rFFv3o4#DQ%4r`HQ5X-); z+Gi2D6M@mWolgX@hc$Pojtk^%b?_{WibLAuH}yAr;8!epcb~jL-5>DT|Miz^>UzJm zJJ~@4E7!Yns(B9!JtJJjopzLuz_P-MVfr;%O}v6yQ^&4fL;t!m{X?*Dg|wyX1qU=3 z=q%urgI6vWx!Yws4wf$sb@xw{W@y^Fw)op!SaiO*dx?rLPj`DS2i zOsu4*8DzO3{j{9{+(c%O@`ir+bB^R8j^EF^jJh$qrCV59|LeM_FJ2c=q51Xy=A2jh zDXzF)=*5`{?)jU%CT>J)Sz&>H*@?lBFxmo{iX#bI!TB>lv(!-A%BaEjz*O$QJB}a6y9vl|m3#5GOiRpH4NHBkK-<@YP@+PoBBV#4CBp2h z37bRRCM|v2$t+Wqvi&zX{v&1gcjhps8t!x%H5?XL3|~I{=dqRlO=#urA7fyJtFj}a z2&jqG3$~qW}XYq1!^Gz7qF(9fo{^kJzXbdA>0t22G$I$$1d1x$fofC zus}X3VM^fN)sWSbprXQ0VWnZ!E|?eaZ*Isr5Ooux0k>wf5rsi)|9?-w?V0gHeIjUJ z*x~P~+<&?oqW^bqW7oasro;F@ertJKkue>NqI^RL<3-!$EhshZJ?d(K?D#bI{UDfW U>xU^Ixc}#?>q+%YThgxo2N`y