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
 from wifi import Wifi
1
 from wifi import Wifi
2
 from config import Config
2
 from config import Config
3
 from toy import Toy
3
 from toy import Toy
4
+from log import LogDup
4
 import random
5
 import random
6
+import time
5
 from machine import Timer
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
 max_laser_power = 0.1
16
 max_laser_power = 0.1
8
 
17
 
9
 limits = [
18
 limits = [
10
     # pan_min, pan_max, tilt_min, tilt_max, name
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
 timerRunning = False
28
 timerRunning = False
15
 timerData = None
29
 timerData = None
16
 outlineIndex = 0
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
 def buildPage(header, footer):
43
 def buildPage(header, footer):
19
     html = """<!DOCTYPE html>
44
     html = """<!DOCTYPE html>
55
                 <input type="submit" name="s" value="Outline"><br>
80
                 <input type="submit" name="s" value="Outline"><br>
56
                 Status: %s
81
                 Status: %s
57
             </form>
82
             </form>
83
+            <h2>Status</h2>
84
+            <p><b>Battery:</b> %s
58
             %s
85
             %s
86
+            <h2>Console</h2>
87
+            <pre>%s</pre>
59
         </body>
88
         </body>
60
     </html>
89
     </html>
61
     """
90
     """
70
     if timerRunning:
99
     if timerRunning:
71
         status = "Program in progress"
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
 def rootCallback(request):
110
 def rootCallback(request):
111
+    pan_min, pan_max, tilt_min, tilt_max = t.maximum_limits
80
     return buildPage(
112
     return buildPage(
81
         '<p>Welcome to the Cat Toy interface by <a href="https://www.xythobuz.de">xythobuz</a>.</p>',
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
 def servoCallback(request):
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
     q = request.find("/servos?")
126
     q = request.find("/servos?")
87
     p1 = request.find("s1=")
127
     p1 = request.find("s1=")
88
     p2 = request.find("s2=")
128
     p2 = request.find("s2=")
119
     )
159
     )
120
 
160
 
121
 def laserCallback(request):
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
     value = 0.0
170
     value = 0.0
123
     text = "off"
171
     text = "off"
124
 
172
 
138
     )
186
     )
139
 
187
 
140
 def randomMoveCallback(request):
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
     tilt = random.randint(t.tilt_min, t.tilt_max)
197
     tilt = random.randint(t.tilt_min, t.tilt_max)
142
     pan = random.randint(t.pan_min, t.pan_max)
198
     pan = random.randint(t.pan_min, t.pan_max)
143
     print("random: tilt={} pan={}".format(tilt, pan))
199
     print("random: tilt={} pan={}".format(tilt, pan))
170
     t.angle(t.pan, pan)
226
     t.angle(t.pan, pan)
171
 
227
 
172
 def timerCallback(unused):
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
     if not timerRunning:
237
     if not timerRunning:
176
         return
238
         return
193
             doMove(pan_min, pan_max, tilt_min, tilt_max, dur)
255
             doMove(pan_min, pan_max, tilt_min, tilt_max, dur)
194
         else:
256
         else:
195
             doOutline(pan_min, pan_max, tilt_min, tilt_max, dur)
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
     else:
259
     else:
198
         timerRunning = False
260
         timerRunning = False
199
         t.laser(0.0)
261
         t.laser(0.0)
215
     t.laser(0.0)
277
     t.laser(0.0)
216
 
278
 
217
 def repeatCallback(request):
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
     q = request.find("/repeat?")
288
     q = request.find("/repeat?")
219
     pl = request.find("limit=", q)
289
     pl = request.find("limit=", q)
220
     ps = request.find("steps=", pl)
290
     ps = request.find("steps=", pl)
270
         '<p><a href="/">Back to main page</a></p>'
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
 w = Wifi(Config.ssid, Config.password)
400
 w = Wifi(Config.ssid, Config.password)
274
 w.add_handler("/", rootCallback)
401
 w.add_handler("/", rootCallback)
275
 w.add_handler("/servos", servoCallback)
402
 w.add_handler("/servos", servoCallback)
277
 w.add_handler("/random_move", randomMoveCallback)
404
 w.add_handler("/random_move", randomMoveCallback)
278
 w.add_handler("/repeat", repeatCallback)
405
 w.add_handler("/repeat", repeatCallback)
279
 w.listen()
406
 w.listen()
407
+
408
+print("Ready!")

+ 4
- 4
copy.sh View File

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

+ 16
- 0
log.py View File

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
 # https://how2electronics.com/how-to-control-servo-motor-with-raspberry-pi-pico/
1
 # https://how2electronics.com/how-to-control-servo-motor-with-raspberry-pi-pico/
2
 
2
 
3
 import time
3
 import time
4
-from machine import Pin, PWM
4
+from machine import Pin, PWM, ADC
5
 from servo import Servo
5
 from servo import Servo
6
 
6
 
7
 class Toy:
7
 class Toy:
10
         servo1: GPIO pin number of the pan servo.
10
         servo1: GPIO pin number of the pan servo.
11
         servo2: GPIO pin number of the tilt servo.
11
         servo2: GPIO pin number of the tilt servo.
12
         laser: GPIO pin number of the laser diode.
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
     # maximum movements on cardboard box
18
     # maximum movements on cardboard box
16
     # pan_min, pan_max, tilt_min, tilt_max
19
     # pan_min, pan_max, tilt_min, tilt_max
17
     maximum_limits = (20, 160, 0, 90)
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
         self.laserPin = PWM(Pin(laser, Pin.OUT))
31
         self.laserPin = PWM(Pin(laser, Pin.OUT))
21
         self.laserPin.freq(1000)
32
         self.laserPin.freq(1000)
22
         self.laser(0)
33
         self.laser(0)
27
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
38
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
28
         self.angle(self.pan, int((pan_max - pan_min) / 2) + pan_min)
39
         self.angle(self.pan, int((pan_max - pan_min) / 2) + pan_min)
29
         self.angle(self.tilt, int((tilt_max - tilt_min) / 2) + tilt_min)
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
     def map_value(self, x, in_min, in_max, out_min, out_max):
48
     def map_value(self, x, in_min, in_max, out_min, out_max):
35
         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
49
         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
45
         v = 1.0 - value
59
         v = 1.0 - value
46
         self.laserPin.duty_u16(int(v * 65535))
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
     def test(self, steps = 10):
87
     def test(self, steps = 10):
49
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
88
         pan_min, pan_max, tilt_min, tilt_max = self.maximum_limits
50
 
89
 
57
                 self.angle(self.pan, x)
96
                 self.angle(self.pan, x)
58
                 time.sleep(0.2)
97
                 time.sleep(0.2)
59
 
98
 
60
-        self.tilt.free()
61
-        self.pan.free()
62
-
99
+        self.free()
63
         self.laser(0)
100
         self.laser(0)

Loading…
Cancel
Save