No Description
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.

CatToy.py 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. from wifi import Wifi
  2. from config import Config
  3. from toy import Toy
  4. import random
  5. from machine import Timer
  6. max_laser_power = 0.1
  7. limits = [
  8. # pan_min, pan_max, tilt_min, tilt_max, name
  9. (84, 120, 53, 76, 'office desk, front right')
  10. ]
  11. timerRunning = False
  12. timerData = None
  13. outlineIndex = 0
  14. def buildPage(header, footer):
  15. html = """<!DOCTYPE html>
  16. <html>
  17. <head>
  18. <title>Cat Toy</title>
  19. </head>
  20. <body>
  21. <h1>Cat Toy</h1>
  22. %s
  23. <h2>Laser Pointer</h2>
  24. <form action="/laser" method="get">
  25. Turn power:
  26. <input type="range" name="s" min="0" max="100" step="1" value="%d">
  27. <input type="submit" value="Set"><br>
  28. </form>
  29. <form action="/laser" method="get">
  30. Or just turn it:
  31. <input type="submit" name="s" value="Off">
  32. </form>
  33. <h2>Pan and Tilt Servos</h2>
  34. <form action="/servos" method="get">
  35. Pan: <input type="text" name="s1"><br>
  36. Tilt: <input type="text" name="s2"><br>
  37. <input type="submit" value="Move">
  38. </form>
  39. <form action="/random_move" method="get">
  40. <input type="submit" name="s" value="Go to random position">
  41. </form>
  42. <h2>Auto Play</h2>
  43. <form action="/repeat" method="get">
  44. Limits:
  45. <select name="limit">
  46. %s
  47. </select><br>
  48. Steps: <input type="text" name="steps" value="100"><br>
  49. Duration: <input type="text" name="duration" value="1000">ms<br>
  50. <input type="submit" name="s" value="Random">
  51. <input type="submit" name="s" value="Outline"><br>
  52. Status: %s
  53. </form>
  54. %s
  55. </body>
  56. </html>
  57. """
  58. sl = ""
  59. for pan_min, pan_max, tilt_min, tilt_max, name in limits:
  60. val = name.replace(' ', '_').replace(',', '').lower()
  61. sl += '<option value="' + val + '">' + name + '</option>'
  62. sl += '<option value="">None</option>'
  63. status = "No program running"
  64. if timerRunning:
  65. status = "Program in progress"
  66. page = html % (header, int(max_laser_power * 100.0), sl, status, footer)
  67. return page
  68. random.seed()
  69. t = Toy()
  70. def rootCallback(request):
  71. return buildPage(
  72. '<p>Welcome to the Cat Toy interface by <a href="https://www.xythobuz.de">xythobuz</a>.</p>',
  73. "<p><b>Limits:</b> tMin={} tMax={} pMin={} pMax={}</p>".format(t.tilt_min, t.tilt_max, t.pan_min, t.pan_max)
  74. )
  75. def servoCallback(request):
  76. q = request.find("/servos?")
  77. p1 = request.find("s1=")
  78. p2 = request.find("s2=")
  79. if (q < 0) or (p1 < 0) or (p2 < 0):
  80. print("servo query error: q={} p1={} p2={}".format(q, p1, p2))
  81. return buildPage(
  82. '<p>Error: no servo arguments found in URL query string.</p>',
  83. '<p><a href="/">Back to main page</a></p>'
  84. )
  85. servos = [p1, p2]
  86. result = []
  87. for p in servos:
  88. pe = request.find("&s", p)
  89. if (pe < 0) or (p + 3 >= pe) or (pe - (p + 3) > 3):
  90. pe = request.find(" HTTP", p)
  91. if (pe < 0) or (p + 3 >= pe) or (pe - (p + 3) > 3):
  92. print("servo query error: p={} pe={}".format(p, pe))
  93. return buildPage(
  94. '<p>Error parsing query string.</p>',
  95. '<p><a href="/">Back to main page</a></p>'
  96. )
  97. r = request[p + 3 : pe]
  98. s = int(r)
  99. result.append(s)
  100. print("servos: pan={} tilt={}", result[0], result[1])
  101. t.angle(t.pan, result[0])
  102. t.angle(t.tilt, result[1])
  103. return buildPage(
  104. '<p>Servos move to s1=' + str(result[0]) + ' s2=' + str(result[1]) + '.</p>',
  105. '<p><a href="/">Back to main page</a></p>'
  106. )
  107. def laserCallback(request):
  108. value = 0.0
  109. text = "off"
  110. if request.find("?s=") == 10:
  111. pe = request.find(" HTTP", 10)
  112. r = request[13 : pe]
  113. if r != "Off":
  114. value = int(r)
  115. text = "to " + str(r) + '%'
  116. print("laser: {}%".format(value))
  117. t.laser(value / 100.0)
  118. return buildPage(
  119. '<p>Laser turned ' + text + '!</p>',
  120. '<p><a href="/">Back to main page</a></p>'
  121. )
  122. def randomMoveCallback(request):
  123. tilt = random.randint(t.tilt_min, t.tilt_max)
  124. pan = random.randint(t.pan_min, t.pan_max)
  125. print("random: tilt={} pan={}".format(tilt, pan))
  126. t.angle(t.tilt, tilt)
  127. t.angle(t.pan, pan)
  128. return buildPage(
  129. '<p>Random move to pan={} tilt={}</p>'.format(pan, tilt),
  130. '<p><a href="/">Back to main page</a></p>'
  131. )
  132. def doMove(pan_min, pan_max, tilt_min, tilt_max, dur):
  133. tilt = random.randint(tilt_min, tilt_max)
  134. pan = random.randint(pan_min, pan_max)
  135. print("random move: tilt={} pan={} duration={}".format(tilt, pan, dur))
  136. t.angle(t.tilt, tilt)
  137. t.angle(t.pan, pan)
  138. def doOutline(pan_min, pan_max, tilt_min, tilt_max, dur):
  139. global outlineIndex
  140. points = [
  141. (pan_min, tilt_min),
  142. (pan_min, tilt_max),
  143. (pan_max, tilt_max),
  144. (pan_max, tilt_min)
  145. ]
  146. outlineIndex = (outlineIndex + 1) % 4
  147. pan, tilt = points[outlineIndex]
  148. print("outline move: tilt={} pan={} duration={}".format(tilt, pan, dur))
  149. t.angle(t.tilt, tilt)
  150. t.angle(t.pan, pan)
  151. def timerCallback(unused):
  152. global timerRunning, timerData
  153. if not timerRunning:
  154. return
  155. pan_min, pan_max, tilt_min, tilt_max, steps, duration, outline = timerData
  156. dur = duration
  157. if not outline:
  158. if dur < 200:
  159. dur = random.randint(200, 2000)
  160. else:
  161. dur = random.randint(200, duration)
  162. else:
  163. if dur < 200:
  164. dur = 500
  165. if steps > 0:
  166. steps -= 1
  167. if not outline:
  168. doMove(pan_min, pan_max, tilt_min, tilt_max, dur)
  169. else:
  170. doOutline(pan_min, pan_max, tilt_min, tilt_max, dur)
  171. tim = Timer(period = dur, mode=Timer.ONE_SHOT, callback = timerCallback)
  172. else:
  173. timerRunning = False
  174. t.laser(0.0)
  175. timerData = (pan_min, pan_max, tilt_min, tilt_max, steps, duration, outline)
  176. def startRepeat(pan_min, pan_max, tilt_min, tilt_max, steps, duration, outline):
  177. global timerRunning, timerData
  178. timerData = (pan_min, pan_max, tilt_min, tilt_max, steps, duration, outline)
  179. if not timerRunning:
  180. timerRunning = True
  181. t.laser(max_laser_power)
  182. timerCallback(None)
  183. def stopRepeat():
  184. global timerRunning, timerData
  185. timerRunning = False
  186. t.laser(0.0)
  187. def repeatCallback(request):
  188. q = request.find("/repeat?")
  189. pl = request.find("limit=", q)
  190. ps = request.find("steps=", pl)
  191. pd = request.find("duration=", ps)
  192. pp = request.find("s=", pd)
  193. if (q < 0) or (pl < 0) or (ps < 0) or (pd < 0) or (pp < 0):
  194. print("repeat query error: q={} pl={} ps={} pd={} pp={}".format(q, pl, ps, pd, pp))
  195. return buildPage(
  196. '<p>Error: no repeat arguments found in URL query string.</p>',
  197. '<p><a href="/">Back to main page</a></p>'
  198. )
  199. data = [("limit=", pl), ("steps=", ps), ("duration=", pd), ("s=", pp)]
  200. result = []
  201. for s, p in data:
  202. #print(p)
  203. pe = request.find("&", p)
  204. #print(pe)
  205. if (pe < 0) or (p + len(s) > pe) or (pe - (p + 3) > 40):
  206. pe = request.find(" HTTP", p)
  207. #print(pe)
  208. if (pe < 0) or (p + len(s) > pe) or (pe - (p + 3) > 40):
  209. print("repeat query error: p={} pe={}".format(p, pe))
  210. return buildPage(
  211. '<p>Error parsing query string.</p>',
  212. '<p><a href="/">Back to main page</a></p>'
  213. )
  214. r = request[p + len(s) : pe]
  215. result.append(r)
  216. #print()
  217. print("repeat: limit={} steps={} duration={} s={}".format(result[0], result[1], result[2], result[3]))
  218. if len(result[0]) == 0:
  219. stopRepeat()
  220. return buildPage(
  221. '<p>Stopped repeated automatic moves!</p>',
  222. '<p><a href="/">Back to main page</a></p>'
  223. )
  224. outline = False
  225. if result[3].lower() == "outline":
  226. outline = True
  227. for pan_min, pan_max, tilt_min, tilt_max, name in limits:
  228. val = name.replace(' ', '_').replace(',', '').lower()
  229. if result[0] == val:
  230. startRepeat(pan_min, pan_max, tilt_min, tilt_max, int(result[1]), int(result[2]), outline)
  231. break
  232. return buildPage(
  233. '<p>Starting moves with limit={} steps={} duration={}</p>'.format(result[0], result[1], result[2]),
  234. '<p><a href="/">Back to main page</a></p>'
  235. )
  236. w = Wifi(Config.ssid, Config.password)
  237. w.add_handler("/", rootCallback)
  238. w.add_handler("/servos", servoCallback)
  239. w.add_handler("/laser", laserCallback)
  240. w.add_handler("/random_move", randomMoveCallback)
  241. w.add_handler("/repeat", repeatCallback)
  242. w.listen()