flexitac 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flexitac-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wesley Maa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: flexitac
3
+ Version: 0.2.0
4
+ Summary: Python runtime and flashing tools for FlexiTac tactile sensors
5
+ Author-email: Wesley Maa <wesley.maa@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/WT-MM/PyFlexiTac.git
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: numpy
12
+ Requires-Dist: pyserial
13
+ Provides-Extra: dev
14
+ Requires-Dist: ruff; extra == "dev"
15
+ Requires-Dist: mypy; extra == "dev"
16
+ Requires-Dist: pytest; extra == "dev"
17
+ Requires-Dist: matplotlib-stubs; extra == "dev"
18
+ Provides-Extra: examples
19
+ Requires-Dist: matplotlib; extra == "examples"
20
+ Provides-Extra: all
21
+ Requires-Dist: flexitac[dev]; extra == "all"
22
+ Requires-Dist: flexitac[examples]; extra == "all"
23
+ Dynamic: license-file
24
+
25
+ # flexitac
26
+
27
+ Python interface for [FlexiTac](https://flexitac.github.io/) tactile sensors:
28
+ flash Arduino firmware, then read framed sensor data over serial.
29
+
30
+ Defaults target the standard FlexiTac 12x32 sensor (12 rows wired to mux
31
+ channels 4-15). Override `--rows`, `--cols`, and `--mux-offset` for variants.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ # recommended
37
+ uv sync # core deps only
38
+ uv sync --extra examples # include matplotlib for the heatmap CLI
39
+ uv sync --extra dev # dev tooling (ruff, mypy, pytest)
40
+
41
+ # or with pip
42
+ pip install flexitac
43
+ pip install 'flexitac[examples]' # include matplotlib
44
+ ```
45
+
46
+ ## Reading frames
47
+
48
+ ```python
49
+ from flexitac import FlexiTacSensor
50
+
51
+ with FlexiTacSensor("/dev/ttyUSB0") as sensor: # rows=12, cols=32 by default
52
+ for frame in sensor:
53
+ print(frame.normalized.shape, frame.normalized.max())
54
+ ```
55
+
56
+ `sensor.read()` returns a `FlexiTacFrame(seq, timestamp_s, raw, normalized)`.
57
+ The first read auto-calibrates by collecting `init_frames` (default 30) and
58
+ storing the per-pixel median as the baseline. Call `sensor.calibrate()` to
59
+ recalibrate.
60
+
61
+ ## CLI tools
62
+
63
+ All CLI commands are installed as entry points:
64
+
65
+ ```bash
66
+ flexitac-stream --port /dev/ttyUSB0 # stream frames & print stats
67
+ flexitac-heatmap --port /dev/ttyUSB0 # live matplotlib heatmap
68
+ flexitac-flash # flash firmware (auto-detects board)
69
+ flexitac-find-port # identify sensor port by unplug
70
+
71
+ # with uv (no install needed)
72
+ uv run flexitac-stream --port /dev/ttyUSB0
73
+ uv run flexitac-heatmap --port /dev/ttyUSB0
74
+ ```
75
+
76
+ ### `flexitac-stream`
77
+
78
+ Stream frames and print FPS / signal stats:
79
+
80
+ ```bash
81
+ flexitac-stream --port /dev/ttyUSB0 --frames 30
82
+ ```
83
+
84
+ ```
85
+ frame= 10 fps= 85.3 raw_max=104 norm_max=0.000
86
+ frame= 20 fps= 87.1 raw_max=109 norm_max=0.123
87
+ ```
88
+
89
+ What to check:
90
+
91
+ - **`fps`** stabilizes near your expected rate (~100+ fps for a 12x32 sensor
92
+ at 2 Mbps). If it's 0 or you get `TimeoutError`, the firmware isn't sending
93
+ framed data -- confirm `--rows`/`--cols`/`--baud` match what you flashed.
94
+ - **`raw_max`** is in `[0, 255]` and changes when you press the sensor.
95
+ A flat 0 or flat 255 indicates a wiring issue, not a flashing issue.
96
+ - **`norm_max`** rises toward 1.0 under contact and stays near 0 at rest.
97
+
98
+ ### `flexitac-heatmap`
99
+
100
+ Live heatmap visualization (requires `matplotlib` -- install with the `examples` extra):
101
+
102
+ ```bash
103
+ flexitac-heatmap --port /dev/ttyUSB0
104
+ ```
105
+
106
+ Press the sensor pad -- bright spots should track your touch.
107
+
108
+ ### `flexitac-find-port`
109
+
110
+ Not sure which `/dev/tty*` your sensor is on? Run:
111
+
112
+ ```bash
113
+ flexitac-find-port
114
+ ```
115
+
116
+ It snapshots ports, asks you to unplug the sensor, then reports whichever port
117
+ disappeared.
118
+
119
+ ### `flexitac-flash`
120
+
121
+ Requires [`arduino-cli`](https://arduino.github.io/arduino-cli/latest/installation/).
122
+ First-time setup:
123
+
124
+ ```bash
125
+ brew install arduino-cli
126
+ # or use the upstream install script:
127
+ curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
128
+
129
+ # then install the Arduino AVR core
130
+ arduino-cli core update-index
131
+ arduino-cli core install arduino:avr # AVR core for Uno/Nano/Mega/etc.
132
+ ```
133
+
134
+ ```bash
135
+ # auto-detects the port + FQBN if exactly one Arduino is plugged in
136
+ flexitac-flash
137
+
138
+ # override geometry / wiring for non-standard sensors
139
+ flexitac-flash --rows 16 --cols 32 --mux-offset 0
140
+
141
+ # if unable to autodetect, pass --port and --fqbn explicitly:
142
+ flexitac-flash --port /dev/ttyUSB0 --fqbn arduino:avr:uno
143
+ ```
144
+
145
+ Defaults: `rows=12`, `cols=32`, `baud=2000000`, `mux-offset=4` (standard
146
+ FlexiTac 12x32 sensor wired to mux channels 4-15). The firmware is generated
147
+ from `flexitac/firmware/template.ino` by substituting `ROW_COUNT`,
148
+ `COLUMN_COUNT`, `BAUD_RATE`, and `MUX_CHANNEL_OFFSET`. To customize pin
149
+ assignments, edit the template directly.
150
+
151
+ ## Wire protocol
152
+
153
+ Each frame: marker `0xAA 0x55` followed by `rows * cols` uint8 ADC samples,
154
+ streamed continuously at the configured baud rate.
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ uv sync --extra dev
160
+
161
+ make format # ruff format + autofix
162
+ make static-checks # ruff + mypy
163
+ make test # pytest
164
+ ```
@@ -0,0 +1,140 @@
1
+ # flexitac
2
+
3
+ Python interface for [FlexiTac](https://flexitac.github.io/) tactile sensors:
4
+ flash Arduino firmware, then read framed sensor data over serial.
5
+
6
+ Defaults target the standard FlexiTac 12x32 sensor (12 rows wired to mux
7
+ channels 4-15). Override `--rows`, `--cols`, and `--mux-offset` for variants.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ # recommended
13
+ uv sync # core deps only
14
+ uv sync --extra examples # include matplotlib for the heatmap CLI
15
+ uv sync --extra dev # dev tooling (ruff, mypy, pytest)
16
+
17
+ # or with pip
18
+ pip install flexitac
19
+ pip install 'flexitac[examples]' # include matplotlib
20
+ ```
21
+
22
+ ## Reading frames
23
+
24
+ ```python
25
+ from flexitac import FlexiTacSensor
26
+
27
+ with FlexiTacSensor("/dev/ttyUSB0") as sensor: # rows=12, cols=32 by default
28
+ for frame in sensor:
29
+ print(frame.normalized.shape, frame.normalized.max())
30
+ ```
31
+
32
+ `sensor.read()` returns a `FlexiTacFrame(seq, timestamp_s, raw, normalized)`.
33
+ The first read auto-calibrates by collecting `init_frames` (default 30) and
34
+ storing the per-pixel median as the baseline. Call `sensor.calibrate()` to
35
+ recalibrate.
36
+
37
+ ## CLI tools
38
+
39
+ All CLI commands are installed as entry points:
40
+
41
+ ```bash
42
+ flexitac-stream --port /dev/ttyUSB0 # stream frames & print stats
43
+ flexitac-heatmap --port /dev/ttyUSB0 # live matplotlib heatmap
44
+ flexitac-flash # flash firmware (auto-detects board)
45
+ flexitac-find-port # identify sensor port by unplug
46
+
47
+ # with uv (no install needed)
48
+ uv run flexitac-stream --port /dev/ttyUSB0
49
+ uv run flexitac-heatmap --port /dev/ttyUSB0
50
+ ```
51
+
52
+ ### `flexitac-stream`
53
+
54
+ Stream frames and print FPS / signal stats:
55
+
56
+ ```bash
57
+ flexitac-stream --port /dev/ttyUSB0 --frames 30
58
+ ```
59
+
60
+ ```
61
+ frame= 10 fps= 85.3 raw_max=104 norm_max=0.000
62
+ frame= 20 fps= 87.1 raw_max=109 norm_max=0.123
63
+ ```
64
+
65
+ What to check:
66
+
67
+ - **`fps`** stabilizes near your expected rate (~100+ fps for a 12x32 sensor
68
+ at 2 Mbps). If it's 0 or you get `TimeoutError`, the firmware isn't sending
69
+ framed data -- confirm `--rows`/`--cols`/`--baud` match what you flashed.
70
+ - **`raw_max`** is in `[0, 255]` and changes when you press the sensor.
71
+ A flat 0 or flat 255 indicates a wiring issue, not a flashing issue.
72
+ - **`norm_max`** rises toward 1.0 under contact and stays near 0 at rest.
73
+
74
+ ### `flexitac-heatmap`
75
+
76
+ Live heatmap visualization (requires `matplotlib` -- install with the `examples` extra):
77
+
78
+ ```bash
79
+ flexitac-heatmap --port /dev/ttyUSB0
80
+ ```
81
+
82
+ Press the sensor pad -- bright spots should track your touch.
83
+
84
+ ### `flexitac-find-port`
85
+
86
+ Not sure which `/dev/tty*` your sensor is on? Run:
87
+
88
+ ```bash
89
+ flexitac-find-port
90
+ ```
91
+
92
+ It snapshots ports, asks you to unplug the sensor, then reports whichever port
93
+ disappeared.
94
+
95
+ ### `flexitac-flash`
96
+
97
+ Requires [`arduino-cli`](https://arduino.github.io/arduino-cli/latest/installation/).
98
+ First-time setup:
99
+
100
+ ```bash
101
+ brew install arduino-cli
102
+ # or use the upstream install script:
103
+ curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
104
+
105
+ # then install the Arduino AVR core
106
+ arduino-cli core update-index
107
+ arduino-cli core install arduino:avr # AVR core for Uno/Nano/Mega/etc.
108
+ ```
109
+
110
+ ```bash
111
+ # auto-detects the port + FQBN if exactly one Arduino is plugged in
112
+ flexitac-flash
113
+
114
+ # override geometry / wiring for non-standard sensors
115
+ flexitac-flash --rows 16 --cols 32 --mux-offset 0
116
+
117
+ # if unable to autodetect, pass --port and --fqbn explicitly:
118
+ flexitac-flash --port /dev/ttyUSB0 --fqbn arduino:avr:uno
119
+ ```
120
+
121
+ Defaults: `rows=12`, `cols=32`, `baud=2000000`, `mux-offset=4` (standard
122
+ FlexiTac 12x32 sensor wired to mux channels 4-15). The firmware is generated
123
+ from `flexitac/firmware/template.ino` by substituting `ROW_COUNT`,
124
+ `COLUMN_COUNT`, `BAUD_RATE`, and `MUX_CHANNEL_OFFSET`. To customize pin
125
+ assignments, edit the template directly.
126
+
127
+ ## Wire protocol
128
+
129
+ Each frame: marker `0xAA 0x55` followed by `rows * cols` uint8 ADC samples,
130
+ streamed continuously at the configured baud rate.
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ uv sync --extra dev
136
+
137
+ make format # ruff format + autofix
138
+ make static-checks # ruff + mypy
139
+ make test # pytest
140
+ ```
@@ -0,0 +1 @@
1
+ """Example scripts for FlexiTac sensors, installable as CLI entry points."""
@@ -0,0 +1,47 @@
1
+ """Stream FlexiTac frames and print FPS / signal stats."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import time
7
+
8
+ from flexitac import FlexiTacSensor
9
+
10
+
11
+ def main() -> int:
12
+ parser = argparse.ArgumentParser(description="Stream FlexiTac frames.")
13
+ parser.add_argument("--port", required=True)
14
+ parser.add_argument("--rows", type=int, default=12)
15
+ parser.add_argument("--cols", type=int, default=32)
16
+ parser.add_argument("--baud", type=int, default=2_000_000)
17
+ parser.add_argument("--frames", type=int, default=0, help="0 = run forever")
18
+ parser.add_argument(
19
+ "--per-row",
20
+ action="store_true",
21
+ help="Print per-row raw mean / baseline (useful when some rows look dead)",
22
+ )
23
+ args = parser.parse_args()
24
+
25
+ started = time.monotonic()
26
+ with FlexiTacSensor(args.port, rows=args.rows, cols=args.cols, baud=args.baud) as sensor:
27
+ sensor.calibrate()
28
+ if args.per_row:
29
+ assert sensor.baseline is not None
30
+ print("baseline per row:", [round(float(v), 1) for v in sensor.baseline.mean(axis=1)])
31
+
32
+ for i, frame in enumerate(sensor, start=1):
33
+ fps = i / max(time.monotonic() - started, 1e-6)
34
+ print(
35
+ f"frame={i:6d} fps={fps:6.1f} raw_max={int(frame.raw.max()):3d} "
36
+ f"norm_max={float(frame.normalized.max()):.3f}"
37
+ )
38
+ if args.per_row and i % 20 == 0:
39
+ row_max = frame.raw.max(axis=1)
40
+ print(" raw_max per row:", [int(v) for v in row_max])
41
+ if args.frames and i >= args.frames:
42
+ break
43
+ return 0
44
+
45
+
46
+ if __name__ == "__main__":
47
+ raise SystemExit(main())
@@ -0,0 +1,61 @@
1
+ """Live heatmap of FlexiTac normalized frames using matplotlib."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import time
7
+
8
+ import matplotlib
9
+ import numpy as np
10
+
11
+ from flexitac import FlexiTacSensor
12
+
13
+ for _backend in ("TkAgg", "Qt5Agg", "MacOSX"):
14
+ try:
15
+ matplotlib.use(_backend)
16
+ break
17
+ except Exception: # noqa: BLE001
18
+ continue
19
+
20
+ from matplotlib import animation, pyplot as plt # noqa: E402
21
+
22
+
23
+ def main() -> int:
24
+ parser = argparse.ArgumentParser(description="Live FlexiTac heatmap.")
25
+ parser.add_argument("--port", required=True)
26
+ parser.add_argument("--rows", type=int, default=12)
27
+ parser.add_argument("--cols", type=int, default=32)
28
+ parser.add_argument("--baud", type=int, default=2_000_000)
29
+ parser.add_argument("--cmap", default="viridis")
30
+ args = parser.parse_args()
31
+
32
+ sensor = FlexiTacSensor(args.port, rows=args.rows, cols=args.cols, baud=args.baud).open()
33
+ sensor.calibrate()
34
+
35
+ fig, ax = plt.subplots(figsize=(8, 4.5))
36
+ im = ax.imshow(np.zeros((args.rows, args.cols), dtype=np.float32), cmap=args.cmap, vmin=0.0, vmax=1.0)
37
+ fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
38
+ title = ax.set_title("FlexiTac")
39
+
40
+ started = time.monotonic()
41
+ count = 0
42
+
43
+ def update(_: int) -> list:
44
+ nonlocal count
45
+ frame = sensor.read_latest()
46
+ count += 1
47
+ im.set_data(frame.normalized)
48
+ fps = count / max(time.monotonic() - started, 1e-6)
49
+ title.set_text(f"FlexiTac | seq={frame.seq} fps={fps:.1f}")
50
+ return [im, title]
51
+
52
+ _anim = animation.FuncAnimation(fig, update, interval=16, blit=True, cache_frame_data=False)
53
+ try:
54
+ plt.show()
55
+ finally:
56
+ sensor.close()
57
+ return 0
58
+
59
+
60
+ if __name__ == "__main__":
61
+ raise SystemExit(main())
@@ -0,0 +1,6 @@
1
+ """Public package interface for flexitac."""
2
+
3
+ from flexitac.sensor import FlexiTacFrame, FlexiTacSensor
4
+
5
+ __all__ = ["FlexiTacFrame", "FlexiTacSensor"]
6
+ __version__ = "0.2.0"
@@ -0,0 +1,55 @@
1
+ """Identify the FlexiTac serial port by snapshotting before/after unplug."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import platform
6
+ import time
7
+ from pathlib import Path
8
+
9
+ from serial.tools import list_ports
10
+
11
+ from flexitac.logging_utils import configure_logging
12
+
13
+
14
+ def list_serial_ports() -> set[str]:
15
+ """Return currently available serial port device paths."""
16
+ if platform.system() == "Windows":
17
+ return {port.device for port in list_ports.comports()}
18
+ return {str(path) for path in Path("/dev").glob("tty*")}
19
+
20
+
21
+ def find_port(*, settle_s: float = 0.5) -> str:
22
+ """Prompt the user to unplug their sensor and return the port that vanished."""
23
+ logger = configure_logging(verbose=False)
24
+
25
+ before = list_serial_ports()
26
+ logger.info("Detected %d serial ports.", len(before))
27
+ input("Unplug the FlexiTac USB cable, then press Enter... ")
28
+
29
+ time.sleep(settle_s)
30
+ after = list_serial_ports()
31
+ diff = sorted(before - after)
32
+
33
+ if len(diff) == 1:
34
+ port = diff[0]
35
+ logger.info("FlexiTac port: %s", port)
36
+ logger.info("Reconnect the USB cable.")
37
+ return port
38
+ if not diff:
39
+ raise OSError("No port disappeared. Make sure you actually unplugged the device.")
40
+ raise OSError(f"Multiple ports disappeared: {diff}. Unplug only the FlexiTac.")
41
+
42
+
43
+ def main(argv: list[str] | None = None) -> int:
44
+ """CLI entrypoint for ``flexitac-find-port``."""
45
+ del argv
46
+ try:
47
+ find_port()
48
+ return 0
49
+ except OSError as exc:
50
+ configure_logging(verbose=False).error("%s", exc)
51
+ return 1
52
+
53
+
54
+ if __name__ == "__main__":
55
+ raise SystemExit(main())
@@ -0,0 +1 @@
1
+ """Firmware helpers for template rendering and profile metadata."""
@@ -0,0 +1,116 @@
1
+ #define BAUD_RATE 2000000
2
+ #define ROW_COUNT 12
3
+ #define COLUMN_COUNT 32
4
+
5
+ #define PIN_ADC_INPUT A0
6
+ #define PIN_SHIFT_REGISTER_DATA 2
7
+ #define PIN_SHIFT_REGISTER_CLOCK 3
8
+ #define PIN_MUX_CHANNEL_0 4
9
+ #define PIN_MUX_CHANNEL_1 5
10
+ #define PIN_MUX_CHANNEL_2 6
11
+ #define PIN_MUX_CHANNEL_3 7
12
+ #define PIN_MUX_INHIBIT_0 8
13
+ #define PIN_MUX_INHIBIT_1 9
14
+
15
+ #define START_MARKER_0 0xAA
16
+ #define START_MARKER_1 0x55
17
+
18
+ #define SET_SR_DATA_HIGH() PORTD|=B00000100
19
+ #define SET_SR_DATA_LOW() PORTD&=~B00000100
20
+ #define SET_SR_CLK_HIGH() PORTD|=B00001000
21
+ #define SET_SR_CLK_LOW() PORTD&=~B00001000
22
+
23
+ #define ROWS_PER_MUX 16
24
+ #define MUX_COUNT 1
25
+ #define CHANNEL_PINS_PER_MUX 4
26
+ #define MUX_CHANNEL_OFFSET 4
27
+
28
+ #ifndef cbi
29
+ #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
30
+ #endif
31
+ #ifndef sbi
32
+ #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
33
+ #endif
34
+
35
+ int current_enabled_mux = MUX_COUNT - 1;
36
+
37
+ void setup()
38
+ {
39
+ Serial.begin(BAUD_RATE);
40
+ pinMode(PIN_ADC_INPUT, INPUT);
41
+ pinMode(PIN_SHIFT_REGISTER_DATA, OUTPUT);
42
+ pinMode(PIN_SHIFT_REGISTER_CLOCK, OUTPUT);
43
+ pinMode(PIN_MUX_CHANNEL_0, OUTPUT);
44
+ pinMode(PIN_MUX_CHANNEL_1, OUTPUT);
45
+ pinMode(PIN_MUX_CHANNEL_2, OUTPUT);
46
+ pinMode(PIN_MUX_CHANNEL_3, OUTPUT);
47
+ pinMode(PIN_MUX_INHIBIT_0, OUTPUT);
48
+ pinMode(PIN_MUX_INHIBIT_1, OUTPUT);
49
+
50
+ sbi(ADCSRA, ADPS2);
51
+ cbi(ADCSRA, ADPS1);
52
+ cbi(ADCSRA, ADPS0);
53
+ }
54
+
55
+ void loop()
56
+ {
57
+ Serial.write(START_MARKER_0);
58
+ Serial.write(START_MARKER_1);
59
+
60
+ for (int i = 0; i < ROW_COUNT; i++)
61
+ {
62
+ setRow(i);
63
+ shiftColumn(true);
64
+ shiftColumn(false);
65
+
66
+ for (int j = 0; j < COLUMN_COUNT; j++)
67
+ {
68
+ int raw_reading = analogRead(PIN_ADC_INPUT);
69
+ byte send_reading = (byte)(raw_reading >> 2);
70
+
71
+ Serial.write(send_reading);
72
+ shiftColumn(false);
73
+ }
74
+ }
75
+ }
76
+
77
+ void setRow(int row_number)
78
+ {
79
+ if ((row_number % ROWS_PER_MUX) == 0)
80
+ {
81
+ digitalWrite(PIN_MUX_INHIBIT_0 + current_enabled_mux, HIGH);
82
+ current_enabled_mux++;
83
+ if (current_enabled_mux >= MUX_COUNT)
84
+ {
85
+ current_enabled_mux = 0;
86
+ }
87
+ digitalWrite(PIN_MUX_INHIBIT_0 + current_enabled_mux, LOW);
88
+ }
89
+
90
+ int mux_channel = row_number + MUX_CHANNEL_OFFSET;
91
+ for (int i = 0; i < CHANNEL_PINS_PER_MUX; i++)
92
+ {
93
+ if (bitRead(mux_channel, i))
94
+ {
95
+ digitalWrite(PIN_MUX_CHANNEL_0 + i, HIGH);
96
+ }
97
+ else
98
+ {
99
+ digitalWrite(PIN_MUX_CHANNEL_0 + i, LOW);
100
+ }
101
+ }
102
+ }
103
+
104
+ void shiftColumn(boolean is_first)
105
+ {
106
+ if (is_first)
107
+ {
108
+ SET_SR_DATA_HIGH();
109
+ }
110
+ SET_SR_CLK_HIGH();
111
+ SET_SR_CLK_LOW();
112
+ if (is_first)
113
+ {
114
+ SET_SR_DATA_LOW();
115
+ }
116
+ }