S&B Volcano vaporizer remote control with Pi Pico W
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

states.py 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/usr/bin/env python
  2. import uasyncio as asyncio
  3. import io
  4. import sys
  5. import machine
  6. import os
  7. import gc
  8. import time
  9. # https://github.com/pimoroni/pimoroni-pico/blob/main/micropython/examples/pico_lipo_shim/battery_pico.py
  10. # https://github.com/pimoroni/enviro/pull/146
  11. # TODO https://github.com/micropython/micropython/issues/11185
  12. full_battery = 4.2
  13. empty_battery = 3.2
  14. charging = machine.Pin("WL_GPIO2", machine.Pin.IN)
  15. conversion_factor = 3 * 3.3 / 65535
  16. cachedVoltage = None
  17. lastCaching = time.time()
  18. def set_pad(gpio, value):
  19. machine.mem32[0x4001c000 | (4 + (4 * gpio))] = value
  20. def get_pad(gpio):
  21. return machine.mem32[0x4001c000 | (4 + (4 * gpio))]
  22. def batteryVoltageRead():
  23. vsys = machine.ADC(3)
  24. voltage = vsys.read_u16() * conversion_factor
  25. return voltage
  26. def batteryVoltageAverage():
  27. old_pad = get_pad(29)
  28. set_pad(29, 128) # no pulls, no output, no input
  29. sample_count = 10
  30. voltage = 0
  31. for i in range(0, sample_count):
  32. voltage += batteryVoltageRead()
  33. voltage /= sample_count
  34. set_pad(29, old_pad)
  35. return voltage
  36. def batteryVoltage():
  37. global cachedVoltage, lastCaching
  38. if ((time.time() - lastCaching) >= 2) or (cachedVoltage == None):
  39. lastCaching = time.time()
  40. cachedVoltage = batteryVoltageAverage()
  41. percentage = 100.0 * ((cachedVoltage - empty_battery) / (full_battery - empty_battery))
  42. if percentage > 100.0:
  43. percentage = 100.0
  44. return cachedVoltage, percentage
  45. class States:
  46. def __init__(self, lcd):
  47. self.lcd = lcd
  48. self.states = []
  49. self.current = None
  50. def add(self, s):
  51. self.states.append(s)
  52. async def draw(self):
  53. self.lcd.fill(self.lcd.black)
  54. self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
  55. r = await self.states[self.current].draw()
  56. voltage, percentage = batteryVoltage()
  57. s = "Charging ({:.2f}V)".format(voltage)
  58. c = self.lcd.green
  59. if charging.value() != 1:
  60. s = "{:.0f}% ({:.2f}V)".format(percentage, voltage)
  61. c = self.lcd.white
  62. if percentage <= 20:
  63. c = self.lcd.red
  64. self.lcd.text("Battery: {}".format(s), 0, self.lcd.height - 10, c)
  65. self.lcd.show()
  66. return r
  67. def run(self):
  68. if self.current == None:
  69. self.current = 0
  70. self.states[self.current].enter()
  71. next = asyncio.run(self.draw())
  72. if next >= 0:
  73. val = self.states[self.current].exit()
  74. self.current = next
  75. self.states[self.current].enter(val)
  76. def state_machine(lcd):
  77. states = States(lcd)
  78. # 0 - Scan
  79. from state_scan import StateScan
  80. scan = StateScan(lcd)
  81. states.add(scan)
  82. # 1 - Connect
  83. from state_connect import StateConnect
  84. conn = StateConnect(lcd, True)
  85. states.add(conn)
  86. # 2 - Select
  87. from state_select import StateSelect
  88. select = StateSelect(lcd)
  89. states.add(select)
  90. # 3 - Heater On
  91. from state_heat import StateHeat
  92. heatOn = StateHeat(lcd, True)
  93. states.add(heatOn)
  94. # 4 - Heater Off
  95. heatOff = StateHeat(lcd, False)
  96. states.add(heatOff)
  97. # 5 - Disconnect
  98. disconn = StateConnect(lcd, False)
  99. states.add(disconn)
  100. # 6 - Wait for temperature
  101. from state_wait_temp import StateWaitTemp
  102. waitTemp = StateWaitTemp(lcd)
  103. states.add(waitTemp)
  104. # 7 - Wait for time
  105. from state_wait_time import StateWaitTime
  106. waitTime = StateWaitTime(lcd)
  107. states.add(waitTime)
  108. # 8 - Pump
  109. from state_pump import StatePump
  110. pump = StatePump(lcd)
  111. states.add(pump)
  112. # 9 - Notify
  113. from state_notify import StateNotify
  114. notify = StateNotify(lcd)
  115. states.add(notify)
  116. while True:
  117. states.run()
  118. from lcd import LCD
  119. lcd = LCD()
  120. # splash screen
  121. lcd.fill(lcd.black)
  122. lcd.textC("S&B Volcano Remote", int(lcd.width / 2), 10, lcd.green)
  123. lcd.textC("by xythobuz", int(lcd.width / 2), 20, lcd.yellow)
  124. lcd.textC("Initializing...", int(lcd.width / 2), 30, lcd.white)
  125. lcd.textC(os.uname()[0][ 0 : 30], int(lcd.width / 2), lcd.height - 50, lcd.green)
  126. lcd.textC(os.uname()[3][ 0 : 30], int(lcd.width / 2), lcd.height - 40, lcd.yellow)
  127. lcd.textC(os.uname()[3][30 : 60], int(lcd.width / 2), lcd.height - 30, lcd.yellow)
  128. lcd.textC(os.uname()[4][ 0 : 30], int(lcd.width / 2), lcd.height - 20, lcd.white)
  129. lcd.textC(os.uname()[4][30 : 60], int(lcd.width / 2), lcd.height - 10, lcd.white)
  130. lcd.show()
  131. lcd.brightness(1.0)
  132. # bootloader access with face buttons
  133. keys = lcd.buttons()
  134. if keys.once("a") and keys.once("b"):
  135. machine.bootloader()
  136. try:
  137. state_machine(lcd)
  138. except Exception as e:
  139. sys.print_exception(e)
  140. gc.collect()
  141. os = io.StringIO()
  142. sys.print_exception(e, os)
  143. s = os.getvalue()
  144. os.close()
  145. lcd.fill(lcd.black)
  146. lcd.textBlock(s, lcd.white)
  147. lcd.show()
  148. raise e