From e2842f17545b9a9db9eb6716088ab613775a04b1 Mon Sep 17 00:00:00 2001 From: GuyCarver Date: Sat, 22 Nov 2014 11:33:41 -0500 Subject: [PATCH] updating stuff ST7735 no longer a directory as font file import from directory failed. --- .gitignore | 1 + Lib/L298N.py | 44 +++++ Lib/PIR.py | 45 +++-- Lib/PWM.py | 81 +++++++++ Lib/{ST7735/__init__.py => ST7735.py} | 253 ++++++++++++++------------ TreatThrower.py | 69 +++---- _SYNCAPP/metadata.xml | Bin 16214 -> 18078 bytes main.py | 24 ++- motion.py | 65 +++++++ tft.py | 234 ++++++++++++++++++++++++ 10 files changed, 644 insertions(+), 172 deletions(-) create mode 100644 Lib/L298N.py create mode 100644 Lib/PWM.py rename Lib/{ST7735/__init__.py => ST7735.py} (78%) create mode 100644 motion.py create mode 100644 tft.py diff --git a/.gitignore b/.gitignore index bc9ceee..91e17ea 100644 --- a/.gitignore +++ b/.gitignore @@ -221,3 +221,4 @@ pybcdc.inf _SYNCAPP/ _SYNCAPP/metadata.xml +_SYNCAPP/metadata.xml diff --git a/Lib/L298N.py b/Lib/L298N.py new file mode 100644 index 0000000..d5e996a --- /dev/null +++ b/Lib/L298N.py @@ -0,0 +1,44 @@ +#Driver for the L298N Dual HBridge motor controller. + +from PWM import PWM +from pyb import Pin + +#motordata +#Forward pin +#Back pin +#Speed pin (PWM) + +class Motor( ): + """docstring for Motor""" + + def __init__(self, forward, backward, speed): + """Speed = (pin name, timer#)""" + 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 + + @speed.setter + def speed(self, value): + self._speed = value + if (value == 0): + self._forward.low() + self._backward.low() + elif (value < 0): + self._forward.low() + self._backward.high() + else: + self._forward.high() + self._backward.low() + + self._speedControl.pulse_width_percent = abs(value) + + def brake( self ) : + """ """ + self._forward.high() + self._backward.high() + self._speedControl.pulse_width_percent(1.0) + diff --git a/Lib/PIR.py b/Lib/PIR.py index 9dd11c5..df00209 100644 --- a/Lib/PIR.py +++ b/Lib/PIR.py @@ -1,4 +1,4 @@ -#Passive infrared motion sensor. +#Passive infrared motipower sensor. import pyb @@ -10,28 +10,35 @@ class PIR(object): """Power and trigger pins, optional interrupt callback in the format of fun( bOnOff ). This will be called whenever the trigger state changes.""" - self._power = pyb.Pin(power, pyb.Pin.OUT_PP) - self._power.low() + if power != None: + self._power = pyb.Pin(power, pyb.Pin.OUT_PP) + self._power.low() + else: + self._power = None self._trigger = pyb.Pin(trigger, pyb.Pin.IN, pyb.Pin.PULL_DOWN) self.interrupt = callback - def _on( self, aTF ) : + def _onoff( self, aTF ) : """Turn device on/off""" - if (aTF): - oldon = self.inton - self.inton = False #Make sure interrupt is off while turning on power to avoid false callbacks. - self._power.high() - if (oldon): - pyb.delay(200) #Need to wait a bit after turning on to make sure we don't get false values. - self.inton = oldon - else: - self._power.low() + if (self._power != None): + if (aTF): + oldon = self.inton + self.inton = False #Make sure interrupt is off while turning on power to avoid false callbacks. + self._power.high() + if (oldon): + pyb.delay(200) #Need to wait a bit after turning on to make sure we don't get false values. + self.inton = oldon + else: + self._power.low() @property - def on(self): return self._power.value() + def power(self): return True if (self._power == None) else self._power.value() - @on.setter - def on(self, value): self._on(value) + @power.setter + 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() @@ -43,13 +50,12 @@ class PIR(object): def interrupt(self, func): self._interrupt = None; self._func = func - if (aFunc != None): + 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, aLine): + def _inthandler(self, line): '''Function to handle interrupts and pass on to callback with on/off trigger state.''' - call if (self._func != None): self._func(self.trigger) @@ -64,4 +70,3 @@ class PIR(object): self._interrupt.enable() else: self._interrupt.disable() - diff --git a/Lib/PWM.py b/Lib/PWM.py new file mode 100644 index 0000000..c008108 --- /dev/null +++ b/Lib/PWM.py @@ -0,0 +1,81 @@ +# PWM driver. + +from pyb import Timer, Pin + +class PWM(object): + """docstring for PWMM""" + + #Dict pin name: timer #, channel #. + PinChannels = { + 'X1': [(2,1), (5,1)], + 'X2': [(2,2), (5,2)], + 'X3': [(2,3), (5,3), (9,1)], + 'X4': [(2,4), (5,4), (9,2)], + 'X6': [(2,1), (8,1)], + 'X7': [(13, 1)], + 'X8': [(1,1), (8,1), (14,1)], + 'X9': [(4,1)], + 'X10': [(4,2)], + 'Y1': [(8,1)], + 'Y2': [(8,2)], + 'Y3': [(4,3), (10,1)], + 'Y4': [(4,4), (11,1)], + 'Y6': [(1,1)], + 'Y7': [(1,2), (8,2), (12,1)], + 'Y8': [(1,3), (8,3), (12,2)], + 'Y9': [(2,3)], + 'Y10': [(2,4)], + 'Y11': [(1,2), (8,2)], + 'Y12': [(1,3), (8,3)] + } + + class PWMException(Exception): + def __init__(self, msg): + self.msg = msg + + @staticmethod + def timerandchannel( pinname, timernum ): + try: + a = PWM.PinChannels[pinname] + if timernum <= 0: + return a[0] + else: + for v in a: + if v[0] == timernum: + return v + except Exception as e : + raise PWM.PWMException("Pin {} cannot be used for PWM".format(pinname)) + + raise PWM.PWMException("Pin {} cannot use timer {}".format(pinname, timernum)) + + def __init__( self, p, timernum, afreq = 100 ): + isname = type(p) == str + pinname = p if isname else p.name() + timernum, channel = PWM.timerandchannel(pinname, timernum) + if isname: + p = Pin(pinname) + + self._timer = Timer(timernum, freq = afreq) + self._channel = self._timer.channel(channel, Timer.PWM, pin = p) + + @property + def pulse_width(self): return self._channel.pulse_width() + + @pulse_width.setter + def pulse_width(self, value): self._channel.pulse_width(value) + + @property + 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) + + @property + def freq(self): return self._timer.freq() + + @freq.setter + def freq(self, value): self._timer.freq(value) + + def callback(self, value): + self._channel.callback(value) + diff --git a/Lib/ST7735/__init__.py b/Lib/ST7735.py similarity index 78% rename from Lib/ST7735/__init__.py rename to Lib/ST7735.py index c6f535b..377c59c 100644 --- a/Lib/ST7735/__init__.py +++ b/Lib/ST7735.py @@ -4,51 +4,6 @@ import pyb from math import sqrt -ST_NOP = 0x0 -ST_SWRESET = 0x01 -ST_RDDID = 0x04 -ST_RDDST = 0x09 - -ST_SLPIN = 0x10 -ST_SLPOUT = 0x11 -ST_PTLON = 0x12 -ST_NORON = 0x13 - -ST_INVOFF = 0x20 -ST_INVON = 0x21 -ST_DISPOFF = 0x28 -ST_DISPON = 0x29 -ST_CASET = 0x2A -ST_RASET = 0x2B -ST_RAMWR = 0x2C -ST_RAMRD = 0x2E - -ST_COLMOD = 0x3A -ST_MADCTL = 0x36 - -ST_FRMCTR1 = 0xB1 -ST_FRMCTR2 = 0xB2 -ST_FRMCTR3 = 0xB3 -ST_INVCTR = 0xB4 -ST_DISSET5 = 0xB6 - -ST_PWCTR1 = 0xC0 -ST_PWCTR2 = 0xC1 -ST_PWCTR3 = 0xC2 -ST_PWCTR4 = 0xC3 -ST_PWCTR5 = 0xC4 -ST_VMCTR1 = 0xC5 - -ST_RDID1 = 0xDA -ST_RDID2 = 0xDB -ST_RDID3 = 0xDC -ST_RDID4 = 0xDD - -ST_PWCTR6 = 0xFC - -ST_GMCTRP1 = 0xE0 -ST_GMCTRN1 = 0xE1 - #TFTRotations and TFTRGB are bits to set # on MADCTL to control display rotation/color layout #Looking at display with pins on top. @@ -66,9 +21,11 @@ TFTRotations = [0x00, 0x60, 0xC0, 0xA0] TFTBGR = 0x08 #When set color is bgr else rgb. TFTRGB = 0x00 +@micropython.native def clamp( aValue, aMin, aMax ) : return max(aMin, min(aMax, aValue)) +@micropython.native def TFTColor( aR, aG, aB ) : '''Create a 16 bit rgb value from the given R,G,B from 0-255. This assumes rgb 565 layout and will be incorrect for bgr.''' @@ -79,6 +36,51 @@ ScreenSize = (128, 160) class TFT(object) : """Sainsmart TFT 7735 display driver.""" + NOP = 0x0 + SWRESET = 0x01 + RDDID = 0x04 + RDDST = 0x09 + + SLPIN = 0x10 + SLPOUT = 0x11 + PTLON = 0x12 + NORON = 0x13 + + INVOFF = 0x20 + INVON = 0x21 + DISPOFF = 0x28 + DISPON = 0x29 + CASET = 0x2A + RASET = 0x2B + RAMWR = 0x2C + RAMRD = 0x2E + + COLMOD = 0x3A + MADCTL = 0x36 + + FRMCTR1 = 0xB1 + FRMCTR2 = 0xB2 + FRMCTR3 = 0xB3 + INVCTR = 0xB4 + DISSET5 = 0xB6 + + PWCTR1 = 0xC0 + PWCTR2 = 0xC1 + PWCTR3 = 0xC2 + PWCTR4 = 0xC3 + PWCTR5 = 0xC4 + VMCTR1 = 0xC5 + + RDID1 = 0xDA + RDID2 = 0xDB + RDID3 = 0xDC + RDID4 = 0xDD + + PWCTR6 = 0xFC + + GMCTRP1 = 0xE0 + GMCTRN1 = 0xE1 + BLACK = 0 RED = TFTColor(0xFF, 0x00, 0x00) MAROON = TFTColor(0x80, 0x00, 0x00) @@ -116,19 +118,23 @@ class TFT(object) : def size( self ): return self._size +# @micropython.native def on( self, aTF = True ) : '''Turn display on or off.''' - self._writecommand(ST_DISPON if aTF else ST_DISPOFF) + self._writecommand(TFT.DISPON if aTF else TFT.DISPOFF) +# @micropython.native def invertcolor( self, aBool ) : '''Invert the color data IE: Black = White.''' - self._writecommand(ST_INVON if aBool else ST_INVOFF) + self._writecommand(TFT.INVON if aBool else TFT.INVOFF) +# @micropython.native def rgb( self, aTF = True ) : '''True = rgb else bgr''' self._rgb = aTF self._setMADCTL() +# @micropython.native def rotation( self, aRot ) : '''0 - 3. Starts vertical with top toward pins and rotates 90 deg clockwise each step.''' @@ -141,12 +147,14 @@ class TFT(object) : self._size =(self._size[1], self._size[0]) self._setMADCTL() + @micropython.native def pixel( self, aPos, aColor ) : '''Draw a pixel at the given position''' if 0 <= aPos[0] < self._size[0] and 0 <= aPos[1] < self._size[1]: self._setwindowpoint(aPos) self._pushcolor(aColor) +# @micropython.native def text( self, aPos, aString, aColor, aFont, aSize = 1 ) : '''Draw a text at the given position. If the string reaches the end of the display it is wrapped to aPos[0] on the next line. aSize may be an integer @@ -173,6 +181,7 @@ class TFT(object) : py += aFont["Height"] * wh[1] + 1 px = aPos[0] +# @micropython.native def char( self, aPos, aChar, aColor, aFont, aSizes ) : '''Draw a character at the given position using the given font and color. aSizes is a tuple with x, y as integer scales indicating the @@ -211,6 +220,7 @@ class TFT(object) : c >>= 1 px += aSizes[0] +# @micropython.native def line( self, aStart, aEnd, aColor ) : '''Draws a line from aStart to aEnd in the given color. Vertical or horizontal lines are forwarded to vline and hline.''' @@ -255,6 +265,7 @@ class TFT(object) : e += dx py += iny +# @micropython.native def vline( self, aStart, aLen, aColor ) : '''Draw a vertical line from aStart for aLen. aLen may be negative.''' start = (clamp(aStart[0], 0, self._size[0]), clamp(aStart[1], 0, self._size[1])) @@ -265,6 +276,7 @@ class TFT(object) : self._setwindowloc(start, stop) self._draw(aLen, aColor) +# @micropython.native def hline( self, aStart, aLen, aColor ) : '''Draw a horizontal line from aStart for aLen. aLen may be negative.''' start = (clamp(aStart[0], 0, self._size[0]), clamp(aStart[1], 0, self._size[1])) @@ -275,6 +287,7 @@ class TFT(object) : self._setwindowloc(start, stop) self._draw(aLen, aColor) +# @micropython.native def rect( self, aStart, aSize, aColor ) : '''Draw a hollow rectangle. aStart is the smallest coordinate corner and aSize is a tuple indicating width, height.''' @@ -283,6 +296,7 @@ class TFT(object) : self.vline(aStart, aSize[1], aColor) self.vline((aStart[0] + aSize[0] - 1, aStart[1]), aSize[1], aColor) +# @micropython.native def fillrect( self, aStart, aSize, aColor ) : '''Draw a filled rectangle. aStart is the smallest coordinate corner and aSize is a tuple indicating width, height.''' @@ -302,6 +316,7 @@ class TFT(object) : numPixels = (end[0] - start[0] + 1) * (end[1] - start[1] + 1) self._draw(numPixels, aColor) +# @micropython.native def circle( self, aPos, aRadius, aColor ) : '''Draw a hollow circle with the given radius and color with aPos as center.''' self.colorData[0] = aColor >> 8 @@ -336,6 +351,7 @@ class TFT(object) : self._setwindowpoint((xyn, yxn)) self._writedata(self.colorData) +# @micropython.native def fillcircle( self, aPos, aRadius, aColor ) : '''Draw a filled circle with given radius and color with aPos as center''' rsq = aRadius * aRadius @@ -353,6 +369,7 @@ class TFT(object) : '''Fill screen with the given color.''' self.fillrect((0, 0), self._size, aColor) +# @micropython.native def _draw( self, aPixels, aColor ) : '''Send given color to the device aPixels times.''' self.colorData[0] = aColor >> 8 @@ -364,39 +381,42 @@ class TFT(object) : self.spi.send(self.colorData) self.cs.high() +# @micropython.native def _setwindowpoint( self, aPos ) : '''Set a single point for drawing a color to.''' x = int(aPos[0]) y = int(aPos[1]) - self._writecommand(ST_CASET) #Column address set. + self._writecommand(TFT.CASET) #Column address set. self.windowLocData[0] = 0x00 self.windowLocData[1] = x self.windowLocData[2] = 0x00 self.windowLocData[3] = x self._writedata(self.windowLocData) - self._writecommand(ST_RASET) #Row address set. + self._writecommand(TFT.RASET) #Row address set. self.windowLocData[1] = y self.windowLocData[3] = y self._writedata(self.windowLocData) - self._writecommand(ST_RAMWR) #Write to RAM. + self._writecommand(TFT.RAMWR) #Write to RAM. +# @micropython.native def _setwindowloc( self, aPos0, aPos1 ) : '''Set a rectangular area for drawing a color to.''' - self._writecommand(ST_CASET) #Column address set. + self._writecommand(TFT.CASET) #Column address set. self.windowLocData[0] = 0x00 self.windowLocData[1] = int(aPos0[0]) self.windowLocData[2] = 0x00 self.windowLocData[3] = int(aPos1[0]) self._writedata(self.windowLocData) - self._writecommand(ST_RASET) #Row address set. + self._writecommand(TFT.RASET) #Row address set. self.windowLocData[1] = int(aPos0[1]) self.windowLocData[3] = int(aPos1[1]) self._writedata(self.windowLocData) - self._writecommand(ST_RAMWR) #Write to RAM. + self._writecommand(TFT.RAMWR) #Write to RAM. + @micropython.native def _writecommand( self, aCommand ) : '''Write given command to the device.''' self.dc.low() @@ -404,6 +424,7 @@ class TFT(object) : self.spi.send(aCommand) self.cs.high() + @micropython.native def _writedata( self, aData ) : '''Write given data to the device. This may be either a single int or a bytearray of values.''' @@ -412,18 +433,21 @@ class TFT(object) : self.spi.send(aData) self.cs.high() + @micropython.native def _pushcolor( self, aColor ) : '''Push given color to the device.''' self.colorData[0] = aColor >> 8 self.colorData[1] = aColor self._writedata(self.colorData) + @micropython.native def _setMADCTL( self ) : '''Set screen rotation and RGB/BGR format.''' - self._writecommand(ST_MADCTL) + self._writecommand(TFT.MADCTL) rgb = TFTRGB if self._rgb else TFTBGR self._writedata(TFTRotations[self.rotate] | rgb) + @micropython.native def _reset(self): '''Reset the device.''' self.dc.low() @@ -438,58 +462,58 @@ class TFT(object) : '''Initialize blue tab version.''' self._size = (ScreenSize[0] + 2, ScreenSize[1] + 1) self._reset() - self._writecommand(ST_SWRESET) #Software reset. + self._writecommand(TFT.SWRESET) #Software reset. pyb.delay(50) - self._writecommand(ST_SLPOUT) #out of sleep mode. + self._writecommand(TFT.SLPOUT) #out of sleep mode. pyb.delay(500) data1 = bytearray(1) - self._writecommand(ST_COLMOD) #Set color mode. + self._writecommand(TFT.COLMOD) #Set color mode. data1[0] = 0x05 #16 bit color. self._writedata(data1) pyb.delay(10) data3 = bytearray([0x00, 0x06, 0x03]) #fastest refresh, 6 lines front, 3 lines back. - self._writecommand(ST_FRMCTR1) #Frame rate control. + self._writecommand(TFT.FRMCTR1) #Frame rate control. self._writedata(data3) pyb.delay(10) - self._writecommand(ST_MADCTL) + self._writecommand(TFT.MADCTL) data1[0] = 0x08 #row address/col address, bottom to top refresh self._writedata(data1) data2 = bytearray(2) - self._writecommand(ST_DISSET5) #Display settings + self._writecommand(TFT.DISSET5) #Display settings data2[0] = 0x15 #1 clock cycle nonoverlap, 2 cycle gate rise, 3 cycle oscil, equalize data2[1] = 0x02 #fix on VTL self._writedata(data2) - self._writecommand(ST_INVCTR) #Display inversion control + self._writecommand(TFT.INVCTR) #Display inversion control data1[0] = 0x00 #Line inversion. self._writedata(data1) - self._writecommand(ST_PWCTR1) #Power control + self._writecommand(TFT.PWCTR1) #Power control data2[0] = 0x02 #GVDD = 4.7V data2[1] = 0x70 #1.0uA self._writedata(data2) pyb.delay(10) - self._writecommand(ST_PWCTR2) #Power control + self._writecommand(TFT.PWCTR2) #Power control data1[0] = 0x05 #VGH = 14.7V, VGL = -7.35V self._writedata(data1) - self._writecommand(ST_PWCTR3) #Power control + self._writecommand(TFT.PWCTR3) #Power control data2[0] = 0x01 #Opamp current small data2[1] = 0x02 #Boost frequency self._writedata(data2) - self._writecommand(ST_VMCTR1) #Power control + self._writecommand(TFT.VMCTR1) #Power control data2[0] = 0x3C #VCOMH = 4V data2[1] = 0x38 #VCOML = -1.1V self._writedata(data2) pyb.delay(10) - self._writecommand(ST_PWCTR6) #Power control + self._writecommand(TFT.PWCTR6) #Power control data2[0] = 0x11 data2[1] = 0x15 self._writedata(data2) @@ -499,36 +523,36 @@ class TFT(object) : # 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10]) dataGMCTRP = bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10]) - self._writecommand(ST_GMCTRP1) + self._writecommand(TFT.GMCTRP1) self._writedata(dataGMCTRP) # dataGMCTRN = bytearray([0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, # 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10]) dataGMCTRN = bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10]) - self._writecommand(ST_GMCTRN1) + self._writecommand(TFT.GMCTRN1) self._writedata(dataGMCTRN) pyb.delay(10) - self._writecommand(ST_CASET) #Column address set. + self._writecommand(TFT.CASET) #Column address set. self.windowLocData[0] = 0x00 self.windowLocData[1] = 2 #Start at column 2 self.windowLocData[2] = 0x00 self.windowLocData[3] = self._size[0] - 1 self._writedata(self.windowLocData) - self._writecommand(ST_RASET) #Row address set. + self._writecommand(TFT.RASET) #Row address set. self.windowLocData[1] = 1 #Start at row 2. self.windowLocData[3] = self._size[1] - 1 self._writedata(self.windowLocData) - self._writecommand(ST_NORON) #Normal display on. + self._writecommand(TFT.NORON) #Normal display on. pyb.delay(10) - self._writecommand(ST_RAMWR) + self._writecommand(TFT.RAMWR) pyb.delay(500) - self._writecommand(ST_DISPON) + self._writecommand(TFT.DISPON) self.cs.high() pyb.delay(500) @@ -536,182 +560,183 @@ class TFT(object) : '''Initialize a red tab version.''' self._reset() - self._writecommand(ST_SWRESET) #Software reset. + self._writecommand(TFT.SWRESET) #Software reset. pyb.delay(150) - self._writecommand(ST_SLPOUT) #out of sleep mode. + self._writecommand(TFT.SLPOUT) #out of sleep mode. pyb.delay(500) data3 = bytearray([0x01, 0x2C, 0x2D]) #fastest refresh, 6 lines front, 3 lines back. - self._writecommand(ST_FRMCTR1) #Frame rate control. + self._writecommand(TFT.FRMCTR1) #Frame rate control. self._writedata(data3) - self._writecommand(ST_FRMCTR2) #Frame rate control. + self._writecommand(TFT.FRMCTR2) #Frame rate control. self._writedata(data3) data6 = bytearray([0x01, 0x2c, 0x2d, 0x01, 0x2c, 0x2d]) - self._writecommand(ST_FRMCTR3) #Frame rate control. + self._writecommand(TFT.FRMCTR3) #Frame rate control. self._writedata(data6) pyb.delay(10) data1 = bytearray(1) - self._writecommand(ST_INVCTR) #Display inversion control + self._writecommand(TFT.INVCTR) #Display inversion control data1[0] = 0x07 #Line inversion. self._writedata(data1) - self._writecommand(ST_PWCTR1) #Power control + self._writecommand(TFT.PWCTR1) #Power control data3[0] = 0xA2 data3[1] = 0x02 data3[2] = 0x84 self._writedata(data3) - self._writecommand(ST_PWCTR2) #Power control + self._writecommand(TFT.PWCTR2) #Power control data1[0] = 0xC5 #VGH = 14.7V, VGL = -7.35V self._writedata(data1) data2 = bytearray(2) - self._writecommand(ST_PWCTR3) #Power control + self._writecommand(TFT.PWCTR3) #Power control data2[0] = 0x0A #Opamp current small data2[1] = 0x00 #Boost frequency self._writedata(data2) - self._writecommand(ST_PWCTR4) #Power control + self._writecommand(TFT.PWCTR4) #Power control data2[0] = 0x8A #Opamp current small data2[1] = 0x2A #Boost frequency self._writedata(data2) - self._writecommand(ST_PWCTR5) #Power control + self._writecommand(TFT.PWCTR5) #Power control data2[0] = 0x8A #Opamp current small data2[1] = 0xEE #Boost frequency self._writedata(data2) - self._writecommand(ST_VMCTR1) #Power control + self._writecommand(TFT.VMCTR1) #Power control data1[0] = 0x0E self._writedata(data1) - self._writecommand(ST_INVOFF) + self._writecommand(TFT.INVOFF) - self._writecommand(ST_MADCTL) #Power control + self._writecommand(TFT.MADCTL) #Power control data1[0] = 0xC8 self._writedata(data1) - self._writecommand(ST_COLMOD) + self._writecommand(TFT.COLMOD) data1[0] = 0x05 self._writedata(data1) - self._writecommand(ST_CASET) #Column address set. + self._writecommand(TFT.CASET) #Column address set. self.windowLocData[0] = 0x00 self.windowLocData[1] = 0x00 self.windowLocData[2] = 0x00 self.windowLocData[3] = self._size[0] - 1 self._writedata(self.windowLocData) - self._writecommand(ST_RASET) #Row address set. + self._writecommand(TFT.RASET) #Row address set. self.windowLocData[3] = self._size[1] - 1 self._writedata(self.windowLocData) dataGMCTRP = bytearray([0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, 0x1f, 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10]) - self._writecommand(ST_GMCTRP1) + self._writecommand(TFT.GMCTRP1) self._writedata(dataGMCTRP) dataGMCTRN = bytearray([0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10]) - self._writecommand(ST_GMCTRN1) + self._writecommand(TFT.GMCTRN1) self._writedata(dataGMCTRN) pyb.delay(10) - self._writecommand(ST_DISPON) + self._writecommand(TFT.DISPON) pyb.delay(100) - self._writecommand(ST_NORON) #Normal display on. + self._writecommand(TFT.NORON) #Normal display on. pyb.delay(10) self.cs.high() + @micropython.native def initg(self): '''Initialize a green tab version.''' self._reset() - self._writecommand(ST_SWRESET) #Software reset. + self._writecommand(TFT.SWRESET) #Software reset. pyb.delay(150) - self._writecommand(ST_SLPOUT) #out of sleep mode. + self._writecommand(TFT.SLPOUT) #out of sleep mode. pyb.delay(255) data3 = bytearray([0x01, 0x2C, 0x2D]) #fastest refresh, 6 lines front, 3 lines back. - self._writecommand(ST_FRMCTR1) #Frame rate control. + self._writecommand(TFT.FRMCTR1) #Frame rate control. self._writedata(data3) - self._writecommand(ST_FRMCTR2) #Frame rate control. + self._writecommand(TFT.FRMCTR2) #Frame rate control. self._writedata(data3) data6 = bytearray([0x01, 0x2c, 0x2d, 0x01, 0x2c, 0x2d]) - self._writecommand(ST_FRMCTR3) #Frame rate control. + self._writecommand(TFT.FRMCTR3) #Frame rate control. self._writedata(data6) pyb.delay(10) - self._writecommand(ST_INVCTR) #Display inversion control + self._writecommand(TFT.INVCTR) #Display inversion control self._writedata(0x07) - self._writecommand(ST_PWCTR1) #Power control + self._writecommand(TFT.PWCTR1) #Power control data3[0] = 0xA2 data3[1] = 0x02 data3[2] = 0x84 self._writedata(data3) - self._writecommand(ST_PWCTR2) #Power control + self._writecommand(TFT.PWCTR2) #Power control self._writedata(0xC5) data2 = bytearray(2) - self._writecommand(ST_PWCTR3) #Power control + self._writecommand(TFT.PWCTR3) #Power control data2[0] = 0x0A #Opamp current small data2[1] = 0x00 #Boost frequency self._writedata(data2) - self._writecommand(ST_PWCTR4) #Power control + self._writecommand(TFT.PWCTR4) #Power control data2[0] = 0x8A #Opamp current small data2[1] = 0x2A #Boost frequency self._writedata(data2) - self._writecommand(ST_PWCTR5) #Power control + self._writecommand(TFT.PWCTR5) #Power control data2[0] = 0x8A #Opamp current small data2[1] = 0xEE #Boost frequency self._writedata(data2) - self._writecommand(ST_VMCTR1) #Power control + self._writecommand(TFT.VMCTR1) #Power control self._writedata(0x0E) - self._writecommand(ST_INVOFF) + self._writecommand(TFT.INVOFF) self._setMADCTL() - self._writecommand(ST_COLMOD) + self._writecommand(TFT.COLMOD) self._writedata(0x05) - self._writecommand(ST_CASET) #Column address set. + self._writecommand(TFT.CASET) #Column address set. self.windowLocData[0] = 0x00 self.windowLocData[1] = 0x01 #Start at row/column 1. self.windowLocData[2] = 0x00 self.windowLocData[3] = self._size[0] - 1 self._writedata(self.windowLocData) - self._writecommand(ST_RASET) #Row address set. + self._writecommand(TFT.RASET) #Row address set. self.windowLocData[3] = self._size[1] - 1 self._writedata(self.windowLocData) dataGMCTRP = bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10]) - self._writecommand(ST_GMCTRP1) + self._writecommand(TFT.GMCTRP1) self._writedata(dataGMCTRP) dataGMCTRN = bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10]) - self._writecommand(ST_GMCTRN1) + self._writecommand(TFT.GMCTRN1) self._writedata(dataGMCTRN) - self._writecommand(ST_NORON) #Normal display on. + self._writecommand(TFT.NORON) #Normal display on. pyb.delay(10) - self._writecommand(ST_DISPON) + self._writecommand(TFT.DISPON) pyb.delay(100) self.cs.high() diff --git a/TreatThrower.py b/TreatThrower.py index 5f632d9..f517811 100644 --- a/TreatThrower.py +++ b/TreatThrower.py @@ -1,41 +1,48 @@ # Control bot that throws treats using a servo import pyb +import motion -servoNum = 1 -servoCenter = 1440 -servoSpeed = 100 -servoTime = 1450 -ledNum = 3 -triggerInput = 'X3' +class TreatThrower(object): + """Watch for trigger and throw treat using servo + when trigger is activated.""" -def runservo( aServo, aSpeed, aDelay ): - aServo.speed(aSpeed) - pyb.delay(aDelay) - aServo.speed(0) + servoCenter = 1440 + servoSpeed = 100 + servoTime = 1450 + ledNum = 3 -def main( ) : - s1 = pyb.Servo(servoNum) - btn = pyb.Pin(triggerInput, pyb.Pin.IN, pyb.Pin.PULL_UP) - mn, mx, _, a, s = s1.calibration() - s1.calibration(mn, mx, servoCenter, a, s) - s1.speed(0) - l = pyb.LED(ledNum) + def __init__(self, sensor, servonum = 3): + self._sensor = sensor + self._servo = pyb.Servo(servonum) + mn, mx, _, a, s = self._servo.calibration() + self._servo.calibration(mn, mx, TreatThrower.servoCenter, a, s) + self._servo.speed(0) + self._led = pyb.LED(TreatThrower.ledNum) - def throwit( ): - l.on() - runservo(s1, servoSpeed, servoTime) - l.off() + def runservo( self, time ) : + '''Run the servo for the given time.''' + self._servo.speed(TreatThrower.servoSpeed) + pyb.delay(time) + self._servo.speed(0) - sw = pyb.Switch() -# sw.callback(throwit) + def throwit( self ): + self._led.on() + self.runservo(TreatThrower.servoTime) + self._led.off() + while(self._sensor.trigger): + pass - while(1): - if (btn.value() == 0): - throwit() - if sw(): - break; - pyb.delay(20) + def adjust( self, time = 50 ) : + '''Adjust the servo position by running the servo + for the given amount of time.''' + for i in range(step) : + self.runservo(50) + + def run( self ) : + sw = pyb.Switch() + while(not sw()): + if self._sensor.trigger: + self.throwit() + pyb.delay(20) -if __name__ == '__main__': - main() diff --git a/_SYNCAPP/metadata.xml b/_SYNCAPP/metadata.xml index cd6995a03c66391d2817d2cdda2d9cc0270a398f..19bb3c19835e2ad567fbe9dc916d7739a3eb1d50 100644 GIT binary patch literal 18078 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 diff --git a/main.py b/main.py index efca0e5..cfa72f7 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,5 @@ # main.py -- put your code here! -# import TreatThrower # import Balance # Balance.main() @@ -16,13 +15,13 @@ if pyt : from ST7735 import makeg t = makeg() else: - t = pyb.TFT("x", "X1", "X2") #makegp() + t = pyb.TFT("x", "X1", "X2") t.initg() t.fill(0) -import TFT -TFT.run(t) +# import TFT +# TFT.run(t) def tst( aColor ): s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=_+[]{}l;'<>?,./!@#$%^&*():" @@ -41,9 +40,20 @@ def s(aRot, aColor): # b = bomber(t) # b.run() -from level import Level -l = Level(t) -l.run() +# from level import Level +# l = Level(t) +# l.run() # sd = SonarDisplay(t, "X3", "X4") # sd.run() + +# import motion +# m = motion.motion(t) +# # m.run() + +# import TreatThrower +# tt = TreatThrower.TreatThrower(m) +# tt.run() + +from L298N import Motor +m = Motor('Y2', 'Y1', ('Y3', 10)) diff --git a/motion.py b/motion.py new file mode 100644 index 0000000..b2fa767 --- /dev/null +++ b/motion.py @@ -0,0 +1,65 @@ + +import pyb +import PIR +from terminalfont import terminalfont + +class motion(PIR.PIR): + NONE = 0 + UP = 1 + DOWN = 2 + txtsize = 2 + displaytime = 4000 + processdelay = 100 + + """detect motion and print msg on TFT""" + def __init__(self, display): + super(motion, self).__init__(None, "X12", self.msg) + self._extpower = pyb.Pin("X11", pyb.Pin.OUT_PP) + self._extpower.high() + self._display = display + display.rotation(1) + self._state = motion.NONE + self._timer = 0 + self._dirty = False + self._font = terminalfont + self._fontW = terminalfont['Width'] + self._fontH = terminalfont['Height'] + + def msg( self, aArg ) : + self._state = motion.UP if aArg else motion.DOWN + + def txt( self, aText, aColor ) : + x, y = self._display.size() + y >>= 1 + y -= (self._fontH >> 1) * motion.txtsize + self._display.fillrect((0, y), (self._display.size()[0], self._fontH * motion.txtsize), self._display.BLACK) + self._dirty = (aText != None) + if self._dirty: + self._timer = 0 + x >>= 1 + x -= len(aText) * (self._fontW >> 1) * motion.txtsize + print(aText) + self._display.text((x, y), aText, aColor, terminalfont, motion.txtsize) + + def processmsg( self, dt ) : + state = self._state + if state != motion.NONE: + self._state = motion.NONE + if state == motion.UP: + self.txt("Hello", self._display.GREEN) + else: + self.txt("Goodbye", self._display.RED) + elif self._dirty: + self._timer += dt + if self._timer >= motion.displaytime: + self._timer = 0 + self.txt(None, 0) + + def run( self ) : + self._display.fill(0) + self.on() + sw = pyb.Switch() + while sw() == False : + self.processmsg(motion.processdelay) + pyb.delay(motion.processdelay) + self.off() \ No newline at end of file diff --git a/tft.py b/tft.py new file mode 100644 index 0000000..eede03f --- /dev/null +++ b/tft.py @@ -0,0 +1,234 @@ +#testing code for pyb.TFT + +import pyb +from sysfont import sysfont +from seriffont import seriffont +from terminalfont import terminalfont + +def randcolor( ) : + r = pyb.rng() & 0xFF + g = pyb.rng() & 0xFF + b = pyb.rng() & 0xFF + return pyb.TFT.color(r, g, b) + +def testpixel( display ) : + print('testing pixels') + displaysize = display.size() + r = 255 + x = -10 + g = 0 + b = 0 + for y in range(-10, displaysize[1] + 10) : + display.pixel((x, y), pyb.TFT.color(r, g, b)) + x += 1 + g += 2 + b += 1 + for i in range(100): + x = pyb.rng() % displaysize[0] + y = pyb.rng() % displaysize[1] + display.pixel((x,y), randcolor()) + pyb.delay(2000) + +def testline( display ) : + print('testing line') + displaysize = display.size() + start = (int(displaysize[0] / 2), int(displaysize[1] / 2)) + px = 0 + py = 0 + + def draw(x, y) : + display.line(start, (x, y), randcolor()) + + for x in range(displaysize[0]) : + draw(px, py) + px += 1 + + for y in range(displaysize[1]) : + draw(px, py) + py += 1 + + for x in range(displaysize[0]) : + draw(px, py) + px -= 1 + + for y in range(displaysize[1]) : + draw(px, py) + py -= 1 + pyb.delay(2000) + +def testrect( display ) : + print('testing rect') + displaysize = display.size() + size = (20, 10) + p0 = (0, 0) + p1 = (displaysize[0] - size[0], p0[1]) + p2 = (p1[0], displaysize[1] - size[1]) + p3 = (p0[0], p2[1]) + #fillrect at center, top left and bottom right + display.fillrect(p0, size, display.BLUE) + display.fillrect(p1, size, display.GRAY) + display.fillrect(p2, size, display.PURPLE) + display.fillrect(p3, size, display.NAVY) + + #now do border rect as well + display.rect(p0, size, display.CYAN) + display.rect(p1, size, display.WHITE) + display.rect(p2, size, display.YELLOW) + display.rect(p3, size, display.FOREST) + + #try negative sizes + size = (-10, -10) + center = (int((displaysize[0] / 2) - (size[0] / 2)), int((displaysize[1] / 2) - (size[1] / 2))) + display.fillrect(center, size, display.WHITE) + pyb.delay(1000) + display.rect(center, size, display.RED) + pyb.delay(1000) + size = (displaysize[0] * 2, 50) + pos = (-displaysize[0], center[1]) + display.fillrect(pos, size, display.GREEN) + display.rect(pos, size, display.WHITE) + pyb.delay(2000) + +def testcircle( display ) : + print('testing circle') + displaysize = display.size() + radius = 20 + p0 = (radius, radius) + p1 = (displaysize[0] - radius, p0[1]) + p2 = (p1[0], displaysize[1] - radius) + p3 = (p0[0], p2[1]) + #draw filled circle win upper right, center and lower left + display.fillcircle(p0, radius, display.BLUE) + display.fillcircle(p1, radius, display.GRAY) + display.fillcircle(p2, radius, display.PURPLE) + display.fillcircle(p3, radius, display.NAVY) + #Now do border. + display.circle(p0, radius, display.CYAN) + display.circle(p1, radius, display.MAROON) + display.circle(p2, radius, display.YELLOW) + display.circle(p3, radius, display.FOREST) + pyb.delay(2000) + center = ((displaysize[0] >> 1), (displaysize[1] >> 1)) + #try negative radius + display.fillcircle(center, -radius, display.WHITE) + pyb.delay(2000) + display.circle(center, -radius, display.GREEN) + #tedt big circle. + display.fillcircle(center, 90, display.WHITE) + pyb.delay(2000) + display.circle(center, 90, display.GREEN) + display.fill(0) + #draw near edge to test clipping. + pos = (center[0], 0) + display.fillcircle(pos, 30, display.RED) + display.circle(pos, 30, display.PURPLE) + pos = (center[0], displaysize[1] - 1) + display.fillcircle(pos, 30, display.RED) + display.circle(pos, 30, display.PURPLE) + pos = (0, center[1]) + display.fillcircle(pos, 30, display.RED) + display.circle(pos, 30, display.PURPLE) + pos = (displaysize[0] - 1, center[1]) + display.fillcircle(pos, 30, display.RED) + display.circle(pos, 30, display.PURPLE) + pyb.delay(2000) + +def testtext( display ) : + print('testing text') + displaysize = display.size() + txt = "Testing Text" + fontA = [None, sysfont, seriffont, terminalfont] + + def draw( ) : + x = 0 + f = 0 + for y in range(0, display.size()[1], 10) : + display.text((x, y), txt, pyb.TFT.CYAN, fontA[f]) + x += 2 + f = (f + 1) % len(fontA) + + #draw text + draw() + pyb.delay(2000) + display.rotation(1) + display.fill(0) + draw() + pyb.delay(2000) + + #try passing bogus font dict + bogus = { "Hello": 1, "Width": 8 } + try: + display.text((0, 0), txt, pyb.TFT.GREEN, bogus) + except Exception as e : + print("Bogus font failed with:") + print(e) + + pyb.delay(2000) + display.fill(0) + display.rotation(3) + #try different scales + display.text((0, 0), txt, pyb.TFT.GREEN, fontA[0], 2) + display.text((0, 20), txt, pyb.TFT.BLUE, fontA[1], (2, 1)) + display.text((0, 30), txt, pyb.TFT.PURPLE, fontA[2], (1, 2)) + + display.rotation(2) + + #try negative scales + display.text((50, 50), txt, pyb.TFT.YELLOW, fontA[1], -1) + + display.rotation(0) + pyb.delay(2000) + +def testfill( display ) : + print("testing fill") + display.fill(pyb.TFT.GREEN) + pyb.delay(2000) + display.rotation(1) + display.fill(pyb.TFT.RED) + pyb.delay(2000) + display.rotation(2) + display.fill(pyb.TFT.BLACK) + #left at rotation 2. + +def testrgb( display ) : + print("bgr") + display.rgb(False) #bgr + pyb.delay(2000) + print("rgb") + display.rgb(True) #rgb + pyb.delay(1000) + +def testinvert( display ) : + print("Invert Color") + display.invertcolor(True) #invert color + pyb.delay(2000) + display.invertcolor(False) + pyb.delay(2000) + +def testonoff( display ) : + print("Display on/off") + display.on(False) + pyb.delay(2000) + display.on(True) + pyb.delay(2000) + +def run( display ) : + testdisplay = display + #inits? +# display.initg() + displaysize = display.size() + #draw pixels all over, try out of range values as well. + testpixel(display) + #draw lines in 360 deg at center, top left and bottom right + testline(display) + #fill using difference colors and different rotations. + testfill(display) + testtext(display) + display.fill(0) + testrect(display) + testcircle(display) + testinvert(display) + testrgb(display) + #off/on + testonoff(display) + print("Test Done")