code.py
  1  # SPDX-FileCopyrightText: 2019 Liz Clark for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import displayio
  7  import terminalio
  8  import adafruit_imageload
  9  from adafruit_display_text.label import Label
 10  from adafruit_featherwing import minitft_featherwing
 11  from adafruit_motorkit import MotorKit
 12  from adafruit_motor import stepper
 13  
 14  # setup stepper motor
 15  kit = MotorKit()
 16  
 17  # setup minitft featherwing
 18  minitft = minitft_featherwing.MiniTFTFeatherWing()
 19  
 20  # setup bitmap file locations
 21  five_minBMP = "/5min_bmp.bmp"
 22  ten_minBMP = "/10min_bmp.bmp"
 23  twenty_minBMP = "/20min_bmp.bmp"
 24  hourBMP = "/60min_bmp.bmp"
 25  runningBMP = "/camSlide_bmp.bmp"
 26  reverseqBMP = "/reverseQ_bmp.bmp"
 27  backingUpBMP = "/backingup_bmp.bmp"
 28  stopBMP = "/stopping_bmp.bmp"
 29  
 30  # variables for state machines in loop
 31  mode = 0
 32  onOff = 0
 33  pause = 0
 34  stop = 0
 35  z = 0
 36  
 37  # image groups
 38  five_minGroup = displayio.Group()
 39  ten_minGroup = displayio.Group()
 40  twenty_minGroup = displayio.Group()
 41  hourGroup = displayio.Group()
 42  reverseqGroup = displayio.Group()
 43  backingUpGroup = displayio.Group()
 44  stopGroup = displayio.Group()
 45  progBarGroup = displayio.Group()
 46  
 47  # bitmap setup for all of the menu screens
 48  five_minBG, five_minPal = adafruit_imageload.load(
 49      five_minBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 50  )
 51  five_minDis = displayio.TileGrid(five_minBG, pixel_shader=five_minPal)
 52  ten_minBG, ten_minPal = adafruit_imageload.load(
 53      ten_minBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 54  )
 55  ten_minDis = displayio.TileGrid(ten_minBG, pixel_shader=ten_minPal)
 56  twenty_minBG, twenty_minPal = adafruit_imageload.load(
 57      twenty_minBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 58  )
 59  twenty_minDis = displayio.TileGrid(twenty_minBG, pixel_shader=twenty_minPal)
 60  hourBG, hourPal = adafruit_imageload.load(
 61      hourBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 62  )
 63  hourDis = displayio.TileGrid(hourBG, pixel_shader=hourPal)
 64  runningBG, runningPal = adafruit_imageload.load(
 65      runningBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 66  )
 67  runningDis = displayio.TileGrid(runningBG, pixel_shader=runningPal)
 68  reverseqBG, reverseqPal = adafruit_imageload.load(
 69      reverseqBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 70  )
 71  reverseqDis = displayio.TileGrid(reverseqBG, pixel_shader=reverseqPal)
 72  backingUpBG, backingUpPal = adafruit_imageload.load(
 73      backingUpBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 74  )
 75  backingUpDis = displayio.TileGrid(backingUpBG, pixel_shader=backingUpPal)
 76  stopBG, stopPal = adafruit_imageload.load(
 77      stopBMP, bitmap=displayio.Bitmap, palette=displayio.Palette
 78  )
 79  stopDis = displayio.TileGrid(stopBG, pixel_shader=stopPal)
 80  
 81  # setup for timer display when camera is sliding
 82  text_area = Label(terminalio.FONT, text="      ")
 83  text_area.x = 55
 84  text_area.y = 65
 85  
 86  # adding the bitmaps to the image groups so they can be displayed
 87  five_minGroup.append(five_minDis)
 88  ten_minGroup.append(ten_minDis)
 89  twenty_minGroup.append(twenty_minDis)
 90  hourGroup.append(hourDis)
 91  progBarGroup.append(runningDis)
 92  progBarGroup.append(text_area)
 93  reverseqGroup.append(reverseqDis)
 94  backingUpGroup.append(backingUpDis)
 95  stopGroup.append(stopDis)
 96  
 97  # setting button states on minitft featherwing to None
 98  down_state = None
 99  up_state = None
100  a_state = None
101  b_state = None
102  select_state = None
103  
104  # arrays to match up with the different slide speeds
105  # graphics menu array
106  graphics = [five_minGroup, ten_minGroup, twenty_minGroup, hourGroup]
107  # delay for the stepper motor
108  speed = [0.0154, 0.034, 0.0688, 0.2062]
109  # time duration for the camera slide
110  slide_duration = [300, 600, 1200, 3600]
111  # beginning timer display
112  slide_begin = ["5:00", "10:00", "20:00", "60:00"]
113  # stepper motor steps that corresponds with the timer display
114  # fmt: off
115  slide_checkin = [  860,  1720,  2580,  3440,  4300,  5160,
116                    6020,  6880,  7740,  8600,  9460, 10320,
117                   11180, 12040, 12900, 13760, 14620, 15480,
118                   16340, 17195]
119  # fmt: on
120  # variable that counts up through the slide_checkin array
121  check = 0
122  
123  # start time
124  begin = time.monotonic()
125  print(begin)
126  # when feather is powered up it shows the initial graphic splash
127  minitft.display.show(graphics[mode])
128  
129  while True:
130      # setup minitft featherwing buttons
131      buttons = minitft.buttons
132      # define the buttons' state changes
133      if not buttons.down and down_state is None:
134          down_state = "pressed"
135      if not buttons.up and up_state is None:
136          up_state = "pressed"
137      if not buttons.select and select_state is None:
138          select_state = "pressed"
139      if not buttons.a and a_state is None:
140          a_state = "pressed"
141      if not buttons.b and b_state is None:
142          b_state = "pressed"
143      # scroll down to change slide duration and graphic
144      if buttons.down and down_state == "pressed":
145          # blocks the button if the slider is sliding or
146          # in an in-between state
147          if pause == 1 or onOff == 1:
148              mode = mode
149              down_state = None
150          else:
151              mode += 1
152              down_state = None
153              if mode > 3:
154                  mode = 0
155              print("Mode:,", mode)
156              minitft.display.show(graphics[mode])
157      # scroll up to change slide duration and graphic
158      if buttons.up and up_state == "pressed":
159          # blocks the button if the slider is sliding or
160          # in an in-between state
161          if pause == 1 or onOff == 1:
162              mode = mode
163              up_state = None
164          else:
165              mode -= 1
166              up_state = None
167              if mode < 0:
168                  mode = 3
169              print("Mode: ", mode)
170              minitft.display.show(graphics[mode])
171      # workaround so that the menu graphics show after a slide is finished
172      if mode == mode and pause == 0 and onOff == 0:
173          minitft.display.show(graphics[mode])
174      # starts slide
175      if buttons.select and select_state == "pressed" or z == 2:
176          # blocks the button if the slider is sliding or
177          # in an in-between state
178          if pause == 1 or onOff == 1:
179              # print("null")
180              select_state = None
181          else:
182              # shows the slider is sliding graphic
183              minitft.display.show(progBarGroup)
184              # gets time of button press
185              press = time.monotonic()
186              print(press)
187              # displays initial timer
188              text_area.text = slide_begin[mode]
189              # resets button
190              select_state = None
191              # changes onOff state
192              onOff += 1
193              # changes z state
194              z = 0
195              if onOff > 1:
196                  onOff = 0
197              # number of steps for the length of the aluminum extrusions
198              for i in range(17200):
199                  # for loop start time
200                  start = time.monotonic()
201                  # gets actual duration time
202                  real_time = start - press
203                  # creates a countdown from the slide's length
204                  end = slide_duration[mode] - real_time
205                  # /60 since time is in seconds
206                  mins_remaining = end / 60
207                  if mins_remaining < 0:
208                      mins_remaining += 60
209                  # gets second(s) count
210                  total_sec_remaining = mins_remaining * 60
211                  # formats to clock time
212                  mins_remaining, total_sec_remaining = divmod(end, 60)
213                  # microstep for the stepper
214                  kit.stepper1.onestep(style=stepper.MICROSTEP)
215                  # delay determines speed of the slide
216                  time.sleep(speed[mode])
217                  if i == slide_checkin[check]:
218                      # check-in for time remaining based on motor steps
219                      print("0%d:%d" % (mins_remaining, total_sec_remaining))
220                      print(check)
221                      if total_sec_remaining < 10:
222                          text_area.text = "%d:0%d" % (
223                              mins_remaining,
224                              total_sec_remaining,
225                          )
226                      else:
227                          text_area.text = "%d:%d" % (mins_remaining, total_sec_remaining)
228                      check = check + 1
229                  if check > 19:
230                      check = 0
231                  if end < 10:
232                      # displays the stopping graphic for the last 10 secs.
233                      minitft.display.show(stopGroup)
234              # changes states after slide has completed
235              kit.stepper1.release()
236              pause = 1
237              onOff = 0
238              stop = 1
239              check = 0
240              # delay for safety
241              time.sleep(2)
242              # shows choice menu
243              minitft.display.show(reverseqGroup)
244      # b is defined to stop the slider
245      # only active if the slider is in the 'stopped' state
246      if buttons.b and b_state == "pressed" and stop == 1:
247          # z defines location of the camera on the slider
248          # 0 means that it is opposite the motor
249          if z == 0:
250              b_state = None
251              time.sleep(1)
252              minitft.display.show(backingUpGroup)
253              # delay for safety
254              time.sleep(2)
255              # brings camera back to 'home' at double speed
256              for i in range(1145):
257                  kit.stepper1.onestep(direction=stepper.BACKWARD, style=stepper.DOUBLE)
258              time.sleep(1)
259              kit.stepper1.release()
260              # changes states
261              pause = 0
262              stop = 0
263          # 1 means that the camera is next to the motor
264          if z == 1:
265              b_state = None
266              time.sleep(2)
267              # changes states
268              pause = 0
269              stop = 0
270              z = 0
271      # a is defined to slide in reverse of the prev. slide
272      # only active if the slider is in the 'stopped' state
273      if buttons.a and a_state == "pressed" and stop == 1:
274          # z defines location of the camera on the slider
275          # 1 means that the camera is next to the motor
276          if z == 1:
277              a_state = None
278              time.sleep(2)
279              stop = 0
280              pause = 0
281              # 2 allows the 'regular' slide loop to run
282              # as if the 'select' button has been pressed
283              z = 2
284          # 0 means that the camera is opposite the motor
285          if z == 0:
286              a_state = None
287              # same script as the 'regular' slide loop
288              time.sleep(2)
289              minitft.display.show(progBarGroup)
290              press = time.monotonic()
291              print(press)
292              text_area.text = slide_begin[mode]
293              onOff += 1
294              pause = 0
295              stop = 0
296              if onOff > 1:
297                  onOff = 0
298              for i in range(17200):
299                  start = time.monotonic()
300                  real_time = start - press
301                  end = slide_duration[mode] - real_time
302                  mins_remaining = end / 60
303                  if mins_remaining < 0:
304                      mins_remaining += 60
305                  total_sec_remaining = mins_remaining * 60
306                  mins_remaining, total_sec_remaining = divmod(end, 60)
307                  # only difference is that the motor is stepping backwards
308                  kit.stepper1.onestep(
309                      direction=stepper.BACKWARD, style=stepper.MICROSTEP
310                  )
311                  time.sleep(speed[mode])
312                  if i == slide_checkin[check]:
313                      print("0%d:%d" % (mins_remaining, total_sec_remaining))
314                      if total_sec_remaining < 10:
315                          text_area.text = "%d:0%d" % (
316                              mins_remaining,
317                              total_sec_remaining,
318                          )
319                      else:
320                          text_area.text = "%d:%d" % (mins_remaining, total_sec_remaining)
321                      check = check + 1
322                  if check > 19:
323                      check = 0
324                  if end < 10:
325                      minitft.display.show(stopGroup)
326              # state changes
327              kit.stepper1.release()
328              pause = 1
329              onOff = 0
330              stop = 1
331              z = 1
332              check = 0
333              time.sleep(2)
334              minitft.display.show(reverseqGroup)