Browse Source

add adc, button, led

Thomas Buck 1 year ago
parent
commit
40b3c9e3fa
4 changed files with 202 additions and 20 deletions
  1. 137
    8
      CatToy.py
  2. 4
    4
      copy.sh
  3. 16
    0
      log.py
  4. 45
    8
      toy.py

+ 137
- 8
CatToy.py View File

@@ -1,19 +1,44 @@
1 1
 from wifi import Wifi
2 2
 from config import Config
3 3
 from toy import Toy
4
+from log import LogDup
4 5
 import random
6
+import time
5 7
 from machine import Timer
8
+import os
9
+
10
+stdio_data = LogDup()
11
+os.dupterm(stdio_data)
12
+
13
+print("Initializing CatToy...")
14
+t = Toy()
6 15
 
7 16
 max_laser_power = 0.1
8 17
 
9 18
 limits = [
10 19
     # pan_min, pan_max, tilt_min, tilt_max, name
11
-    (84, 120, 53, 76, 'office desk, front right')
20
+    (84, 120, 53, 76, '3d printer tower, top'),
21
+    (84, 120, 53, 76, 'office desk, front right'),
22
+    (t.maximum_limits[0], t.maximum_limits[1], t.maximum_limits[2], t.maximum_limits[3], 'maximum'),
12 23
 ]
13 24
 
25
+minimumBatteryVoltage = 3.25 * 2.0
26
+maximumBatteryVoltage = 4.2 * 2.0
27
+
14 28
 timerRunning = False
15 29
 timerData = None
16 30
 outlineIndex = 0
31
+buttonSelection = 0
32
+ledPattern = None
33
+patternIndex = 0
34
+patternTime = time.ticks_ms()
35
+buttonTime = None
36
+
37
+repeatTimer = Timer()
38
+buttonTimer = Timer()
39
+ledTimer = Timer()
40
+
41
+random.seed()
17 42
 
18 43
 def buildPage(header, footer):
19 44
     html = """<!DOCTYPE html>
@@ -55,7 +80,11 @@ def buildPage(header, footer):
55 80
                 <input type="submit" name="s" value="Outline"><br>
56 81
                 Status: %s
57 82
             </form>
83
+            <h2>Status</h2>
84
+            <p><b>Battery:</b> %s
58 85
             %s
86
+            <h2>Console</h2>
87
+            <pre>%s</pre>
59 88
         </body>
60 89
     </html>
61 90
     """
@@ -70,19 +99,30 @@ def buildPage(header, footer):
70 99
     if timerRunning:
71 100
         status = "Program in progress"
72 101
 
73
-    page = html % (header, int(max_laser_power * 100.0), sl, status, footer)
74
-    return page
102
+    vb = t.getBatteryVoltage()
103
+    pb = (vb - minimumBatteryVoltage) / (maximumBatteryVoltage - minimumBatteryVoltage) * 100.0
104
+    pb = max(min(pb, 100), 0)
105
+    battery = str(vb) + "V (" + str(pb) + "%)"
75 106
 
76
-random.seed()
77
-t = Toy()
107
+    page = html % (header, int(max_laser_power * 100.0), sl, status, battery, footer, stdio_data.data.decode("utf-8"))
108
+    return page
78 109
 
79 110
 def rootCallback(request):
111
+    pan_min, pan_max, tilt_min, tilt_max = t.maximum_limits
80 112
     return buildPage(
81 113
         '<p>Welcome to the Cat Toy interface by <a href="https://www.xythobuz.de">xythobuz</a>.</p>',
82
-        "<p><b>Limits:</b> tMin={} tMax={} pMin={} pMax={}</p>".format(t.tilt_min, t.tilt_max, t.pan_min, t.pan_max)
114
+        "<p><b>Limits:</b> tMin={} tMax={} pMin={} pMax={}</p>".format(tilt_min, tilt_max, pan_min, pan_max)
83 115
     )
84 116
 
85 117
 def servoCallback(request):
118
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
119
+        stopRepeat()
120
+        t.free()
121
+        return buildPage(
122
+            '<p>Error: Battery Voltage too low. <b>Please charge them!</b></p>',
123
+            '<p><a href="/">Back to main page</a></p>'
124
+        )
125
+
86 126
     q = request.find("/servos?")
87 127
     p1 = request.find("s1=")
88 128
     p2 = request.find("s2=")
@@ -119,6 +159,14 @@ def servoCallback(request):
119 159
     )
120 160
 
121 161
 def laserCallback(request):
162
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
163
+        stopRepeat()
164
+        t.free()
165
+        return buildPage(
166
+            '<p>Error: Battery Voltage too low. <b>Please charge them!</b></p>',
167
+            '<p><a href="/">Back to main page</a></p>'
168
+        )
169
+
122 170
     value = 0.0
123 171
     text = "off"
124 172
 
@@ -138,6 +186,14 @@ def laserCallback(request):
138 186
     )
139 187
 
140 188
 def randomMoveCallback(request):
189
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
190
+        stopRepeat()
191
+        t.free()
192
+        return buildPage(
193
+            '<p>Error: Battery Voltage too low. <b>Please charge them!</b></p>',
194
+            '<p><a href="/">Back to main page</a></p>'
195
+        )
196
+
141 197
     tilt = random.randint(t.tilt_min, t.tilt_max)
142 198
     pan = random.randint(t.pan_min, t.pan_max)
143 199
     print("random: tilt={} pan={}".format(tilt, pan))
@@ -170,7 +226,13 @@ def doOutline(pan_min, pan_max, tilt_min, tilt_max, dur):
170 226
     t.angle(t.pan, pan)
171 227
 
172 228
 def timerCallback(unused):
173
-    global timerRunning, timerData
229
+    global timerRunning, timerData, repeatTimer
230
+
231
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
232
+        print("Abort due to low battery voltage: " + str(t.getBatteryVoltage()))
233
+        stopRepeat()
234
+        t.free()
235
+        return
174 236
 
175 237
     if not timerRunning:
176 238
         return
@@ -193,7 +255,7 @@ def timerCallback(unused):
193 255
             doMove(pan_min, pan_max, tilt_min, tilt_max, dur)
194 256
         else:
195 257
             doOutline(pan_min, pan_max, tilt_min, tilt_max, dur)
196
-        tim = Timer(period = dur, mode=Timer.ONE_SHOT, callback = timerCallback)
258
+        repeatTimer.init(period = dur, mode = Timer.ONE_SHOT, callback = timerCallback)
197 259
     else:
198 260
         timerRunning = False
199 261
         t.laser(0.0)
@@ -215,6 +277,14 @@ def stopRepeat():
215 277
     t.laser(0.0)
216 278
 
217 279
 def repeatCallback(request):
280
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
281
+        stopRepeat()
282
+        t.free()
283
+        return buildPage(
284
+            '<p>Error: Battery Voltage too low. <b>Please charge them!</b></p>',
285
+            '<p><a href="/">Back to main page</a></p>'
286
+        )
287
+
218 288
     q = request.find("/repeat?")
219 289
     pl = request.find("limit=", q)
220 290
     ps = request.find("steps=", pl)
@@ -270,6 +340,63 @@ def repeatCallback(request):
270 340
         '<p><a href="/">Back to main page</a></p>'
271 341
     )
272 342
 
343
+def buttonCallback(state):
344
+    global timerRunning, buttonSelection, buttonTime
345
+
346
+    if state:
347
+        buttonTime = time.ticks_ms()
348
+    elif buttonTime != None:
349
+        if time.ticks_diff(time.ticks_ms(), buttonTime) <= 500:
350
+            buttonSelection = (buttonSelection + 1) % len(limits)
351
+            print("Selection: " + str(buttonSelection + 1))
352
+        else:
353
+            if not timerRunning:
354
+                pan_min, pan_max, tilt_min, tilt_max, name = limits[buttonSelection]
355
+                print("Start pattern " + name)
356
+                startRepeat(pan_min, pan_max, tilt_min, tilt_max, 200, 2500, False)
357
+            else:
358
+                print("Stop")
359
+                stopRepeat()
360
+
361
+def ledStatus():
362
+    global timerRunning, buttonSelection, ledPattern, patternIndex, patternTime
363
+    patternMode = False
364
+
365
+    if t.getBatteryVoltage() < minimumBatteryVoltage:
366
+        patternMode = True
367
+        pattern = [ 100, 100 ]
368
+    elif timerRunning:
369
+        t.status(True)
370
+    else:
371
+        patternMode = True
372
+        pattern = [ 300 ] * (buttonSelection * 2 + 1) + [ 1500 ]
373
+
374
+    if patternMode:
375
+        if pattern != ledPattern:
376
+            ledPattern = pattern
377
+            patternIndex = 0
378
+            patternTime = time.ticks_ms()
379
+
380
+        if time.ticks_diff(time.ticks_ms(), patternTime) >= ledPattern[patternIndex]:
381
+            t.status(patternIndex % 2 == 1)
382
+            patternIndex = (patternIndex + 1) % len(ledPattern)
383
+            patternTime = time.ticks_ms()
384
+
385
+def buttonTimerCallback(timer):
386
+    global buttonTimer
387
+    t.poll(buttonCallback)
388
+    buttonTimer.init(period = 25, mode = Timer.ONE_SHOT, callback = buttonTimerCallback)
389
+
390
+def ledTimerCallback(timer):
391
+    global ledTimer
392
+    ledStatus()
393
+    ledTimer.init(period = 100, mode = Timer.ONE_SHOT, callback = ledTimerCallback)
394
+
395
+print("Starting Timers...")
396
+buttonTimerCallback(None)
397
+ledTimerCallback(None)
398
+
399
+print("Initializing WiFi...")
273 400
 w = Wifi(Config.ssid, Config.password)
274 401
 w.add_handler("/", rootCallback)
275 402
 w.add_handler("/servos", servoCallback)
@@ -277,3 +404,5 @@ w.add_handler("/laser", laserCallback)
277 404
 w.add_handler("/random_move", randomMoveCallback)
278 405
 w.add_handler("/repeat", repeatCallback)
279 406
 w.listen()
407
+
408
+print("Ready!")

+ 4
- 4
copy.sh View File

@@ -1,18 +1,18 @@
1 1
 #!/bin/bash
2 2
 
3
-PORT=/dev/ttyACM1
4
-
5 3
 if [ $# -ne 0 ] ; then
6
-cat << EOF | rshell -p $PORT
4
+cat << EOF | rshell
7 5
 cp config.py /pyboard
6
+cp log.py /pyboard
8 7
 cp servo.py /pyboard
9 8
 cp toy.py /pyboard
10 9
 cp wifi.py /pyboard
11 10
 cp $1 /pyboard/main.py
12 11
 EOF
13 12
 else
14
-cat << EOF | rshell -p $PORT
13
+cat << EOF | rshell
15 14
 cp config.py /pyboard
15
+cp log.py /pyboard
16 16
 cp servo.py /pyboard
17 17
 cp toy.py /pyboard
18 18
 cp wifi.py /pyboard

+ 16
- 0
log.py View File

@@ -0,0 +1,16 @@
1
+# https://forum.micropython.org/viewtopic.php?t=5442
2
+
3
+import io
4
+
5
+class LogDup(io.IOBase):
6
+    def __init__(self):
7
+        self.data = bytearray()
8
+
9
+    def write(self, data):
10
+        self.data += data
11
+        if len(self.data) > 1024:
12
+            self.data = self.data[len(self.data) - 1024 : ]
13
+        return len(data)
14
+
15
+    def readinto(self, data):
16
+        return 0

+ 45
- 8
toy.py View File

@@ -1,7 +1,7 @@
1 1
 # https://how2electronics.com/how-to-control-servo-motor-with-raspberry-pi-pico/
2 2
 
3 3
 import time
4
-from machine import Pin, PWM
4
+from machine import Pin, PWM, ADC
5 5
 from servo import Servo
6 6
 
7 7
 class Toy:
@@ -10,13 +10,24 @@ class Toy:
10 10
         servo1: GPIO pin number of the pan servo.
11 11
         servo2: GPIO pin number of the tilt servo.
12 12
         laser: GPIO pin number of the laser diode.
13
+        button: GPIO pin number of an active high push button.
14
+        led: GPIO pin number of an active high LED.
15
+        battery: ADC pin number of battery voltage divider.
13 16
     """
14 17
 
15 18
     # maximum movements on cardboard box
16 19
     # pan_min, pan_max, tilt_min, tilt_max
17 20
     maximum_limits = (20, 160, 0, 90)
18 21
 
19
-    def __init__(self, servo1 = 28, servo2 = 27, laser = 2):
22
+    last_button = None
23
+    time_button = None
24
+    last_value = None
25
+
26
+    # Battery Voltage divider
27
+    r1 = 18000.0
28
+    r2 = 10000.0
29
+
30
+    def __init__(self, servo1 = 28, servo2 = 27, laser = 2, button = 22, led = 16, battery = 26):
20 31
         self.laserPin = PWM(Pin(laser, Pin.OUT))
21 32
         self.laserPin.freq(1000)
22 33
         self.laser(0)
@@ -27,9 +38,12 @@ class Toy:
27 38
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
28 39
         self.angle(self.pan, int((pan_max - pan_min) / 2) + pan_min)
29 40
         self.angle(self.tilt, int((tilt_max - tilt_min) / 2) + tilt_min)
30
-        time.sleep(0.1)
31
-        self.pan.free()
32
-        self.tilt.free()
41
+        time.sleep(0.2)
42
+        self.free()
43
+
44
+        self.button = Pin(button, Pin.IN, Pin.PULL_UP)
45
+        self.led = Pin(led, Pin.OUT)
46
+        self.battery = ADC(Pin(battery, Pin.IN))
33 47
 
34 48
     def map_value(self, x, in_min, in_max, out_min, out_max):
35 49
         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
@@ -45,6 +59,31 @@ class Toy:
45 59
         v = 1.0 - value
46 60
         self.laserPin.duty_u16(int(v * 65535))
47 61
 
62
+    def getBatteryVoltage(self):
63
+        adc = self.battery.read_u16()
64
+        u2 = adc / 65535.0 * 3.3
65
+        u1 = u2 / (self.r2 / (self.r1 + self.r2))
66
+        #print("ADC:", adc, u2, u1)
67
+        return u1
68
+
69
+    def status(self, state):
70
+        self.led(1 if state else 0)
71
+
72
+    def poll(self, callback):
73
+        val = not self.button.value()
74
+        if val != self.last_button:
75
+            self.time_button = time.ticks_ms()
76
+            self.last_button = val
77
+
78
+        if time.ticks_diff(time.ticks_ms(), self.time_button) > 50:
79
+            if self.last_value != val:
80
+                callback(val)
81
+                self.last_value = val
82
+
83
+    def free(self):
84
+        self.tilt.free()
85
+        self.pan.free()
86
+
48 87
     def test(self, steps = 10):
49 88
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
50 89
 
@@ -57,7 +96,5 @@ class Toy:
57 96
                 self.angle(self.pan, x)
58 97
                 time.sleep(0.2)
59 98
 
60
-        self.tilt.free()
61
-        self.pan.free()
62
-
99
+        self.free()
63 100
         self.laser(0)

Loading…
Cancel
Save