Browse Source

fix tetris. image resize aspect ratio. cheap chinese gamepad support.

Thomas Buck 1 year ago
parent
commit
ea9b0d94ff
11 changed files with 407 additions and 202 deletions
  1. 48
    48
      camp_pico.py
  2. 53
    52
      camp_small.py
  3. 61
    29
      gamepad.py
  4. 14
    5
      image.py
  5. 4
    4
      mapper.py
  6. 1
    1
      pi.py
  7. 100
    24
      pico_ota.py
  8. 26
    13
      snake.py
  9. 1
    1
      solid.py
  10. 87
    23
      tetris.py
  11. 12
    2
      util.py

+ 48
- 48
camp_pico.py View File

14
 from qr_tmp import qr_data
14
 from qr_tmp import qr_data
15
 from img_tmp import img_data
15
 from img_tmp import img_data
16
 
16
 
17
-if True:#__name__ == "__main__":
18
-    from solid import Solid
19
-    from life import GameOfLife
20
-    from net import CheckHTTP
21
-    from qr import QRScreen
22
-    from scroll import ScrollText
23
-    from splash import SplashScreen
24
-    from manager import Manager
25
-    from pico import PicoBatt
17
+from solid import Solid
18
+from life import GameOfLife
19
+from net import CheckHTTP
20
+from qr import QRScreen
21
+from scroll import ScrollText
22
+from splash import SplashScreen
23
+from manager import Manager
24
+from pico import PicoBatt
26
 
25
 
27
-    url = "http://ubabot.frubar.net"
26
+#url = "http://ubabot.frubar.net"
27
+url = "http://www.xythobuz.de"
28
 
28
 
29
-    import util
30
-    t = util.getTarget()
29
+import util
30
+t = util.getTarget()
31
 
31
 
32
-    # Loading fonts and graphics takes a while.
33
-    # So show a splash screen while the user waits.
34
-    splash = SplashScreen(t)
35
-    t.loop_start()
36
-    splash.draw()
37
-    t.loop_end()
32
+# Loading fonts and graphics takes a while.
33
+# So show a splash screen while the user waits.
34
+splash = SplashScreen(t)
35
+t.loop_start()
36
+splash.draw()
37
+t.loop_end()
38
 
38
 
39
-    # UbaBot is online
40
-    success = Manager(t)
41
-    success.add(ScrollText(t, "Visit UbaBot Cocktail machine at FruBar village for drinks!", "bitmap8", 1, 10, camp_green))
42
-    success.add(Solid(t, 1.0))
43
-    success.add(QRScreen(t, qr_data, 30.0, "Drinks", "bitmap6", camp_pink, (0, 0, 0)))
44
-    success.add(Solid(t, 1.0))
39
+# UbaBot is online
40
+success = Manager(t)
41
+success.add(ScrollText(t, "Visit UbaBot Cocktail machine at FruBar village for drinks!", "bitmap8", 1, 10, camp_green))
42
+success.add(Solid(t, 1.0))
43
+success.add(QRScreen(t, qr_data, 30.0, "Drinks", "bitmap6", camp_pink, (0, 0, 0)))
44
+success.add(Solid(t, 1.0))
45
 
45
 
46
-    # UbaBot is offline
47
-    fail = Manager(t)
48
-    fail.add(ScrollText(t, "#CCCAMP23", "bitmap8", 1, 10, camp_green))
49
-    fail.add(Solid(t, 1.0))
50
-    fail.add(ScrollText(t, "The UbaBot Cocktail machine is closed. Please come back later!", "bitmap8", 1, 10, camp_green))
51
-    fail.add(Solid(t, 1.0))
52
-    fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
53
-    fail.add(Solid(t, 1.0))
54
-    fail.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "bitmap8", 1, 10, camp_green))
55
-    fail.add(Solid(t, 1.0))
46
+# UbaBot is offline
47
+fail = Manager(t)
48
+fail.add(ScrollText(t, "#CCCAMP23", "bitmap8", 1, 10, camp_green))
49
+fail.add(Solid(t, 1.0))
50
+fail.add(ScrollText(t, "The UbaBot Cocktail machine is closed. Please come back later!", "bitmap8", 1, 10, camp_green))
51
+fail.add(Solid(t, 1.0))
52
+fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
53
+fail.add(Solid(t, 1.0))
54
+fail.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "bitmap8", 1, 10, camp_green))
55
+fail.add(Solid(t, 1.0))
56
 
56
 
57
-    # UbaBot status checker
58
-    d = CheckHTTP(url)
59
-    d.success(success)
60
-    d.fail(fail)
57
+# UbaBot status checker
58
+d = CheckHTTP(url)
59
+d.success(success)
60
+d.fail(fail)
61
 
61
 
62
-    # Main "Menu"
63
-    m = Manager(t)
64
-    m.add(QRScreen(t, img_data, 15.0))
65
-    m.add(Solid(t, 1.0))
66
-    m.add(d) # HTTP Check, either "success" or "fail"
67
-    m.add(Solid(t, 1.0))
68
-    m.add(PicoBatt(t, 5.0, 5.0))
69
-    m.add(Solid(t, 1.0))
62
+# Main "Menu"
63
+m = Manager(t)
64
+m.add(QRScreen(t, img_data, 15.0))
65
+m.add(Solid(t, 1.0))
66
+m.add(d) # HTTP Check, either "success" or "fail"
67
+m.add(Solid(t, 1.0))
68
+m.add(PicoBatt(t, 5.0, 5.0))
69
+m.add(Solid(t, 1.0))
70
 
70
 
71
-    m.restart()
72
-    t.loop(m.draw)
71
+m.restart()
72
+t.loop(m.draw)

+ 53
- 52
camp_small.py View File

10
 camp_pink = (251, 72, 196)
10
 camp_pink = (251, 72, 196)
11
 camp_green = (63, 255, 33)
11
 camp_green = (63, 255, 33)
12
 
12
 
13
-if __name__ == "__main__":
14
-    from splash import SplashScreen
15
-    from scroll import ScrollText
16
-    from solid import Solid
17
-    from life import GameOfLife
18
-    from net import CheckHTTP
19
-    from image import ImageScreen
20
-    from qr import QRScreen
21
-    from snake import Snake
22
-    from gamepad import InputWrapper
23
-    from manager import Manager
13
+from splash import SplashScreen
14
+from scroll import ScrollText
15
+from solid import Solid
16
+from life import GameOfLife
17
+from net import CheckHTTP
18
+from image import ImageScreen
19
+from qr import QRScreen
20
+from snake import Snake
21
+from gamepad import InputWrapper
22
+from manager import Manager
23
+import util
24
 
24
 
25
-    url = "http://ubabot.frubar.net"
25
+url = "http://ubabot.frubar.net"
26
 
26
 
27
-    import util
28
-    t = util.getTarget()
29
-    i = InputWrapper()
27
+# Need to import InputWrapper before initializing RGB Matrix on Pi
28
+i = InputWrapper()
30
 
29
 
31
-    # Loading fonts and graphics takes a while.
32
-    # So show a splash screen while the user waits.
33
-    splash = SplashScreen(t)
34
-    t.loop_start()
35
-    splash.draw()
36
-    t.loop_end()
30
+t = util.getTarget()
37
 
31
 
38
-    # UbaBot is online
39
-    success = Manager(t)
40
-    success.add(ImageScreen(t, "drinka.gif", 0.2, 2, 20.0))
41
-    success.add(Solid(t, 1.0))
42
-    success.add(QRScreen(t, url, 30.0, "Drinks:", "tom-thumb", (255, 255, 255), (0, 0, 0)))
43
-    success.add(Solid(t, 1.0))
32
+# Loading fonts and graphics takes a while.
33
+# So show a splash screen while the user waits.
34
+splash = SplashScreen(t)
35
+t.loop_start()
36
+splash.draw()
37
+t.loop_end()
44
 
38
 
45
-    # UbaBot is offline
46
-    fail = Manager(t)
47
-    fail.add(ImageScreen(t, "attention.gif", 0.2, 2, 20.0, (0, 0, 0)))
48
-    fail.add(ScrollText(t, "The UbaBot Cocktail machine is currently closed. Please come back later for more drinks!", "lemon", 2, 75, camp_pink))
49
-    fail.add(Solid(t, 1.0))
50
-    fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
51
-    fail.add(Solid(t, 1.0))
39
+# UbaBot is online
40
+success = Manager(t)
41
+success.add(ImageScreen(t, "drinka.gif", 0.2, 2, 20.0))
42
+success.add(Solid(t, 1.0))
43
+success.add(QRScreen(t, url, 30.0, "Drinks:", "tom-thumb", (255, 255, 255), (0, 0, 0)))
44
+success.add(Solid(t, 1.0))
52
 
45
 
53
-    # UbaBot status checker
54
-    d = CheckHTTP(url)
55
-    d.success(success)
56
-    d.fail(fail)
46
+# UbaBot is offline
47
+fail = Manager(t)
48
+fail.add(ImageScreen(t, "attention.gif", 0.2, 2, 20.0, (0, 0, 0)))
49
+fail.add(ScrollText(t, "The UbaBot Cocktail machine is currently closed. Please come back later for more drinks!", "lemon", 2, 75, camp_pink))
50
+fail.add(Solid(t, 1.0))
51
+fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
52
+fail.add(Solid(t, 1.0))
57
 
53
 
58
-    # Main "Menu"
59
-    m = Manager(t)
60
-    m.add(ScrollText(t, "#CCCAMP23", "lemon", 1, 75, camp_green))
61
-    m.add(Solid(t, 1.0))
62
-    m.add(ImageScreen(t, "Favicon.png", 0, 1, 10.0))
63
-    m.add(Solid(t, 1.0))
64
-    m.add(d) # HTTP Check, either "success" or "fail"
65
-    m.add(Solid(t, 1.0))
66
-    m.add(Snake(t, i, camp_pink, camp_green))
67
-    m.add(Solid(t, 1.0))
68
-    m.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "iv18x16u", 2, 70, camp_green))
69
-    m.add(Solid(t, 1.0))
54
+# UbaBot status checker
55
+d = CheckHTTP(url)
56
+d.success(success)
57
+d.fail(fail)
70
 
58
 
71
-    m.restart()
72
-    t.loop(m.draw)
59
+# Main "Menu"
60
+m = Manager(t)
61
+m.add(ScrollText(t, "#CCCAMP23", "lemon", 1, 75, camp_green))
62
+m.add(Solid(t, 1.0))
63
+m.add(ImageScreen(t, "Favicon.png", 0, 1, 10.0))
64
+m.add(Solid(t, 1.0))
65
+m.add(d) # HTTP Check, either "success" or "fail"
66
+m.add(Solid(t, 1.0))
67
+m.add(Snake(t, i, camp_pink, camp_green))
68
+m.add(Solid(t, 1.0))
69
+m.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "iv18x16u", 2, 70, camp_green))
70
+m.add(Solid(t, 1.0))
71
+
72
+m.restart()
73
+t.loop(m.draw)

+ 61
- 29
gamepad.py View File

26
             "b": False,
26
             "b": False,
27
             "x": False,
27
             "x": False,
28
             "y": False,
28
             "y": False,
29
+            "l": False,
30
+            "r": False,
31
+            "start": False,
32
+            "select": False,
29
         }
33
         }
30
 
34
 
31
         devices = [InputDevice(path) for path in list_devices()]
35
         devices = [InputDevice(path) for path in list_devices()]
32
         for device in devices:
36
         for device in devices:
33
-            c = device.capabilities()
37
+            c = device.capabilities(absinfo=False)
34
             keep = False
38
             keep = False
35
 
39
 
36
-            # check for key events
37
-            if ecodes.EV_KEY in c:
40
+            # check for axis events
41
+            if ecodes.EV_ABS in c:
38
                 # check for gamepad
42
                 # check for gamepad
39
-                if ecodes.BTN_A in c[ecodes.EV_KEY]:
43
+                if ecodes.ABS_X in c[ecodes.EV_ABS]:
40
                     print("Gamepad detected: ", device.name)
44
                     print("Gamepad detected: ", device.name)
41
                     keep = True
45
                     keep = True
42
 
46
 
47
+            # check for key events
48
+            if ecodes.EV_KEY in c:
43
                 # check for arrow keys
49
                 # check for arrow keys
44
-                elif ecodes.KEY_LEFT in c[ecodes.EV_KEY]:
50
+                if ecodes.KEY_LEFT in c[ecodes.EV_KEY]:
45
                     print("Keyboard detected:", device.name)
51
                     print("Keyboard detected:", device.name)
46
                     keep = True
52
                     keep = True
47
 
53
 
56
         for key, mask in self.selector.select(0):
62
         for key, mask in self.selector.select(0):
57
             device = key.fileobj
63
             device = key.fileobj
58
             for event in device.read():
64
             for event in device.read():
59
-                if event.type != ecodes.EV_KEY:
60
-                    continue
61
-
62
-                v = False
63
-                if event.value != 0:
64
-                    v = True
65
+                if event.type == ecodes.EV_KEY:
66
+                    v = False
67
+                    if event.value != 0:
68
+                        v = True
65
 
69
 
66
-                if (event.code == ecodes.KEY_LEFT) or (event.code == ecodes.BTN_WEST):
67
-                    self.keys["left"] = v
68
-                elif (event.code == ecodes.KEY_RIGHT) or (event.code == ecodes.BTN_EAST):
69
-                    self.keys["right"] = v
70
-                elif (event.code == ecodes.KEY_UP) or (event.code == ecodes.BTN_NORTH):
71
-                    self.keys["up"] = v
72
-                elif (event.code == ecodes.KEY_DOWN) or (event.code == ecodes.BTN_SOUTH):
73
-                    self.keys["down"] = v
74
-                elif (event.code == ecodes.KEY_SPACE) or (event.code == ecodes.BTN_A):
75
-                    self.keys["a"] = v
76
-                elif (event.code == ecodes.KEY_LEFTCTRL) or (event.code == ecodes.BTN_B):
77
-                    self.keys["b"] = v
78
-                elif (event.code == ecodes.KEY_LEFTALT) or (event.code == ecodes.BTN_X):
79
-                    self.keys["x"] = v
80
-                elif (event.code == ecodes.KEY_ENTER) or (event.code == ecodes.BTN_Y):
81
-                    self.keys["y"] = v
82
-                else:
83
-                    return self.keys
70
+                    if (event.code == ecodes.KEY_LEFT) or (event.code == ecodes.BTN_WEST):
71
+                        self.keys["left"] = v
72
+                    elif (event.code == ecodes.KEY_RIGHT) or (event.code == ecodes.BTN_EAST):
73
+                        self.keys["right"] = v
74
+                    elif (event.code == ecodes.KEY_UP) or (event.code == ecodes.BTN_NORTH):
75
+                        self.keys["up"] = v
76
+                    elif (event.code == ecodes.KEY_DOWN) or (event.code == ecodes.BTN_SOUTH):
77
+                        self.keys["down"] = v
78
+                    elif (event.code == ecodes.KEY_SPACE) or (event.code == ecodes.BTN_A) or (event.code == ecodes.BTN_THUMB):
79
+                        self.keys["a"] = v
80
+                    elif (event.code == ecodes.KEY_LEFTCTRL) or (event.code == ecodes.BTN_B) or (event.code == ecodes.BTN_THUMB2):
81
+                        self.keys["b"] = v
82
+                    elif (event.code == ecodes.KEY_LEFTALT) or (event.code == ecodes.BTN_X) or (event.code == ecodes.BTN_JOYSTICK) or (event.code == ecodes.BTN_TRIGGER):
83
+                        self.keys["x"] = v
84
+                    elif (event.code == ecodes.KEY_ENTER) or (event.code == ecodes.BTN_Y) or (event.code == ecodes.BTN_TOP):
85
+                        self.keys["y"] = v
86
+                    elif event.code == ecodes.BTN_PINKIE:
87
+                        self.keys["r"] = v
88
+                    elif event.code == ecodes.BTN_TOP2:
89
+                        self.keys["l"] = v
90
+                    elif event.code == ecodes.BTN_BASE4:
91
+                        self.keys["start"] = v
92
+                    elif event.code == ecodes.BTN_BASE3:
93
+                        self.keys["select"] = v
94
+                elif event.type == ecodes.EV_ABS:
95
+                    # TODO 8bit hardcoded
96
+                    if event.code == ecodes.ABS_X:
97
+                        if event.value > 200:
98
+                            self.keys["right"] = True
99
+                            self.keys["left"] = False
100
+                        elif event.value < 100:
101
+                            self.keys["right"] = False
102
+                            self.keys["left"] = True
103
+                        else:
104
+                            self.keys["right"] = False
105
+                            self.keys["left"] = False
106
+                    elif event.code == ecodes.ABS_Y:
107
+                        if event.value > 200:
108
+                            self.keys["down"] = True
109
+                            self.keys["up"] = False
110
+                        elif event.value < 100:
111
+                            self.keys["down"] = False
112
+                            self.keys["up"] = True
113
+                        else:
114
+                            self.keys["down"] = False
115
+                            self.keys["up"] = False
84
 
116
 
85
                 if verbose:
117
                 if verbose:
86
                     print(categorize(event), event)
118
                     print(categorize(event), event)

+ 14
- 5
image.py View File

34
 
34
 
35
         # automatically crop and scale large images
35
         # automatically crop and scale large images
36
         if not self.image.is_animated and ((self.image.width > self.gui.width) or (self.image.height > self.gui.height)):
36
         if not self.image.is_animated and ((self.image.width > self.gui.width) or (self.image.height > self.gui.height)):
37
+            # crop to visible area
37
             self.image = self.image.crop(self.image.getbbox())
38
             self.image = self.image.crop(self.image.getbbox())
38
 
39
 
39
-            if util.isPi():
40
-                # TODO PIL version is too old on Pi
41
-                self.image = self.image.resize((self.gui.width, self.gui.height))
40
+            # keep the aspect ratio and fit within visible area
41
+            ratio = self.image.width / self.image.height
42
+            width = self.gui.width
43
+            height = self.gui.height
44
+            if width < height:
45
+                width = self.gui.width
46
+                height = int(width / ratio)
42
             else:
47
             else:
43
-                self.image = self.image.resize((self.gui.width, self.gui.height),
44
-                                            Image.Resampling.NEAREST)
48
+                height = self.gui.height
49
+                width = int(ratio * height)
50
+
51
+            # resize
52
+            self.image = self.image.resize((width, height),
53
+                                           Image.Resampling.NEAREST)
45
 
54
 
46
             # new image object is also missing these
55
             # new image object is also missing these
47
             self.image.is_animated = False
56
             self.image.is_animated = False

+ 4
- 4
mapper.py View File

52
 # on hard-corded coordinate ranges.
52
 # on hard-corded coordinate ranges.
53
 class MapperColorAdjust(MapperNull):
53
 class MapperColorAdjust(MapperNull):
54
     def set_pixel(self, x, y, color):
54
     def set_pixel(self, x, y, color):
55
-        # third panel from the left, with 64 <= x < 96,
55
+        # second panel from the left, with 32 <= x,
56
         # is "old" type with brighter LEDs.
56
         # is "old" type with brighter LEDs.
57
-        # rest of panels to the left and right are less bright.
58
-        # so adjust brightness of inner panel rg channels down.
59
-        if (x >= (self.gui.panelW * 2)) and (x < (self.gui.panelW * 3)):
57
+        # rest of panels to the left are less bright.
58
+        # so adjust brightness of other panel rg channels down.
59
+        if x >= self.gui.panelW:
60
             color = (int(color[0] * 0.75), int(color[1] * 0.75), color[2])
60
             color = (int(color[0] * 0.75), int(color[1] * 0.75), color[2])
61
 
61
 
62
         self.gui.set_pixel(x, y, color)
62
         self.gui.set_pixel(x, y, color)

+ 1
- 1
pi.py View File

32
 from PIL import Image
32
 from PIL import Image
33
 
33
 
34
 class PiMatrix:
34
 class PiMatrix:
35
-    def __init__(self, w = 32 * 4, h = 32, panelW = 32, panelH = 32):
35
+    def __init__(self, w = 32 * 2, h = 32, panelW = 32, panelH = 32):
36
         self.width = w # x-axis
36
         self.width = w # x-axis
37
         self.height = h # y-axis
37
         self.height = h # y-axis
38
 
38
 

+ 100
- 24
pico_ota.py View File

19
 # to check if we're actually running on MicroPython
19
 # to check if we're actually running on MicroPython
20
 on_pico = False
20
 on_pico = False
21
 try:
21
 try:
22
-    import uos
22
+    import machine
23
     on_pico = True
23
     on_pico = True
24
 except Exception as e:
24
 except Exception as e:
25
     print()
25
     print()
37
 
37
 
38
         self.get = None
38
         self.get = None
39
         self.update_path = "."
39
         self.update_path = "."
40
+        self.exe_path = ""
41
+        self.version_file = "ota_version"
40
         self.blacklist = []
42
         self.blacklist = []
41
 
43
 
42
     def path(self, p):
44
     def path(self, p):
43
         self.update_path = p
45
         self.update_path = p
44
 
46
 
47
+    def exe(self, e):
48
+        self.exe_path = e
49
+
45
     def ignore(self, path):
50
     def ignore(self, path):
46
         if not path in self.blacklist:
51
         if not path in self.blacklist:
47
             self.blacklist.append(path)
52
             self.blacklist.append(path)
67
             print()
72
             print()
68
             return None
73
             return None
69
 
74
 
75
+    def get_stored_commit(self):
76
+        current = "unknown"
77
+        try:
78
+            f = open(os.path.join(self.update_path, self.version_file), "r")
79
+            current = f.readline().strip()
80
+            f.close()
81
+        except Exception as e:
82
+            print()
83
+            if hasattr(sys, "print_exception"):
84
+                sys.print_exception(e)
85
+            else:
86
+                print(e)
87
+            print()
88
+        return current
89
+
90
+    def get_previous_commit(self, commit):
91
+        r = self.fetch(self.host + "/" + self.repo + "/commit/" + commit).text
92
+        for line in r.splitlines():
93
+            if not (self.repo + "/commit/") in line:
94
+                continue
95
+
96
+            line = line[line.find("/commit/") : ][8 : ][ : 40]
97
+            if line != commit:
98
+                return line
99
+        return "unknown"
100
+
70
     def check(self, verbose = False):
101
     def check(self, verbose = False):
71
         if self.branch == None:
102
         if self.branch == None:
72
             # get default branch
103
             # get default branch
83
         if verbose:
114
         if verbose:
84
             print("Latest commit is " + commit)
115
             print("Latest commit is " + commit)
85
 
116
 
86
-        current = ""
87
-        try:
88
-            f = open(os.path.join(self.update_path, "ota_version"), "r")
89
-            current = f.readline().strip()
90
-            f.close()
91
-        except Exception as e:
92
-            print()
93
-            if hasattr(sys, "print_exception"):
94
-                sys.print_exception(e)
95
-            else:
96
-                print(e)
97
-            print()
117
+        current = self.get_stored_commit()
98
 
118
 
99
         if verbose:
119
         if verbose:
100
             if current != commit:
120
             if current != commit:
132
                 print("Writing " + f["path"] + " to " + self.update_path)
152
                 print("Writing " + f["path"] + " to " + self.update_path)
133
 
153
 
134
             # overwrite existing file
154
             # overwrite existing file
135
-            f = open(os.path.join(self.update_path, f["path"]), "w")
136
-            f.write(r)
137
-            f.close()
155
+            fo = open(os.path.join(self.update_path, f["path"]), "w")
156
+            fo.write(r)
157
+            fo.close()
158
+
159
+            if f["path"] == self.exe_path:
160
+                if verbose:
161
+                    print("Writing " + f["path"] + " to main.py")
162
+
163
+                fo = open(os.path.join(self.update_path, "main.py"), "w")
164
+                fo.write(r)
165
+                fo.close()
138
 
166
 
139
         # Write new commit id to local file
167
         # Write new commit id to local file
140
-        f = open(os.path.join(self.update_path, "ota_version"), "w")
168
+        f = open(os.path.join(self.update_path, self.version_file), "w")
141
         f.write(commit + os.linesep)
169
         f.write(commit + os.linesep)
142
         f.close()
170
         f.close()
143
 
171
 
172
+def non_pico_ota_test(ota):
173
+    if not os.path.exists("tmp"):
174
+        os.makedirs("tmp")
175
+    ota.path("tmp")
176
+
177
+    print("Checking for updates")
178
+    newer, commit = ota.check(True)
179
+    print()
180
+
181
+    # Just for testing
182
+    previous = ota.get_previous_commit(commit)
183
+    print("Previous commit (-1):", previous)
184
+    previous = ota.get_previous_commit(previous)
185
+    print("Previous commit (-2):", previous)
186
+    print()
187
+
188
+    if newer:
189
+        print("Updating")
190
+        ota.update_to_commit(commit, True)
191
+    else:
192
+        print("No update required")
193
+
194
+def pico_ota_run(ota):
195
+    newer, commit = ota.check(True)
196
+
197
+    if newer:
198
+        ota.update_to_commit(commit, True)
199
+        machine.soft_reset()
200
+
201
+    fallback = False
202
+
203
+    try:
204
+        import camp_pico
205
+    except Exception as e:
206
+        fallback = True
207
+        print()
208
+        if hasattr(sys, "print_exception"):
209
+            sys.print_exception(e)
210
+        else:
211
+            print(e)
212
+        print()
213
+
214
+    # TODO this would immediately cause another update on reboot
215
+    # TODO set a flag to prevent updates after fallbacks?
216
+    # TODO or better, blacklist failed commit_id!
217
+    #if fallback:
218
+    #    previous = ota.get_previous_commit(commit, True)
219
+    #    ota.update_to_commit(previous, True)
220
+    #    machine.soft_reset()
221
+
144
 if __name__ == "__main__":
222
 if __name__ == "__main__":
145
     ota = PicoOTA("https://git.xythobuz.de", "thomas/rgb-matrix-visualizer")
223
     ota = PicoOTA("https://git.xythobuz.de", "thomas/rgb-matrix-visualizer")
146
 
224
 
157
     ota.ignore("pi.py")
235
     ota.ignore("pi.py")
158
     ota.ignore("test.py")
236
     ota.ignore("test.py")
159
 
237
 
160
-    if not on_pico:
161
-        if not os.path.exists("tmp"):
162
-            os.makedirs("tmp")
163
-        ota.path("tmp")
238
+    ota.exe("camp_pico.py")
164
 
239
 
165
-    newer, commit = ota.check(True)
166
-    if newer:
167
-        ota.update_to_commit(commit, True)
240
+    if not on_pico:
241
+        non_pico_ota_test(ota)
242
+    else:
243
+        pico_ota_run(ota)

+ 26
- 13
snake.py View File

41
         self.player = [ (int(self.gui.width / 2), int(self.gui.height / 2)) ]
41
         self.player = [ (int(self.gui.width / 2), int(self.gui.height / 2)) ]
42
         self.data[self.player[0][0]][self.player[0][1]] = 1
42
         self.data[self.player[0][0]][self.player[0][1]] = 1
43
 
43
 
44
+        self.old_keys = {
45
+            "left": False,
46
+            "right": False,
47
+            "up": False,
48
+            "down": False,
49
+        }
50
+
44
         self.placeDot()
51
         self.placeDot()
45
 
52
 
46
     def finished(self):
53
     def finished(self):
63
 
70
 
64
     def buttons(self):
71
     def buttons(self):
65
         keys = self.input.get()
72
         keys = self.input.get()
66
-        if keys["left"]:
73
+
74
+        if keys["left"] and (not self.old_keys["left"]):
67
             self.directionTmp = "l"
75
             self.directionTmp = "l"
68
-        elif keys["right"]:
76
+        elif keys["right"] and (not self.old_keys["right"]):
69
             self.directionTmp = "r"
77
             self.directionTmp = "r"
70
-        elif keys["up"]:
78
+        elif keys["up"] and (not self.old_keys["up"]):
71
             self.directionTmp = "u"
79
             self.directionTmp = "u"
72
-        elif keys["down"]:
80
+        elif keys["down"] and (not self.old_keys["down"]):
73
             self.directionTmp = "d"
81
             self.directionTmp = "d"
82
+        elif (keys["select"] and keys["start"] and (not self.old_keys["start"])) or (keys["start"] and keys["select"] and (not self.old_keys["select"])):
83
+            self.restart()
84
+
85
+        self.old_keys = keys.copy()
74
 
86
 
75
     def step(self):
87
     def step(self):
76
         player = self.player[len(self.player) - 1]
88
         player = self.player[len(self.player) - 1]
122
         self.scoreText.draw()
134
         self.scoreText.draw()
123
 
135
 
124
     def draw(self):
136
     def draw(self):
137
+        if self.input != None:
138
+            self.buttons()
139
+        else:
140
+            # TODO "AI"
141
+            pass
142
+
125
         if self.direction == "":
143
         if self.direction == "":
126
             if self.finishedEndScreen():
144
             if self.finishedEndScreen():
127
                 self.drawScoreScreen()
145
                 self.drawScoreScreen()
130
                 self.scoreText.restart()
148
                 self.scoreText.restart()
131
             return
149
             return
132
 
150
 
133
-        if self.input != None:
134
-            self.buttons()
135
-        else:
136
-            # TODO "AI"
137
-            pass
138
-
139
         now = time.time()
151
         now = time.time()
140
         if (now - self.last) >= self.timestep:
152
         if (now - self.last) >= self.timestep:
141
             self.last = now
153
             self.last = now
158
                 self.gui.set_pixel(x, y, self.colors[self.data[x][y]])
170
                 self.gui.set_pixel(x, y, self.colors[self.data[x][y]])
159
 
171
 
160
 if __name__ == "__main__":
172
 if __name__ == "__main__":
161
-    import util
162
-    t = util.getTarget()
163
-
173
+    # Need to import InputWrapper before initializing RGB Matrix on Pi
164
     from gamepad import InputWrapper
174
     from gamepad import InputWrapper
165
     i = InputWrapper()
175
     i = InputWrapper()
166
 
176
 
177
+    import util
178
+    t = util.getTarget()
179
+
167
     d = Snake(t, i)
180
     d = Snake(t, i)
168
     t.loop(d.draw)
181
     t.loop(d.draw)

+ 1
- 1
solid.py View File

57
         global s, colors, ci, n, c
57
         global s, colors, ci, n, c
58
 
58
 
59
         now = time.time()
59
         now = time.time()
60
-        if ((now - s) >= 0.1) or (now < s):
60
+        if ((now - s) >= 0.5) or (now < s):
61
             s = now
61
             s = now
62
             n += 1
62
             n += 1
63
             if n >= 15:
63
             if n >= 15:

+ 87
- 23
tetris.py View File

12
 import random
12
 import random
13
 
13
 
14
 class Tetris:
14
 class Tetris:
15
-    def __init__(self, g, i, ts = 0.5, to = 60.0):
15
+    def __init__(self, g, i, ts = 0.5, to = 60.0, w = 10, h = 22):
16
         self.gui = g
16
         self.gui = g
17
         self.input = i
17
         self.input = i
18
         self.timestep = ts
18
         self.timestep = ts
19
         self.timeout = to
19
         self.timeout = to
20
+        self.width = min(w, self.gui.width)
21
+        self.height = min(h, self.gui.height)
22
+
23
+        self.x_off = int((self.gui.panelW - self.width - 2) / 2)
24
+        self.y_off = int((self.gui.panelH - self.height - 2) / 2)
20
 
25
 
21
         self.endText = ScrollText(self.gui, "Game Over!", "uushi",
26
         self.endText = ScrollText(self.gui, "Game Over!", "uushi",
22
                                    2, 75, (251, 72, 196))
27
                                    2, 75, (251, 72, 196))
94
         self.button = None
99
         self.button = None
95
         self.score = 0
100
         self.score = 0
96
         self.done = False
101
         self.done = False
97
-        self.data = [[self.bg for y in range(self.gui.height)] for x in range(self.gui.width)]
102
+        self.data = [[self.bg for y in range(self.height)] for x in range(self.width)]
98
         self.piece = None
103
         self.piece = None
104
+        self.old_keys = {
105
+            "left": False,
106
+            "right": False,
107
+            "up": False,
108
+            "down": False,
109
+        }
99
 
110
 
100
     def finished(self):
111
     def finished(self):
101
         if self.input == None:
112
         if self.input == None:
109
 
120
 
110
         return False
121
         return False
111
 
122
 
112
-    # TODO collision detection is broken
113
-    # TODO not working for pixels outside of vertical center line???
114
-    # TODO something like that...
115
     def collision(self):
123
     def collision(self):
116
         # check for collision of piece with data
124
         # check for collision of piece with data
117
         pos = (self.piece[2], self.piece[3])
125
         pos = (self.piece[2], self.piece[3])
122
                     continue
130
                     continue
123
 
131
 
124
                 # check for collision with bottom wall
132
                 # check for collision with bottom wall
125
-                if (y + pos[1]) >= self.gui.height:
133
+                if (y + pos[1]) >= self.height:
134
+                    return True
135
+
136
+                # check for collision with right wall
137
+                if (x + pos[0]) >= self.width:
126
                     return True
138
                     return True
127
 
139
 
128
                 # check for collision with previous pieces
140
                 # check for collision with previous pieces
142
 
154
 
143
         for y in range(0, len(piece)):
155
         for y in range(0, len(piece)):
144
             for x in range(0, len(piece[y])):
156
             for x in range(0, len(piece[y])):
145
-                remove = False
157
+                # only set or clear where piece actually is
146
                 if piece[y][x] == 0:
158
                 if piece[y][x] == 0:
147
-                    remove = True
159
+                    continue
148
 
160
 
149
-                if clear or remove:
161
+                if clear:
150
                     self.data[x + position[0]][y + position[1]] = self.bg
162
                     self.data[x + position[0]][y + position[1]] = self.bg
151
                 else:
163
                 else:
152
                     self.data[x + position[0]][y + position[1]] = self.piece[1]
164
                     self.data[x + position[0]][y + position[1]] = self.piece[1]
153
 
165
 
166
+    def checkWin(self):
167
+        had_data = False
168
+        for y in range(0, self.height):
169
+            line_full = True
170
+            for x in range(0, self.width):
171
+                if self.data[x][y] == self.bg:
172
+                    line_full = False
173
+                else:
174
+                    had_data = True
175
+
176
+            if had_data and line_full:
177
+                self.score += 1
178
+
179
+                # move stuff above into this line
180
+                for y2 in reversed(range(1, y + 1)):
181
+                    for x in range(0, self.width):
182
+                        self.data[x][y2] = self.data[x][y2 - 1]
183
+
184
+                # clear out top line
185
+                for x in range(0, self.width):
186
+                    self.data[x][0] = self.bg
187
+
188
+        # check for complete win
189
+        board_clear = (self.piece == None)
190
+        for y in range(0, self.height):
191
+            for x in range(0, self.width):
192
+                if self.data[x][y] != self.bg:
193
+                    board_clear = False
194
+                if board_clear == False:
195
+                    break
196
+            if board_clear == False:
197
+                break
198
+        return board_clear
199
+
154
     def step(self):
200
     def step(self):
155
         if self.piece == None:
201
         if self.piece == None:
156
             justPlaced = True
202
             justPlaced = True
164
             ]
210
             ]
165
 
211
 
166
             # center the piece on top of the playing board
212
             # center the piece on top of the playing board
167
-            self.piece[2] = int((self.gui.width - len(self.piece[0][0])) / 2)
213
+            self.piece[2] = int((self.width - len(self.piece[0][0])) / 2)
168
 
214
 
169
             if self.collision():
215
             if self.collision():
170
                 # new piece immediately collided. game over!
216
                 # new piece immediately collided. game over!
188
             if self.piece[2] > 0:
234
             if self.piece[2] > 0:
189
                 self.piece[2] -= 1
235
                 self.piece[2] -= 1
190
         elif self.button == "r":
236
         elif self.button == "r":
191
-            if self.piece[2] < (self.gui.width - 1):
237
+            if self.piece[2] < (self.width - 1):
192
                 self.piece[2] += 1
238
                 self.piece[2] += 1
193
         else:
239
         else:
194
             # one pixel down
240
             # one pixel down
205
             self.piece[2] = oldPosition[0]
251
             self.piece[2] = oldPosition[0]
206
             self.piece[3] = oldPosition[1]
252
             self.piece[3] = oldPosition[1]
207
 
253
 
208
-            if (self.button != "l") and (self.button != "r"):
254
+            if (self.button != "l") and (self.button != "r") and (self.button != "u"):
209
                 # but only stop playing it if it was moving down
255
                 # but only stop playing it if it was moving down
210
                 self.piece = None
256
                 self.piece = None
257
+
258
+                # check for cleared line
259
+                if self.checkWin():
260
+                    return False
211
         else:
261
         else:
212
             # copy piece at new location into buffer
262
             # copy piece at new location into buffer
213
             self.put(False)
263
             self.put(False)
219
 
269
 
220
     def buttons(self):
270
     def buttons(self):
221
         keys = self.input.get()
271
         keys = self.input.get()
222
-        if keys["left"]:
272
+
273
+        if keys["left"] and (not self.old_keys["left"]):
223
             self.button = "l"
274
             self.button = "l"
224
-        elif keys["right"]:
275
+        elif keys["right"] and (not self.old_keys["right"]):
225
             self.button = "r"
276
             self.button = "r"
226
-        elif keys["up"]:
277
+        elif keys["up"] and (not self.old_keys["up"]):
227
             self.button = "u"
278
             self.button = "u"
228
         elif keys["down"]:
279
         elif keys["down"]:
229
             self.button = "d"
280
             self.button = "d"
281
+        elif (keys["select"] and keys["start"] and (not self.old_keys["start"])) or (keys["start"] and keys["select"] and (not self.old_keys["select"])):
282
+            self.restart()
283
+
284
+        self.old_keys = keys.copy()
230
 
285
 
231
     def draw(self):
286
     def draw(self):
232
         if self.done:
287
         if self.done:
245
 
300
 
246
         now = time.time()
301
         now = time.time()
247
         if (self.button != None) or ((now - self.last) >= self.timestep) or (now < self.last):
302
         if (self.button != None) or ((now - self.last) >= self.timestep) or (now < self.last):
248
-            self.last = now
303
+            # don't let user stop falling pieces by moving/rotating endlessly
304
+            if (self.button != "l") and (self.button != "r") and (self.button != "u"):
305
+                self.last = now
306
+
249
             cont = self.step()
307
             cont = self.step()
250
             if cont == False:
308
             if cont == False:
251
                 self.done = True
309
                 self.done = True
252
                 self.scoreText.setText("Score: " + str(self.score), "uushi")
310
                 self.scoreText.setText("Score: " + str(self.score), "uushi")
253
                 self.endText.restart()
311
                 self.endText.restart()
254
 
312
 
255
-        for x in range(0, self.gui.width):
256
-            for y in range(0, self.gui.height):
257
-                self.gui.set_pixel(x, y, self.data[x][y])
313
+        # TODO placement of play area
314
+        for x in range(-1, self.width + 1):
315
+            for y in range(-1, self.height + 1):
316
+                c = (255, 255, 255)
317
+                if (x >= 0) and (y >= 0) and (x < self.width) and (y < self.height):
318
+                    c = self.data[x][y]
258
 
319
 
259
-if __name__ == "__main__":
260
-    import util
261
-    t = util.getTarget()
320
+                self.gui.set_pixel(x + 1 + self.x_off, y + 1 + self.y_off, c)
262
 
321
 
322
+if __name__ == "__main__":
323
+    # Need to import InputWrapper before initializing RGB Matrix on Pi
263
     from gamepad import InputWrapper
324
     from gamepad import InputWrapper
264
     i = InputWrapper()
325
     i = InputWrapper()
265
 
326
 
266
-    d = Tetris(t, i, 0.1)
327
+    import util
328
+    t = util.getTarget()
329
+
330
+    d = Tetris(t, i)
267
     t.loop(d.draw)
331
     t.loop(d.draw)

+ 12
- 2
util.py View File

9
 
9
 
10
 import sys
10
 import sys
11
 
11
 
12
+cachedTarget = None
12
 targetPlatform = None
13
 targetPlatform = None
13
 wifiConnected = False
14
 wifiConnected = False
14
 
15
 
29
     return targetPlatform == "pico"
30
     return targetPlatform == "pico"
30
 
31
 
31
 def getTarget():
32
 def getTarget():
32
-    global targetPlatform
33
+    global targetPlatform, cachedTarget
34
+
35
+    if cachedTarget != None:
36
+        return cachedTarget
33
 
37
 
34
     target = None
38
     target = None
35
     try:
39
     try:
40
         # TODO hard-coded adjustments
44
         # TODO hard-coded adjustments
41
         from mapper import MapperColorAdjust, MapperStripToRect
45
         from mapper import MapperColorAdjust, MapperStripToRect
42
         col = MapperColorAdjust(pi)
46
         col = MapperColorAdjust(pi)
43
-        target = MapperStripToRect(col)
47
+        #target = MapperStripToRect(col)
48
+        target = col
44
 
49
 
45
         if targetPlatform == None:
50
         if targetPlatform == None:
46
             # only print once
51
             # only print once
47
             print("Raspberry Pi Adafruit RGB LED Matrix detected")
52
             print("Raspberry Pi Adafruit RGB LED Matrix detected")
48
         targetPlatform = "pi"
53
         targetPlatform = "pi"
49
     except Exception as e:
54
     except Exception as e:
55
+        target = None
56
+
50
         print()
57
         print()
51
         if hasattr(sys, "print_exception"):
58
         if hasattr(sys, "print_exception"):
52
             sys.print_exception(e)
59
             sys.print_exception(e)
68
                 print("Raspberry Pi Pico Interstate75 RGB LED Matrix detected")
75
                 print("Raspberry Pi Pico Interstate75 RGB LED Matrix detected")
69
             targetPlatform = "pico"
76
             targetPlatform = "pico"
70
         except Exception as e:
77
         except Exception as e:
78
+            target = None
79
+
71
             print()
80
             print()
72
             if hasattr(sys, "print_exception"):
81
             if hasattr(sys, "print_exception"):
73
                 sys.print_exception(e)
82
                 sys.print_exception(e)
84
                 print("Falling back to GUI debug interface")
93
                 print("Falling back to GUI debug interface")
85
             targetPlatform = "tk"
94
             targetPlatform = "tk"
86
 
95
 
96
+    cachedTarget = target
87
     return target
97
     return target
88
 
98
 
89
 # https://github.com/raspberrypi/pico-examples/blob/master/pico_w/wifi/python_test_tcp/micropython_test_tcp_client.py
99
 # https://github.com/raspberrypi/pico-examples/blob/master/pico_w/wifi/python_test_tcp/micropython_test_tcp_client.py

Loading…
Cancel
Save