Browse Source

lcd ui for whole workflow

Thomas Buck 1 year ago
parent
commit
d7dc4c45be

+ 15
- 0
python-test/copy.sh View File

@@ -6,8 +6,15 @@ cp flow.py /pyboard
6 6
 cp poll.py /pyboard
7 7
 cp scan.py /pyboard
8 8
 cp lcd.py /pyboard
9
+cp workflows.py /pyboard
9 10
 cp states.py /pyboard
10 11
 cp state_scan.py /pyboard
12
+cp state_connect.py /pyboard
13
+cp state_select.py /pyboard
14
+cp state_heat.py /pyboard
15
+cp state_wait_temp.py /pyboard
16
+cp state_wait_time.py /pyboard
17
+cp state_pump.py /pyboard
11 18
 cp $1 /pyboard/main.py
12 19
 EOF
13 20
 else
@@ -16,7 +23,15 @@ cp flow.py /pyboard
16 23
 cp poll.py /pyboard
17 24
 cp scan.py /pyboard
18 25
 cp lcd.py /pyboard
26
+cp workflows.py /pyboard
19 27
 cp states.py /pyboard
20 28
 cp state_scan.py /pyboard
29
+cp state_connect.py /pyboard
30
+cp state_select.py /pyboard
31
+cp state_heat.py /pyboard
32
+cp state_wait_temp.py /pyboard
33
+cp state_wait_time.py /pyboard
34
+cp state_pump.py /pyboard
35
+rm /pyboard/main.py
21 36
 EOF
22 37
 fi

+ 5
- 4
python-test/poll.py View File

@@ -73,22 +73,23 @@ async def get_state(device):
73 73
 
74 74
 async def set_state(device, state):
75 75
     heater, pump = state
76
-    if heater:
76
+    if heater == True:
77 77
         service = await device.service(serviceUuidVolcano4)
78 78
         uuid = bluetooth.UUID("1011000f-5354-4f52-5a26-4249434b454c")
79 79
         characteristic = await service.characteristic(uuid)
80 80
         await characteristic.write(int(0).to_bytes(1, "little"))
81
-    else:
81
+    elif heater == False:
82 82
         service = await device.service(serviceUuidVolcano4)
83 83
         uuid = bluetooth.UUID("10110010-5354-4f52-5a26-4249434b454c")
84 84
         characteristic = await service.characteristic(uuid)
85 85
         await characteristic.write(int(0).to_bytes(1, "little"))
86
-    if pump:
86
+
87
+    if pump == True:
87 88
         service = await device.service(serviceUuidVolcano4)
88 89
         uuid = bluetooth.UUID("10110013-5354-4f52-5a26-4249434b454c")
89 90
         characteristic = await service.characteristic(uuid)
90 91
         await characteristic.write(int(0).to_bytes(1, "little"))
91
-    else:
92
+    elif pump == False:
92 93
         service = await device.service(serviceUuidVolcano4)
93 94
         uuid = bluetooth.UUID("10110014-5354-4f52-5a26-4249434b454c")
94 95
         characteristic = await service.characteristic(uuid)

+ 67
- 0
python-test/state_connect.py View File

@@ -0,0 +1,67 @@
1
+#!/usr/bin/env python
2
+
3
+import uasyncio as asyncio
4
+
5
+class StateConnect:
6
+    def __init__(self, lcd, state):
7
+        self.lcd = lcd
8
+        self.state = state
9
+
10
+        self.lock = asyncio.Lock()
11
+
12
+    def enter(self, val = None):
13
+        self.done = False
14
+        self.client = None
15
+        self.connector = asyncio.create_task(self.connect(val))
16
+
17
+    def exit(self):
18
+        self.connector.cancel()
19
+
20
+        if self.lock.locked():
21
+            self.lock.release()
22
+
23
+        return self.client
24
+
25
+    async def connect(self, d):
26
+        async with self.lock:
27
+            self.done = False
28
+
29
+        if self.state:
30
+            client = await d.device.connect()
31
+        else:
32
+            await d[0].disconnect()
33
+            client = None
34
+
35
+        async with self.lock:
36
+            self.done = True
37
+            self.client = client
38
+
39
+    async def draw(self):
40
+        self.lcd.fill(self.lcd.black)
41
+
42
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
43
+        self.lcd.text("Connecting to Bluetooth device", 0, 10, self.lcd.red)
44
+
45
+        if self.state:
46
+            self.lcd.text("Connecting...", 0, int(self.lcd.height / 2) - 5, self.lcd.white)
47
+        else:
48
+            self.lcd.text("Disconnecting...", 0, int(self.lcd.height / 2) - 5, self.lcd.white)
49
+
50
+        keys = self.lcd.buttons()
51
+
52
+        if keys.once("y"):
53
+            print("user abort")
54
+            if self.state:
55
+                return 5 # disconnect
56
+            else:
57
+                return 0 # scan
58
+
59
+        async with self.lock:
60
+            if self.done:
61
+                if self.state:
62
+                    return 2 # selection
63
+                else:
64
+                    return 0 # scan
65
+
66
+        self.lcd.show()
67
+        return -1 # stay in this state

+ 61
- 0
python-test/state_heat.py View File

@@ -0,0 +1,61 @@
1
+#!/usr/bin/env python
2
+
3
+import uasyncio as asyncio
4
+from poll import set_state
5
+
6
+class StateHeat:
7
+    def __init__(self, lcd, state):
8
+        self.lcd = lcd
9
+        self.state = state
10
+
11
+        self.lock = asyncio.Lock()
12
+
13
+    def enter(self, val = None):
14
+        self.value = val
15
+        self.heater = asyncio.create_task(self.heat())
16
+        self.done = False
17
+
18
+    def exit(self):
19
+        self.heater.cancel()
20
+
21
+        if self.lock.locked():
22
+            self.lock.release()
23
+
24
+        return (self.value[0], self.value[1], 0)
25
+
26
+    async def heat(self):
27
+        print("Setting heater: {}".format(self.state))
28
+        pump = None
29
+        if self.state == False:
30
+            pump = False
31
+        await set_state(self.value[0], (self.state, pump))
32
+
33
+        async with self.lock:
34
+            self.done = True
35
+
36
+    async def draw(self):
37
+        self.lcd.fill(self.lcd.black)
38
+
39
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
40
+        self.lcd.text("Running Workflow - Heat {}".format(self.state), 0, 10, self.lcd.red)
41
+
42
+        keys = self.lcd.buttons()
43
+
44
+        if keys.once("y"):
45
+            print("user abort")
46
+            if self.state:
47
+                async with self.lock:
48
+                    if self.done:
49
+                        return 4 # heat off
50
+            else:
51
+                return 5 # disconnect
52
+
53
+        async with self.lock:
54
+            if self.done:
55
+                if self.state == False:
56
+                    return 5 # disconnect
57
+                else:
58
+                    return 6 # wait for temperature
59
+
60
+        self.lcd.show()
61
+        return -1 # stay in this state

+ 72
- 0
python-test/state_pump.py View File

@@ -0,0 +1,72 @@
1
+#!/usr/bin/env python
2
+
3
+import time
4
+import uasyncio as asyncio
5
+from poll import set_state
6
+from state_wait_temp import draw_graph
7
+
8
+class StatePump:
9
+    def __init__(self, lcd):
10
+        self.lcd = lcd
11
+
12
+        self.lock = asyncio.Lock()
13
+
14
+    def enter(self, val = None):
15
+        self.value = val
16
+        self.pumper = asyncio.create_task(self.pump())
17
+        self.done = False
18
+
19
+        device, workflow, index = self.value
20
+        self.start = None
21
+        self.duration = workflow["steps"][index][2]
22
+
23
+    def exit(self):
24
+        self.pumper.cancel()
25
+
26
+        if self.lock.locked():
27
+            self.lock.release()
28
+
29
+        return (self.value[0], self.value[1], self.value[2] + 1)
30
+
31
+    async def pump(self):
32
+        device, workflow, index = self.value
33
+
34
+        print("Turning on pump")
35
+        await set_state(device, (None, True))
36
+        async with self.lock:
37
+            self.start = time.time()
38
+
39
+        await asyncio.sleep_ms(int(self.duration * 1000))
40
+
41
+        print("Turning off pump")
42
+        await set_state(device, (None, False))
43
+        async with self.lock:
44
+            self.done = True
45
+
46
+    async def draw(self):
47
+        self.lcd.fill(self.lcd.black)
48
+
49
+        device, workflow, index = self.value
50
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
51
+        self.lcd.text("Running Workflow - Pump {}".format(workflow["steps"][index][2]), 0, 10, self.lcd.red)
52
+
53
+        keys = self.lcd.buttons()
54
+
55
+        if keys.once("y"):
56
+            print("user abort")
57
+            return 4 # heat off
58
+
59
+        async with self.lock:
60
+            if self.start != None:
61
+                draw_graph(self.lcd, 0.0, time.time() - self.start, self.duration)
62
+            else:
63
+                self.lcd.text("Turning on pump...", 0, 100, self.lcd.white)
64
+            if self.done:
65
+                if self.value[2] >= (len(workflow["steps"]) - 1):
66
+                    # TODO notify
67
+                    return 4 # heater off
68
+                else:
69
+                    return 6 # wait for temperature
70
+
71
+        self.lcd.show()
72
+        return -1 # stay in this state

+ 4
- 5
python-test/state_scan.py View File

@@ -2,7 +2,6 @@
2 2
 
3 3
 import uasyncio as asyncio
4 4
 from scan import ble_scan
5
-from flow import flow
6 5
 
7 6
 class StateScan:
8 7
     def __init__(self, lcd):
@@ -10,7 +9,7 @@ class StateScan:
10 9
 
11 10
         self.lock = asyncio.Lock()
12 11
 
13
-    def enter(self):
12
+    def enter(self, val = None):
14 13
         self.scanner = asyncio.create_task(self.scan())
15 14
         self.results = []
16 15
         self.current = None
@@ -21,6 +20,8 @@ class StateScan:
21 20
         if self.lock.locked():
22 21
             self.lock.release()
23 22
 
23
+        return self.results[self.current]
24
+
24 25
     async def scan(self):
25 26
         while True:
26 27
             new = await ble_scan(None, None, 0.25)
@@ -71,9 +72,7 @@ class StateScan:
71 72
         async with self.lock:
72 73
             if keys.once("enter"):
73 74
                 if self.current < len(self.results):
74
-                    #return 1 # connect
75
-                    client = await self.results[self.current].device.connect()
76
-                    await flow(client)
75
+                    return 1 # connect
77 76
             elif keys.once("up"):
78 77
                 if self.current == None:
79 78
                     self.current = len(self.results) - 1

+ 57
- 0
python-test/state_select.py View File

@@ -0,0 +1,57 @@
1
+#!/usr/bin/env python
2
+
3
+import uasyncio as asyncio
4
+from workflows import workflows
5
+
6
+class StateSelect:
7
+    def __init__(self, lcd):
8
+        self.lcd = lcd
9
+
10
+    def enter(self, val = None):
11
+        self.client = val
12
+        self.current = 0
13
+
14
+    def exit(self):
15
+        return self.client, workflows[self.current]
16
+
17
+    def draw_list(self):
18
+        for i, wf in enumerate(workflows):
19
+            s1 = "{}".format(wf["name"])
20
+            s2 = "by: {}".format(wf["author"])
21
+
22
+            off = i * 25 + 30
23
+            if off >= self.lcd.height:
24
+                break
25
+
26
+            c = self.lcd.white
27
+            if self.current == i:
28
+                c = self.lcd.red
29
+
30
+            self.lcd.hline(0, off, self.lcd.width, self.lcd.blue)
31
+            self.lcd.text(s1, 0, off + 2, c)
32
+            self.lcd.text(s2, 0, off + 12, c)
33
+
34
+    async def draw(self):
35
+        self.lcd.fill(self.lcd.black)
36
+
37
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
38
+        self.lcd.text("Please select your Workflow", 0, 10, self.lcd.red)
39
+
40
+        keys = self.lcd.buttons()
41
+
42
+        if keys.once("y"):
43
+            print("user abort")
44
+            return 5 # disconnect
45
+        elif keys.once("up"):
46
+            if self.current > 0:
47
+                self.current -= 1
48
+        elif keys.once("down"):
49
+            if self.current < (len(workflows) - 1):
50
+                self.current += 1
51
+        elif keys.once("enter"):
52
+            return 3 # heater on
53
+
54
+        self.draw_list()
55
+
56
+        self.lcd.show()
57
+        return -1 # stay in this state

+ 73
- 0
python-test/state_wait_temp.py View File

@@ -0,0 +1,73 @@
1
+#!/usr/bin/env python
2
+
3
+import uasyncio as asyncio
4
+from poll import set_target_temp, get_current_temp
5
+
6
+def draw_graph(lcd, min, val, max):
7
+    # TODO
8
+    lcd.text("{} -> {} -> {}".format(min, val, max), 0, 100, lcd.white)
9
+
10
+class StateWaitTemp:
11
+    def __init__(self, lcd):
12
+        self.lcd = lcd
13
+
14
+        self.lock = asyncio.Lock()
15
+
16
+    def enter(self, val = None):
17
+        self.value = val
18
+        self.poller = asyncio.create_task(self.poll())
19
+        self.temp = 0.0
20
+        self.min = 0.0
21
+        self.max = 100.0
22
+
23
+    def exit(self):
24
+        self.poller.cancel()
25
+
26
+        if self.lock.locked():
27
+            self.lock.release()
28
+
29
+        return (self.value[0], self.value[1], self.value[2])
30
+
31
+    async def poll(self):
32
+        device, workflow, index = self.value
33
+        async with self.lock:
34
+            self.temp = 0.0
35
+            self.min = 0.0
36
+            self.max = workflow["steps"][index][0]
37
+
38
+        temp = await get_current_temp(device)
39
+        print("initial temp: {}".format(temp))
40
+        async with self.lock:
41
+            self.temp = temp
42
+            self.min = temp
43
+
44
+        print("Setting temperature: {}".format(self.max))
45
+        await set_target_temp(device, self.max)
46
+
47
+        while temp < self.max:
48
+            temp = await get_current_temp(device)
49
+            print("now at {}".format(temp))
50
+            async with self.lock:
51
+                self.temp = temp
52
+
53
+    async def draw(self):
54
+        self.lcd.fill(self.lcd.black)
55
+
56
+        device, workflow, index = self.value
57
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
58
+        self.lcd.text("Running Workflow - Heat {}".format(workflow["steps"][index][0]), 0, 10, self.lcd.red)
59
+
60
+        keys = self.lcd.buttons()
61
+
62
+        if keys.once("y"):
63
+            print("user abort")
64
+            return 4 # heat off
65
+
66
+        async with self.lock:
67
+            draw_graph(self.lcd, self.min, self.temp, self.max)
68
+            if self.temp >= self.max:
69
+                print("switch, {} >= {}".format(self.temp, self.max))
70
+                return 7 # wait for time
71
+
72
+        self.lcd.show()
73
+        return -1 # stay in this state

+ 40
- 0
python-test/state_wait_time.py View File

@@ -0,0 +1,40 @@
1
+#!/usr/bin/env python
2
+
3
+import time
4
+from state_wait_temp import draw_graph
5
+
6
+class StateWaitTime:
7
+    def __init__(self, lcd):
8
+        self.lcd = lcd
9
+
10
+    def enter(self, val = None):
11
+        self.value = val
12
+
13
+        device, workflow, index = self.value
14
+        self.start = time.time()
15
+        self.end = self.start + workflow["steps"][index][1]
16
+
17
+    def exit(self):
18
+        return (self.value[0], self.value[1], self.value[2])
19
+
20
+    async def draw(self):
21
+        self.lcd.fill(self.lcd.black)
22
+
23
+        device, workflow, index = self.value
24
+        self.lcd.text("Volcano Remote Control App", 0, 0, self.lcd.green)
25
+        self.lcd.text("Running Workflow - Wait {}".format(workflow["steps"][index][1]), 0, 10, self.lcd.red)
26
+
27
+        keys = self.lcd.buttons()
28
+
29
+        if keys.once("y"):
30
+            print("user abort")
31
+            return 4 # heat off
32
+
33
+        now = time.time()
34
+        draw_graph(self.lcd, 0.0, now - self.start, self.end - self.start)
35
+        if now >= self.end:
36
+            print("switch, {} >= {}".format(now, self.end))
37
+            return 8 # pump
38
+
39
+        self.lcd.show()
40
+        return -1 # stay in this state

+ 41
- 2
python-test/states.py View File

@@ -3,6 +3,12 @@
3 3
 import uasyncio as asyncio
4 4
 from lcd import LCD
5 5
 from state_scan import StateScan
6
+from state_connect import StateConnect
7
+from state_select import StateSelect
8
+from state_heat import StateHeat
9
+from state_wait_temp import StateWaitTemp
10
+from state_wait_time import StateWaitTime
11
+from state_pump import StatePump
6 12
 
7 13
 class States:
8 14
     def __init__(self):
@@ -19,9 +25,10 @@ class States:
19 25
 
20 26
         next = asyncio.run(self.states[self.current].draw())
21 27
         if next >= 0:
22
-            self.states[self.current].exit()
28
+            print("switch to {}".format(next))
29
+            val = self.states[self.current].exit()
23 30
             self.current = next
24
-            self.states[self.current].enter()
31
+            self.states[self.current].enter(val)
25 32
 
26 33
 if True:#__name__ == "__main__":
27 34
     lcd = LCD()
@@ -33,5 +40,37 @@ if True:#__name__ == "__main__":
33 40
     scan = StateScan(lcd)
34 41
     states.add(scan)
35 42
 
43
+    # 1 - Connect
44
+    conn = StateConnect(lcd, True)
45
+    states.add(conn)
46
+
47
+    # 2 - Select
48
+    select = StateSelect(lcd)
49
+    states.add(select)
50
+
51
+    # 3 - Heater On
52
+    heatOn = StateHeat(lcd, True)
53
+    states.add(heatOn)
54
+
55
+    # 4 - Heater Off
56
+    heatOff = StateHeat(lcd, False)
57
+    states.add(heatOff)
58
+
59
+    # 5 - Disconnect
60
+    disconn = StateConnect(lcd, False)
61
+    states.add(disconn)
62
+
63
+    # 6 - Wait for temperature
64
+    waitTemp = StateWaitTemp(lcd)
65
+    states.add(waitTemp)
66
+
67
+    # 7 - Wait for time
68
+    waitTime = StateWaitTime(lcd)
69
+    states.add(waitTime)
70
+
71
+    # 8 - Pump
72
+    pump = StatePump(lcd)
73
+    states.add(pump)
74
+
36 75
     while True:
37 76
         states.run()

+ 23
- 0
python-test/workflows.py View File

@@ -0,0 +1,23 @@
1
+#!/usr/bin/env python
2
+
3
+workflows = [
4
+    {
5
+        "name": "Hardcore",
6
+        "author": "xythobuz",
7
+        "steps": [
8
+            (190.0, 15.0, 5.0 - 4),
9
+            (205.0, 10.0, 20.0 - 4),
10
+            (220.0, 10.0, 20.0 - 4),
11
+        ],
12
+    },
13
+    {
14
+        "name": "Vorbi",
15
+        "author": "Rinor",
16
+        "steps": [
17
+            (176.0, 10.0, 6.0 - 4),
18
+            (187.0, 5.0, 10.0 -3),
19
+            (204.0, 3.0, 10.0 - 3),
20
+            (217.0, 5.0, 10.0 - 3),
21
+        ],
22
+    },
23
+]

Loading…
Cancel
Save