S&B Volcano vaporizer remote control with Pi Pico W
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

states.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #!/usr/bin/env python3
  2. # ----------------------------------------------------------------------------
  3. # Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # See <http://www.gnu.org/licenses/>.
  16. # ----------------------------------------------------------------------------
  17. from lcd import LCD
  18. lcd = LCD()
  19. import uasyncio as asyncio
  20. import io
  21. import sys
  22. import machine
  23. import os
  24. import gc
  25. import time
  26. from state_wait_temp import from_hsv, translate
  27. # https://github.com/pimoroni/pimoroni-pico/blob/main/micropython/examples/pico_lipo_shim/battery_pico.py
  28. # https://github.com/pimoroni/enviro/pull/146
  29. # TODO https://github.com/micropython/micropython/issues/11185
  30. full_battery = 4.1
  31. empty_battery = 3.2
  32. batt_warn_limit = 15
  33. batt_reread_limit = 2.7
  34. charging = machine.Pin("WL_GPIO2", machine.Pin.IN)
  35. conversion_factor = 3 * 3.3 / 65535
  36. cachedVoltage = None
  37. lastCaching = time.time()
  38. def set_pad(gpio, value):
  39. machine.mem32[0x4001c000 | (4 + (4 * gpio))] = value
  40. def get_pad(gpio):
  41. return machine.mem32[0x4001c000 | (4 + (4 * gpio))]
  42. def batteryVoltageRead():
  43. vsys = machine.ADC(3)
  44. voltage = vsys.read_u16() * conversion_factor
  45. return voltage
  46. def batteryVoltageAverage():
  47. old_pad = get_pad(29)
  48. set_pad(29, 128) # no pulls, no output, no input
  49. sample_count = 3
  50. voltage = 0
  51. for i in range(0, sample_count):
  52. voltage += batteryVoltageRead()
  53. voltage /= sample_count
  54. set_pad(29, old_pad)
  55. return voltage
  56. def batteryVoltage():
  57. global cachedVoltage, lastCaching
  58. if ((time.time() - lastCaching) > 0) or (cachedVoltage == None):
  59. lastCaching = time.time()
  60. cachedVoltage = batteryVoltageAverage()
  61. if cachedVoltage <= batt_reread_limit:
  62. cachedVoltage = batteryVoltageAverage()
  63. percentage = 100.0 * ((cachedVoltage - empty_battery) / (full_battery - empty_battery))
  64. if percentage >= 100.0:
  65. percentage = 99.0
  66. elif percentage < 0.0:
  67. percentage = 0.0
  68. return cachedVoltage, percentage
  69. class States:
  70. def __init__(self, lcd):
  71. self.lcd = lcd
  72. self.states = []
  73. self.current = None
  74. def add(self, s):
  75. self.states.append(s)
  76. async def draw(self):
  77. self.lcd.fill(self.lcd.black)
  78. self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
  79. ret = await self.states[self.current].draw()
  80. voltage, percentage = batteryVoltage()
  81. s = "Charging"
  82. c = self.lcd.white
  83. if charging.value() != 1:
  84. s = "{:.0f}% ({:.2f}V)".format(percentage, voltage)
  85. if percentage <= batt_warn_limit:
  86. c = self.lcd.red
  87. else:
  88. hue = translate(percentage, batt_warn_limit, 100, 0.0, 0.333)
  89. r, g, b = from_hsv(hue, 1.0, 1.0)
  90. c = self.lcd.color(r, g, b)
  91. whole = "Batt: {}".format(s)
  92. self.lcd.text(whole, 0, self.lcd.height - 10, c)
  93. off = (len(whole) + 1) * 8
  94. if percentage <= batt_warn_limit:
  95. self.lcd.text("CHARGE NOW!", off, self.lcd.height - 10, self.lcd.red)
  96. elif charging.value() != 1:
  97. self.lcd.rect(off, self.lcd.height - 10, self.lcd.width - off, 8, c, False)
  98. max_w = self.lcd.width - off - 2
  99. w = int(percentage / 100.0 * max_w)
  100. self.lcd.rect(off + 1, self.lcd.height - 9, w, 6, c, True)
  101. self.lcd.show()
  102. return ret
  103. def run(self):
  104. if self.current == None:
  105. self.current = 0
  106. self.states[self.current].enter()
  107. next = asyncio.run(self.draw())
  108. if next >= 0:
  109. val = self.states[self.current].exit()
  110. self.current = next
  111. self.states[self.current].enter(val)
  112. def state_machine(lcd):
  113. states = States(lcd)
  114. # 0 - Scan
  115. from state_scan import StateScan
  116. scan = StateScan(lcd)
  117. states.add(scan)
  118. # 1 - Connect
  119. from state_connect import StateConnect
  120. conn = StateConnect(lcd, True)
  121. states.add(conn)
  122. # 2 - Select
  123. from state_select import StateSelect
  124. select = StateSelect(lcd)
  125. states.add(select)
  126. # 3 - Heater On
  127. from state_heat import StateHeat
  128. heatOn = StateHeat(lcd, True)
  129. states.add(heatOn)
  130. # 4 - Heater Off
  131. heatOff = StateHeat(lcd, False)
  132. states.add(heatOff)
  133. # 5 - Disconnect
  134. disconn = StateConnect(lcd, False)
  135. states.add(disconn)
  136. # 6 - Wait for temperature
  137. from state_wait_temp import StateWaitTemp
  138. waitTemp = StateWaitTemp(lcd)
  139. states.add(waitTemp)
  140. # 7 - Wait for time
  141. from state_wait_time import StateWaitTime
  142. waitTime = StateWaitTime(lcd)
  143. states.add(waitTime)
  144. # 8 - Pump
  145. from state_pump import StatePump
  146. pump = StatePump(lcd)
  147. states.add(pump)
  148. # 9 - Notify
  149. from state_notify import StateNotify
  150. notify = StateNotify(lcd)
  151. states.add(notify)
  152. while True:
  153. states.run()
  154. def main():
  155. # splash screen
  156. from state_wait_temp import from_hsv
  157. for x in range(0, lcd.width):
  158. hue = x / (lcd.width - 1)
  159. r, g, b = from_hsv(hue, 1.0, 1.0)
  160. c = lcd.color(r, g, b)
  161. lcd.rect(x, 0, 1, lcd.height, c)
  162. lcd.textC("S&B Volcano Remote", int(lcd.width / 2), 10, lcd.green, lcd.black)
  163. lcd.textC("by xythobuz", int(lcd.width / 2), 20, lcd.yellow, lcd.black)
  164. lcd.textC("Initializing...", int(lcd.width / 2), 30, lcd.white, lcd.black)
  165. import _git
  166. lcd.textC(_git.git_branch, int(lcd.width / 2), int(lcd.height / 2) - 10, lcd.green, lcd.black)
  167. lcd.textC(_git.git_hash, int(lcd.width / 2), int(lcd.height / 2), lcd.yellow, lcd.black)
  168. lcd.textC(_git.build_date, int(lcd.width / 2), int(lcd.height / 2) + 10, lcd.white, lcd.black)
  169. lcd.textC(os.uname()[0][ 0 : 30], int(lcd.width / 2), lcd.height - 50, lcd.green, lcd.black)
  170. lcd.textC(os.uname()[3][ 0 : 30], int(lcd.width / 2), lcd.height - 40, lcd.yellow, lcd.black)
  171. lcd.textC(os.uname()[3][30 : 60], int(lcd.width / 2), lcd.height - 30, lcd.yellow, lcd.black)
  172. lcd.textC(os.uname()[4][ 0 : 30], int(lcd.width / 2), lcd.height - 20, lcd.white, lcd.black)
  173. lcd.textC(os.uname()[4][30 : 60], int(lcd.width / 2), lcd.height - 10, lcd.white, lcd.black)
  174. lcd.show()
  175. # bootloader access with face buttons
  176. keys = lcd.buttons()
  177. if keys.once("a") and keys.once("b"):
  178. machine.bootloader()
  179. state_machine(lcd)
  180. try:
  181. main()
  182. except Exception as e:
  183. sys.print_exception(e)
  184. gc.collect()
  185. os = io.StringIO()
  186. sys.print_exception(e, os)
  187. s = os.getvalue()
  188. os.close()
  189. lcd.fill(lcd.black)
  190. lcd.textBlock(s, lcd.white)
  191. lcd.show()
  192. raise e