From cc66fa09cd3e66e3b8a0acf29193e9137503b645 Mon Sep 17 00:00:00 2001
From: MathiasMahn <53939819+MathiasMahn@users.noreply.github.com>
Date: Thu, 8 Jan 2026 16:41:39 +0100
Subject: [PATCH 1/8] adding event type decoding to FSMThread
---
.gitignore | 3 +
bpod_core/bpod.py | 7 +-
examples/hello_world.ipynb | 222 ++++++++++++++++++++++++++++++++++++
examples/minimal_example.py | 67 +++++++++++
4 files changed, 298 insertions(+), 1 deletion(-)
create mode 100644 examples/hello_world.ipynb
create mode 100644 examples/minimal_example.py
diff --git a/.gitignore b/.gitignore
index 65de09cc..24aa61a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -193,6 +193,9 @@ target/
profile_default/
ipython_config.py
+# LLM files
+.repotomdrc
+
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index fc6e539b..4fc09462 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -169,6 +169,7 @@ def __init__( # noqa: PLR0913
softcode_handler: Callable,
state_transitions: NDArray[np.uint8],
use_back_op: bool,
+ event_names: list[str],
) -> None:
"""
Initialize the FSMThread.
@@ -199,6 +200,7 @@ def __init__( # noqa: PLR0913
self._softcode_handler = softcode_handler
self._state_transitions = state_transitions
self._use_back_op = use_back_op
+ self._event_names = event_names
def stop(self) -> None:
self._stop_event.set()
@@ -258,7 +260,9 @@ def run(self) -> None:
events = event_data_view[:param]
for event in events:
if debug:
- logger.debug('%d µs: Event %d', micros, event)
+ if not(event == 255): # exit event
+ event_name = self._event_names[event] if event < len(self._event_names) else "Unknown"
+ logger.debug('%d µs: Event: %s (%d)', micros, event_name, event)
# TODO: handle event
# handle state transitions / exit event
@@ -1301,6 +1305,7 @@ def _run_state_machine(self, *, blocking: bool) -> None:
self._softcode_handler,
self._state_transitions,
self._use_back_op,
+ self.event_names,
)
self._fsm_thread.start()
self._waiting_for_confirmation = False
diff --git a/examples/hello_world.ipynb b/examples/hello_world.ipynb
new file mode 100644
index 00000000..e8a80b05
--- /dev/null
+++ b/examples/hello_world.ipynb
@@ -0,0 +1,222 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e8a973cd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from bpod_core.fsm import StateMachine"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09331e75",
+ "metadata": {},
+ "source": [
+ "### Define a state machine"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "1dfc3950",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "fsm = StateMachine()\n",
+ "\n",
+ "fsm.set_global_timer(\n",
+ " index=1,\n",
+ " duration=5\n",
+ ")\n",
+ "\n",
+ "\n",
+ "fsm.add_state(\n",
+ " name='StartGlobalTimer',\n",
+ " timer=0.25,\n",
+ " transitions={'Tup': 'Port1Light'},\n",
+ " actions={'GlobalTimerTrig': 1},\n",
+ ")\n",
+ "fsm.add_state(\n",
+ " name='Port1Light',\n",
+ " timer=0.25,\n",
+ " transitions={\n",
+ " 'Tup': 'Port3Light',\n",
+ " 'GlobalTimer1_End': '>exit',\n",
+ " },\n",
+ " actions={'PWM1': 255},\n",
+ ")\n",
+ "fsm.add_state(\n",
+ " name='Port3Light',\n",
+ " timer=0.25,\n",
+ " transitions={\n",
+ " 'Tup': 'Port1Light',\n",
+ " 'GlobalTimer1_End': '>exit',\n",
+ " },\n",
+ " actions={'PWM3': 255},\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a32cfda8",
+ "metadata": {},
+ "source": [
+ "### Display the statemechine for easy verification"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "2c77e81e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fsm.to_digraph() "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ecf44170",
+ "metadata": {},
+ "source": [
+ "### Running a state machine / bpod communication does not seem to work from a jupyter notebook..."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "bpod-core",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/minimal_example.py b/examples/minimal_example.py
new file mode 100644
index 00000000..6e1559f1
--- /dev/null
+++ b/examples/minimal_example.py
@@ -0,0 +1,67 @@
+from bpod_core.fsm import StateMachine
+from bpod_core.bpod import Bpod
+import logging
+import sys
+
+
+LOG_FILE = "bpod_debug.log"
+
+logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
+ logging.StreamHandler(sys.stdout) # This also keeps logs appearing in the terminal
+ ]
+)
+
+# 3. Specifically ensure the bpod_core library is set to DEBUG
+logging.getLogger('bpod_core').setLevel(logging.DEBUG)
+
+print(f"Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}")
+
+fsm = StateMachine()
+
+fsm.set_global_timer(
+ index=1,
+ duration=5
+)
+
+
+fsm.add_state(
+ name='StartGlobalTimer',
+ timer=0.25,
+ transitions={'Tup': 'Port1Light'},
+ actions={'GlobalTimerTrig': 1},
+)
+fsm.add_state(
+ name='Port1Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port3Light',
+ 'GlobalTimer1_End': '>exit',
+ },
+ actions={'PWM1': 255},
+)
+fsm.add_state(
+ name='Port3Light',
+ timer=0.25,
+ transitions={
+ 'Tup': '>exit',
+ 'GlobalTimer1_End': '>exit',
+ },
+ actions={'PWM3': 255},
+)
+
+
+
+with Bpod() as bpod:
+ print(f"Connected to Bpod!")
+ print(f"Found Bpod on port {bpod.serial0.port}")
+ print(f"Firmware Version: {bpod.version.firmware}")
+ print(f"Hardware Version: {bpod.version.machine}")
+ print("Send State machine.")
+ bpod.send_state_machine(fsm)
+ print("Run State machine.")
+ bpod.run_state_machine()
+ print("State machine finished.")
\ No newline at end of file
From 3471d866f066662b45d52efcbdd50cd9909818fc Mon Sep 17 00:00:00 2001
From: MathiasMahn <53939819+MathiasMahn@users.noreply.github.com>
Date: Fri, 9 Jan 2026 13:23:02 +0100
Subject: [PATCH 2/8] additional debugging and working global counter SM and
run added
---
bpod_core/bpod.py | 10 +++++
examples/global_timers.py | 39 +++++++++++++++++++
...=> minimal_example_run_global_timer_SM.py} | 10 ++---
3 files changed, 54 insertions(+), 5 deletions(-)
create mode 100644 examples/global_timers.py
rename examples/{minimal_example.py => minimal_example_run_global_timer_SM.py} (85%)
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index 4fc09462..175b2835 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -827,6 +827,8 @@ def _compile_output_actions(self) -> None:
if self.version.machine == 4:
self.actions.extend(['AnalogThreshEnable', 'AnalogThreshDisable'])
+
+
@property
def port(self) -> str | None:
"""The port of the Bpod's primary serial device."""
@@ -895,6 +897,12 @@ def update_modules(self) -> None:
self._compile_event_names()
self._compile_output_actions()
+ def load_serial_message(self, module_index, message_id, message_bytes):
+ # Format: ord('L'), module_index(0-based), n_messages(1), msg_id, msg_len, msg_payload
+ header = struct.pack(' None:
"""
Validate the provided state machine for compatibility with the hardware.
@@ -1265,6 +1273,8 @@ def append_events(event0: str, event1: str) -> None:
f'exit', # this is 1 indexed
+ },
+ actions={'PWM1': 255},
+)
+fsm.add_state(
+ name='Port3Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port1Light',
+ 'GlobalTimer1_End': '>exit', # this is 1 indexed
+ },
+ actions={'PWM3': 255},
+)
\ No newline at end of file
diff --git a/examples/minimal_example.py b/examples/minimal_example_run_global_timer_SM.py
similarity index 85%
rename from examples/minimal_example.py
rename to examples/minimal_example_run_global_timer_SM.py
index 6e1559f1..36e2dfbe 100644
--- a/examples/minimal_example.py
+++ b/examples/minimal_example_run_global_timer_SM.py
@@ -23,7 +23,7 @@
fsm = StateMachine()
fsm.set_global_timer(
- index=1,
+ index=0, # this is 0 indexed
duration=5
)
@@ -32,14 +32,14 @@
name='StartGlobalTimer',
timer=0.25,
transitions={'Tup': 'Port1Light'},
- actions={'GlobalTimerTrig': 1},
+ actions={'GlobalTimerTrig': 1}, # this is 1 indexed
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalTimer1_End': '>exit',
+ 'GlobalTimer1_End': '>exit', # this is 1 indexed
},
actions={'PWM1': 255},
)
@@ -47,8 +47,8 @@
name='Port3Light',
timer=0.25,
transitions={
- 'Tup': '>exit',
- 'GlobalTimer1_End': '>exit',
+ 'Tup': 'Port1Light',
+ 'GlobalTimer1_End': '>exit', # this is 1 indexed
},
actions={'PWM3': 255},
)
From df7a55151c78204bcb62387e940ded9029adb9b4 Mon Sep 17 00:00:00 2001
From: MathiasMahn <53939819+MathiasMahn@users.noreply.github.com>
Date: Fri, 9 Jan 2026 14:04:20 +0100
Subject: [PATCH 3/8] extended debug logging to include event_names. Fixed
global counter index in example
---
bpod_core/bpod.py | 1 +
bpod_core/fsm.py | 2 +-
...ld.ipynb => Graph_global_counter_SM.ipynb} | 0
examples/global_counters.py | 8 +--
examples/global_timers.py | 8 +--
.../minimal_example_run_global_counter_SM.py | 72 +++++++++++++++++++
6 files changed, 82 insertions(+), 9 deletions(-)
rename examples/{hello_world.ipynb => Graph_global_counter_SM.ipynb} (100%)
create mode 100644 examples/minimal_example_run_global_counter_SM.py
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index 175b2835..a30cbe09 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -801,6 +801,7 @@ def _compile_event_names(self) -> None:
]:
self.event_names.extend(event_name.format(i) for i in range(n))
self.event_names.append('Tup')
+ logger.debug('Compiled event names: %s', self.event_names)
def _compile_output_actions(self) -> None:
"""Compile the list of output actions supported by the Bpod hardware."""
diff --git a/bpod_core/fsm.py b/bpod_core/fsm.py
index 9a01ac7c..a5db2124 100644
--- a/bpod_core/fsm.py
+++ b/bpod_core/fsm.py
@@ -462,7 +462,7 @@ def set_global_counter(
threshold: GlobalCounterThreshold,
) -> None:
"""
- Configure a global timer with the specified parameters.
+ Configure a global counter with the specified parameters.
Parameters
----------
diff --git a/examples/hello_world.ipynb b/examples/Graph_global_counter_SM.ipynb
similarity index 100%
rename from examples/hello_world.ipynb
rename to examples/Graph_global_counter_SM.ipynb
diff --git a/examples/global_counters.py b/examples/global_counters.py
index 3ce22293..38d0a36b 100644
--- a/examples/global_counters.py
+++ b/examples/global_counters.py
@@ -9,7 +9,7 @@
fsm = StateMachine()
fsm.set_global_counter(
- index=0,
+ index=0, # this is zero based, so this is Global Counter 1
event='Port1High',
threshold=5,
)
@@ -23,14 +23,14 @@
fsm.add_state(
name='ResetGlobalCounter',
transitions={'Tup': 'Port1Light'},
- actions={'GlobalCounterReset': 0},
+ actions={'GlobalCounterReset': 1},
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalCounter0_End': '>exit',
+ 'GlobalCounter1_End': '>exit',
},
actions={'PWM1': 255},
)
@@ -39,7 +39,7 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalCounter0_End': '>exit',
+ 'GlobalCounter1_End': '>exit',
},
actions={'PWM3': 255},
)
diff --git a/examples/global_timers.py b/examples/global_timers.py
index 0df414dc..69890c87 100644
--- a/examples/global_timers.py
+++ b/examples/global_timers.py
@@ -8,7 +8,7 @@
fsm = StateMachine()
fsm.set_global_timer(
- index=0, # this is 0 indexed
+ index=0, # this is zero based, so this is Global Timer 1
duration=5
)
@@ -17,14 +17,14 @@
name='StartGlobalTimer',
timer=0.25,
transitions={'Tup': 'Port1Light'},
- actions={'GlobalTimerTrig': 1}, # this is 1 indexed
+ actions={'GlobalTimerTrig': 1}, # this is 1 based
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
+ 'GlobalTimer1_End': '>exit', # this is 1 based
},
actions={'PWM1': 255},
)
@@ -33,7 +33,7 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
+ 'GlobalTimer1_End': '>exit', # this is 1 based
},
actions={'PWM3': 255},
)
\ No newline at end of file
diff --git a/examples/minimal_example_run_global_counter_SM.py b/examples/minimal_example_run_global_counter_SM.py
new file mode 100644
index 00000000..f275fc53
--- /dev/null
+++ b/examples/minimal_example_run_global_counter_SM.py
@@ -0,0 +1,72 @@
+from bpod_core.fsm import StateMachine
+from bpod_core.bpod import Bpod
+import logging
+import sys
+
+
+LOG_FILE = "bpod_debug.log"
+
+logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
+ logging.StreamHandler(sys.stdout) # This also keeps logs appearing in the terminal
+ ]
+)
+
+# 3. Specifically ensure the bpod_core library is set to DEBUG
+logging.getLogger('bpod_core').setLevel(logging.DEBUG)
+
+print(f"Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}")
+
+fsm = StateMachine()
+
+fsm.set_global_counter(
+ index=0, # this is zero based, so this is Global Counter 1
+ event='BNC1_High',
+ threshold=5,
+)
+
+fsm.add_state(
+ name='InitialDelay',
+ timer=2,
+ transitions={'Tup': 'ResetGlobalCounter'},
+ actions={'PWM2': 255},
+)
+fsm.add_state(
+ name='ResetGlobalCounter',
+ transitions={'Tup': 'Port1Light'},
+ actions={'GlobalCounterReset': 1},
+)
+fsm.add_state(
+ name='Port1Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port3Light',
+ 'GlobalCounter1_End': '>exit',
+ },
+ actions={'PWM1': 255},
+)
+fsm.add_state(
+ name='Port3Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port1Light',
+ 'GlobalCounter1_End': '>exit',
+ },
+ actions={'PWM3': 255},
+)
+
+
+
+with Bpod() as bpod:
+ print(f"Connected to Bpod!")
+ print(f"Found Bpod on port {bpod.serial0.port}")
+ print(f"Firmware Version: {bpod.version.firmware}")
+ print(f"Hardware Version: {bpod.version.machine}")
+ print("Send State machine.")
+ bpod.send_state_machine(fsm)
+ print("Run State machine.")
+ bpod.run_state_machine()
+ print("State machine finished.")
\ No newline at end of file
From 0acc9d7ab5fb7b445ff2c8c93c32912ee84eba9a Mon Sep 17 00:00:00 2001
From: Florian Rau
Date: Tue, 27 Jan 2026 18:00:00 +0000
Subject: [PATCH 4/8] refactor `load_serial_message` and move to `bpod.Module`
class
---
bpod_core/bpod.py | 55 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 49 insertions(+), 6 deletions(-)
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index a30cbe09..ab728ae2 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -898,12 +898,6 @@ def update_modules(self) -> None:
self._compile_event_names()
self._compile_output_actions()
- def load_serial_message(self, module_index, message_id, message_bytes):
- # Format: ord('L'), module_index(0-based), n_messages(1), msg_id, msg_len, msg_payload
- header = struct.pack(' None:
"""
Validate the provided state machine for compatibility with the hardware.
@@ -1570,6 +1564,55 @@ def relay(self, state: bool) -> None:
"""The current state of the serial relay."""
self.set_relay(state)
+ @validate_call
+ def load_serial_message(
+ self,
+ message_id: int,
+ message_bytes: bytes,
+ ) -> bool:
+ """
+ Load a serial message targeting the module.
+
+ Serial messages are byte sequences targeting a specific module that can be
+ triggered as output actions during a state machine run. Each message is
+ identified by a ``message_id``.
+
+ Parameters
+ ----------
+ message_id : int
+ Identifier for the message, in the range ``[0, 254]``.
+ message_bytes : bytes
+ The message payload (1 to 3 bytes).
+
+ Returns
+ -------
+ bool
+ :obj:`True` if the Bpod acknowledged the message, :obj:`False` otherwise.
+
+ Raises
+ ------
+ ValidationError
+ If the provided parameters cannot be validated or coerced to the expected
+ type.
+ ValueError
+ If ```message_id``, or ``message_bytes`` length is out of range.
+ """
+ if not (0 <= message_id <= 254):
+ raise ValueError('Message ID must be between 0 and 254')
+ if not (1 <= (message_length := len(message_bytes)) <= 3):
+ raise ValueError('Message must be between 1 and 3 bytes long')
+
+ self._bpod.serial0.write_struct(
+ f'
Date: Tue, 27 Jan 2026 18:03:43 +0000
Subject: [PATCH 5/8] please ruff
---
bpod_core/bpod.py | 19 +++++----
examples/global_counters.py | 2 +-
examples/global_timers.py | 10 ++---
.../minimal_example_run_global_counter_SM.py | 31 ++++++++-------
.../minimal_example_run_global_timer_SM.py | 39 ++++++++++---------
5 files changed, 53 insertions(+), 48 deletions(-)
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index ab728ae2..81147b8c 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -259,10 +259,13 @@ def run(self) -> None:
# handle each event
events = event_data_view[:param]
for event in events:
- if debug:
- if not(event == 255): # exit event
- event_name = self._event_names[event] if event < len(self._event_names) else "Unknown"
- logger.debug('%d µs: Event: %s (%d)', micros, event_name, event)
+ if debug and event != 255: # exit event
+ event_name = (
+ self._event_names[event]
+ if event < len(self._event_names)
+ else 'Unknown'
+ )
+ logger.debug('%d µs: Event: %s (%d)', micros, event_name, event)
# TODO: handle event
# handle state transitions / exit event
@@ -828,8 +831,6 @@ def _compile_output_actions(self) -> None:
if self.version.machine == 4:
self.actions.extend(['AnalogThreshEnable', 'AnalogThreshDisable'])
-
-
@property
def port(self) -> str | None:
"""The port of the Bpod's primary serial device."""
@@ -1268,8 +1269,10 @@ def append_events(event0: str, event1: str) -> None:
f'exit', # this is 1 based
+ 'GlobalTimer1_End': '>exit', # this is 1 based
},
actions={'PWM1': 255},
)
@@ -33,7 +33,7 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalTimer1_End': '>exit', # this is 1 based
+ 'GlobalTimer1_End': '>exit', # this is 1 based
},
actions={'PWM3': 255},
-)
\ No newline at end of file
+)
diff --git a/examples/minimal_example_run_global_counter_SM.py b/examples/minimal_example_run_global_counter_SM.py
index f275fc53..03ee238c 100644
--- a/examples/minimal_example_run_global_counter_SM.py
+++ b/examples/minimal_example_run_global_counter_SM.py
@@ -1,29 +1,31 @@
-from bpod_core.fsm import StateMachine
-from bpod_core.bpod import Bpod
import logging
import sys
+from bpod_core.bpod import Bpod
+from bpod_core.fsm import StateMachine
-LOG_FILE = "bpod_debug.log"
+LOG_FILE = 'bpod_debug.log'
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
- logging.StreamHandler(sys.stdout) # This also keeps logs appearing in the terminal
- ]
+ logging.StreamHandler(
+ sys.stdout
+ ), # This also keeps logs appearing in the terminal
+ ],
)
# 3. Specifically ensure the bpod_core library is set to DEBUG
logging.getLogger('bpod_core').setLevel(logging.DEBUG)
-print(f"Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}")
+print(f'Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}')
fsm = StateMachine()
fsm.set_global_counter(
- index=0, # this is zero based, so this is Global Counter 1
+ index=0, # this is zero based, so this is Global Counter 1
event='BNC1_High',
threshold=5,
)
@@ -59,14 +61,13 @@
)
-
with Bpod() as bpod:
- print(f"Connected to Bpod!")
- print(f"Found Bpod on port {bpod.serial0.port}")
- print(f"Firmware Version: {bpod.version.firmware}")
- print(f"Hardware Version: {bpod.version.machine}")
- print("Send State machine.")
+ print('Connected to Bpod!')
+ print(f'Found Bpod on port {bpod.serial0.port}')
+ print(f'Firmware Version: {bpod.version.firmware}')
+ print(f'Hardware Version: {bpod.version.machine}')
+ print('Send State machine.')
bpod.send_state_machine(fsm)
- print("Run State machine.")
+ print('Run State machine.')
bpod.run_state_machine()
- print("State machine finished.")
\ No newline at end of file
+ print('State machine finished.')
diff --git a/examples/minimal_example_run_global_timer_SM.py b/examples/minimal_example_run_global_timer_SM.py
index 36e2dfbe..f56dedbc 100644
--- a/examples/minimal_example_run_global_timer_SM.py
+++ b/examples/minimal_example_run_global_timer_SM.py
@@ -1,30 +1,32 @@
-from bpod_core.fsm import StateMachine
-from bpod_core.bpod import Bpod
import logging
import sys
+from bpod_core.bpod import Bpod
+from bpod_core.fsm import StateMachine
-LOG_FILE = "bpod_debug.log"
+LOG_FILE = 'bpod_debug.log'
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
- logging.StreamHandler(sys.stdout) # This also keeps logs appearing in the terminal
- ]
+ logging.StreamHandler(
+ sys.stdout
+ ), # This also keeps logs appearing in the terminal
+ ],
)
# 3. Specifically ensure the bpod_core library is set to DEBUG
logging.getLogger('bpod_core').setLevel(logging.DEBUG)
-print(f"Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}")
+print(f'Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}')
fsm = StateMachine()
fsm.set_global_timer(
- index=0, # this is 0 indexed
- duration=5
+ index=0, # this is 0 indexed
+ duration=5,
)
@@ -32,14 +34,14 @@
name='StartGlobalTimer',
timer=0.25,
transitions={'Tup': 'Port1Light'},
- actions={'GlobalTimerTrig': 1}, # this is 1 indexed
+ actions={'GlobalTimerTrig': 1}, # this is 1 indexed
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
+ 'GlobalTimer1_End': '>exit', # this is 1 indexed
},
actions={'PWM1': 255},
)
@@ -48,20 +50,19 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
+ 'GlobalTimer1_End': '>exit', # this is 1 indexed
},
actions={'PWM3': 255},
)
-
with Bpod() as bpod:
- print(f"Connected to Bpod!")
- print(f"Found Bpod on port {bpod.serial0.port}")
- print(f"Firmware Version: {bpod.version.firmware}")
- print(f"Hardware Version: {bpod.version.machine}")
- print("Send State machine.")
+ print('Connected to Bpod!')
+ print(f'Found Bpod on port {bpod.serial0.port}')
+ print(f'Firmware Version: {bpod.version.firmware}')
+ print(f'Hardware Version: {bpod.version.machine}')
+ print('Send State machine.')
bpod.send_state_machine(fsm)
- print("Run State machine.")
+ print('Run State machine.')
bpod.run_state_machine()
- print("State machine finished.")
\ No newline at end of file
+ print('State machine finished.')
From c3d0d097757b38bb04fbfe0e14dfad53099d2f5f Mon Sep 17 00:00:00 2001
From: Florian Rau
Date: Tue, 27 Jan 2026 19:19:25 +0000
Subject: [PATCH 6/8] reorganize examples
---
docs/source/conf.py | 4 +-
examples/Graph_global_counter_SM.ipynb | 222 ------------------
.../graph_state_machine.ipynb | 99 ++++++++
.../minimal_example_run_global_counter_SM.py | 73 ------
.../minimal_example_run_global_timer_SM.py | 68 ------
examples/minimal_examples/run_global_timer.py | 49 ++++
.../{ => state_machines}/back_operator.py | 0
.../bnc_triggered_state_change.py | 0
examples/{ => state_machines}/conditions.py | 0
.../{ => state_machines}/global_counters.py | 8 +-
.../{ => state_machines}/global_timers.py | 9 +-
.../{ => state_machines}/light_chasing.py | 0
examples/{ => state_machines}/two_choice.py | 0
pyproject.toml | 2 +-
14 files changed, 159 insertions(+), 375 deletions(-)
delete mode 100644 examples/Graph_global_counter_SM.ipynb
create mode 100644 examples/ipython_notebooks/graph_state_machine.ipynb
delete mode 100644 examples/minimal_example_run_global_counter_SM.py
delete mode 100644 examples/minimal_example_run_global_timer_SM.py
create mode 100644 examples/minimal_examples/run_global_timer.py
rename examples/{ => state_machines}/back_operator.py (100%)
rename examples/{ => state_machines}/bnc_triggered_state_change.py (100%)
rename examples/{ => state_machines}/conditions.py (100%)
rename examples/{ => state_machines}/global_counters.py (81%)
rename examples/{ => state_machines}/global_timers.py (71%)
rename examples/{ => state_machines}/light_chasing.py (100%)
rename examples/{ => state_machines}/two_choice.py (100%)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 4be9786f..7684a527 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -19,7 +19,7 @@ def generate_fsm_examples(app):
return
# Create docs/source/state_machines/examples/ with one page per example
- examples_source_path = project_root / 'examples'
+ examples_source_path = project_root / 'examples' / 'state_machines'
examples_target_path = docs_source_path / 'state_machines' / 'examples'
examples_target_path.mkdir(parents=True, exist_ok=True)
example_files = sorted(examples_source_path.glob('*.py'), key=lambda f: f.name)
@@ -63,7 +63,7 @@ def generate_fsm_examples(app):
'',
' .. tab-item:: Python',
'',
- f' .. literalinclude:: ../../../../examples/{fn.name}',
+ f' .. literalinclude:: ../../../../examples/state_machines/{fn.name}',
' :language: python',
' :start-at: from bpod_core.',
'',
diff --git a/examples/Graph_global_counter_SM.ipynb b/examples/Graph_global_counter_SM.ipynb
deleted file mode 100644
index e8a80b05..00000000
--- a/examples/Graph_global_counter_SM.ipynb
+++ /dev/null
@@ -1,222 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e8a973cd",
- "metadata": {},
- "outputs": [],
- "source": [
- "from bpod_core.fsm import StateMachine"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "09331e75",
- "metadata": {},
- "source": [
- "### Define a state machine"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "1dfc3950",
- "metadata": {},
- "outputs": [],
- "source": [
- "fsm = StateMachine()\n",
- "\n",
- "fsm.set_global_timer(\n",
- " index=1,\n",
- " duration=5\n",
- ")\n",
- "\n",
- "\n",
- "fsm.add_state(\n",
- " name='StartGlobalTimer',\n",
- " timer=0.25,\n",
- " transitions={'Tup': 'Port1Light'},\n",
- " actions={'GlobalTimerTrig': 1},\n",
- ")\n",
- "fsm.add_state(\n",
- " name='Port1Light',\n",
- " timer=0.25,\n",
- " transitions={\n",
- " 'Tup': 'Port3Light',\n",
- " 'GlobalTimer1_End': '>exit',\n",
- " },\n",
- " actions={'PWM1': 255},\n",
- ")\n",
- "fsm.add_state(\n",
- " name='Port3Light',\n",
- " timer=0.25,\n",
- " transitions={\n",
- " 'Tup': 'Port1Light',\n",
- " 'GlobalTimer1_End': '>exit',\n",
- " },\n",
- " actions={'PWM3': 255},\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a32cfda8",
- "metadata": {},
- "source": [
- "### Display the statemechine for easy verification"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "2c77e81e",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/svg+xml": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "fsm.to_digraph() "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ecf44170",
- "metadata": {},
- "source": [
- "### Running a state machine / bpod communication does not seem to work from a jupyter notebook..."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "bpod-core",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.12.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/ipython_notebooks/graph_state_machine.ipynb b/examples/ipython_notebooks/graph_state_machine.ipynb
new file mode 100644
index 00000000..180d71fb
--- /dev/null
+++ b/examples/ipython_notebooks/graph_state_machine.ipynb
@@ -0,0 +1,99 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "id": "e8a973cd",
+ "metadata": {},
+ "source": [
+ "from bpod_core.fsm import StateMachine"
+ ],
+ "outputs": [],
+ "execution_count": null
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09331e75",
+ "metadata": {},
+ "source": [
+ "### Define a state machine"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "id": "1dfc3950",
+ "metadata": {},
+ "source": [
+ "fsm = StateMachine()\n",
+ "\n",
+ "fsm.set_global_timer(\n",
+ " index=0,\n",
+ " duration=5\n",
+ ")\n",
+ "\n",
+ "fsm.add_state(\n",
+ " name='StartGlobalTimer',\n",
+ " timer=0.25,\n",
+ " transitions={'Tup': 'Port1Light'},\n",
+ " actions={'GlobalTimerTrig': 0},\n",
+ ")\n",
+ "fsm.add_state(\n",
+ " name='Port1Light',\n",
+ " timer=0.25,\n",
+ " transitions={\n",
+ " 'Tup': 'Port3Light',\n",
+ " 'GlobalTimer0_End': '>exit',\n",
+ " },\n",
+ " actions={'PWM1': 255},\n",
+ ")\n",
+ "fsm.add_state(\n",
+ " name='Port3Light',\n",
+ " timer=0.25,\n",
+ " transitions={\n",
+ " 'Tup': 'Port1Light',\n",
+ " 'GlobalTimer0_End': '>exit',\n",
+ " },\n",
+ " actions={'PWM3': 255},\n",
+ ")"
+ ],
+ "outputs": [],
+ "execution_count": null
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a32cfda8",
+ "metadata": {},
+ "source": [
+ "### Display the statemechine for easy verification"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "id": "2c77e81e",
+ "metadata": {},
+ "source": "fsm.to_digraph()",
+ "outputs": [],
+ "execution_count": null
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "bpod-core",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/minimal_example_run_global_counter_SM.py b/examples/minimal_example_run_global_counter_SM.py
deleted file mode 100644
index 03ee238c..00000000
--- a/examples/minimal_example_run_global_counter_SM.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import logging
-import sys
-
-from bpod_core.bpod import Bpod
-from bpod_core.fsm import StateMachine
-
-LOG_FILE = 'bpod_debug.log'
-
-logging.basicConfig(
- level=logging.DEBUG,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- handlers=[
- logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
- logging.StreamHandler(
- sys.stdout
- ), # This also keeps logs appearing in the terminal
- ],
-)
-
-# 3. Specifically ensure the bpod_core library is set to DEBUG
-logging.getLogger('bpod_core').setLevel(logging.DEBUG)
-
-print(f'Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}')
-
-fsm = StateMachine()
-
-fsm.set_global_counter(
- index=0, # this is zero based, so this is Global Counter 1
- event='BNC1_High',
- threshold=5,
-)
-
-fsm.add_state(
- name='InitialDelay',
- timer=2,
- transitions={'Tup': 'ResetGlobalCounter'},
- actions={'PWM2': 255},
-)
-fsm.add_state(
- name='ResetGlobalCounter',
- transitions={'Tup': 'Port1Light'},
- actions={'GlobalCounterReset': 1},
-)
-fsm.add_state(
- name='Port1Light',
- timer=0.25,
- transitions={
- 'Tup': 'Port3Light',
- 'GlobalCounter1_End': '>exit',
- },
- actions={'PWM1': 255},
-)
-fsm.add_state(
- name='Port3Light',
- timer=0.25,
- transitions={
- 'Tup': 'Port1Light',
- 'GlobalCounter1_End': '>exit',
- },
- actions={'PWM3': 255},
-)
-
-
-with Bpod() as bpod:
- print('Connected to Bpod!')
- print(f'Found Bpod on port {bpod.serial0.port}')
- print(f'Firmware Version: {bpod.version.firmware}')
- print(f'Hardware Version: {bpod.version.machine}')
- print('Send State machine.')
- bpod.send_state_machine(fsm)
- print('Run State machine.')
- bpod.run_state_machine()
- print('State machine finished.')
diff --git a/examples/minimal_example_run_global_timer_SM.py b/examples/minimal_example_run_global_timer_SM.py
deleted file mode 100644
index f56dedbc..00000000
--- a/examples/minimal_example_run_global_timer_SM.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import logging
-import sys
-
-from bpod_core.bpod import Bpod
-from bpod_core.fsm import StateMachine
-
-LOG_FILE = 'bpod_debug.log'
-
-logging.basicConfig(
- level=logging.DEBUG,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- handlers=[
- logging.FileHandler(LOG_FILE, mode='w', encoding='utf-8'),
- logging.StreamHandler(
- sys.stdout
- ), # This also keeps logs appearing in the terminal
- ],
-)
-
-# 3. Specifically ensure the bpod_core library is set to DEBUG
-logging.getLogger('bpod_core').setLevel(logging.DEBUG)
-
-print(f'Logging initialized. All Bpod traffic will be saved to: {LOG_FILE}')
-
-fsm = StateMachine()
-
-fsm.set_global_timer(
- index=0, # this is 0 indexed
- duration=5,
-)
-
-
-fsm.add_state(
- name='StartGlobalTimer',
- timer=0.25,
- transitions={'Tup': 'Port1Light'},
- actions={'GlobalTimerTrig': 1}, # this is 1 indexed
-)
-fsm.add_state(
- name='Port1Light',
- timer=0.25,
- transitions={
- 'Tup': 'Port3Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
- },
- actions={'PWM1': 255},
-)
-fsm.add_state(
- name='Port3Light',
- timer=0.25,
- transitions={
- 'Tup': 'Port1Light',
- 'GlobalTimer1_End': '>exit', # this is 1 indexed
- },
- actions={'PWM3': 255},
-)
-
-
-with Bpod() as bpod:
- print('Connected to Bpod!')
- print(f'Found Bpod on port {bpod.serial0.port}')
- print(f'Firmware Version: {bpod.version.firmware}')
- print(f'Hardware Version: {bpod.version.machine}')
- print('Send State machine.')
- bpod.send_state_machine(fsm)
- print('Run State machine.')
- bpod.run_state_machine()
- print('State machine finished.')
diff --git a/examples/minimal_examples/run_global_timer.py b/examples/minimal_examples/run_global_timer.py
new file mode 100644
index 00000000..b3e6fc1b
--- /dev/null
+++ b/examples/minimal_examples/run_global_timer.py
@@ -0,0 +1,49 @@
+"""Global Timer Example.
+
+This example demonstrates how to define a state machine with a global timer and run it
+on a Bpod with debug logging enabled. The state machine alternates LEDs of Port 1 and
+Port 3 every 250 ms until a 5-second global timer expires, then exits the state machine.
+"""
+
+import logging
+
+from bpod_core.bpod import Bpod
+from bpod_core.fsm import StateMachine
+
+# configure debug logging
+logging.basicConfig(level=logging.DEBUG)
+
+# create a new StateMachine instance and configure a 5-second global timer
+fsm = StateMachine()
+fsm.set_global_timer(index=0, duration=5)
+
+# define the state machine's states
+fsm.add_state(
+ name='StartGlobalTimer',
+ timer=0.25,
+ transitions={'Tup': 'Port1Light'},
+ actions={'GlobalTimerTrig': 0},
+)
+fsm.add_state(
+ name='Port1Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port3Light',
+ 'GlobalTimer0_End': '>exit',
+ },
+ actions={'PWM1': 255},
+)
+fsm.add_state(
+ name='Port3Light',
+ timer=0.25,
+ transitions={
+ 'Tup': 'Port1Light',
+ 'GlobalTimer0_End': '>exit',
+ },
+ actions={'PWM3': 255},
+)
+
+# connect to the Bpod, send the state machine, and run it
+with Bpod() as bpod:
+ bpod.send_state_machine(fsm)
+ bpod.run_state_machine()
diff --git a/examples/back_operator.py b/examples/state_machines/back_operator.py
similarity index 100%
rename from examples/back_operator.py
rename to examples/state_machines/back_operator.py
diff --git a/examples/bnc_triggered_state_change.py b/examples/state_machines/bnc_triggered_state_change.py
similarity index 100%
rename from examples/bnc_triggered_state_change.py
rename to examples/state_machines/bnc_triggered_state_change.py
diff --git a/examples/conditions.py b/examples/state_machines/conditions.py
similarity index 100%
rename from examples/conditions.py
rename to examples/state_machines/conditions.py
diff --git a/examples/global_counters.py b/examples/state_machines/global_counters.py
similarity index 81%
rename from examples/global_counters.py
rename to examples/state_machines/global_counters.py
index 6e0bed4b..3ce22293 100644
--- a/examples/global_counters.py
+++ b/examples/state_machines/global_counters.py
@@ -9,7 +9,7 @@
fsm = StateMachine()
fsm.set_global_counter(
- index=0, # this is zero based, so this is Global Counter 1
+ index=0,
event='Port1High',
threshold=5,
)
@@ -23,14 +23,14 @@
fsm.add_state(
name='ResetGlobalCounter',
transitions={'Tup': 'Port1Light'},
- actions={'GlobalCounterReset': 1},
+ actions={'GlobalCounterReset': 0},
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalCounter1_End': '>exit',
+ 'GlobalCounter0_End': '>exit',
},
actions={'PWM1': 255},
)
@@ -39,7 +39,7 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalCounter1_End': '>exit',
+ 'GlobalCounter0_End': '>exit',
},
actions={'PWM3': 255},
)
diff --git a/examples/global_timers.py b/examples/state_machines/global_timers.py
similarity index 71%
rename from examples/global_timers.py
rename to examples/state_machines/global_timers.py
index 7a555e35..21ab25c7 100644
--- a/examples/global_timers.py
+++ b/examples/state_machines/global_timers.py
@@ -8,23 +8,22 @@
fsm = StateMachine()
fsm.set_global_timer(
- index=0, # this is zero based, so this is Global Timer 1
+ index=0,
duration=5,
)
-
fsm.add_state(
name='StartGlobalTimer',
timer=0.25,
transitions={'Tup': 'Port1Light'},
- actions={'GlobalTimerTrig': 1}, # this is 1 based
+ actions={'GlobalTimerTrig': 0},
)
fsm.add_state(
name='Port1Light',
timer=0.25,
transitions={
'Tup': 'Port3Light',
- 'GlobalTimer1_End': '>exit', # this is 1 based
+ 'GlobalTimer0_End': '>exit',
},
actions={'PWM1': 255},
)
@@ -33,7 +32,7 @@
timer=0.25,
transitions={
'Tup': 'Port1Light',
- 'GlobalTimer1_End': '>exit', # this is 1 based
+ 'GlobalTimer0_End': '>exit',
},
actions={'PWM3': 255},
)
diff --git a/examples/light_chasing.py b/examples/state_machines/light_chasing.py
similarity index 100%
rename from examples/light_chasing.py
rename to examples/state_machines/light_chasing.py
diff --git a/examples/two_choice.py b/examples/state_machines/two_choice.py
similarity index 100%
rename from examples/two_choice.py
rename to examples/state_machines/two_choice.py
diff --git a/pyproject.toml b/pyproject.toml
index f674a762..a2944e37 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -100,7 +100,7 @@ source-include = [
"tests/*.py",
"schema/*.json",
"docs/source/**/*",
- "examples/*"
+ "examples/**/*"
]
source-exclude = [
"docs/source/api",
From 20f871416a02cac3006794538858a8c9fcfd228c Mon Sep 17 00:00:00 2001
From: Florian Rau
Date: Tue, 27 Jan 2026 19:33:00 +0000
Subject: [PATCH 7/8] refactor: remove redundant debug checks and logs
---
bpod_core/bpod.py | 39 ++++++++++++---------------------------
1 file changed, 12 insertions(+), 27 deletions(-)
diff --git a/bpod_core/bpod.py b/bpod_core/bpod.py
index 81147b8c..7bf5251b 100644
--- a/bpod_core/bpod.py
+++ b/bpod_core/bpod.py
@@ -218,26 +218,22 @@ def run(self) -> None:
target_exit = np.uint8(state_transitions.shape[0])
target_back = np.uint8(255)
use_back_op = self._use_back_op
+ event_names = self._event_names
# create buffers for repeated serial reads
opcode_buf = bytearray(2) # buffer for opcodes
event_data_buf = bytearray(259) # max 255 events + 4 bytes for n_cycles
- # should we use debug logging?
- debug = logger.isEnabledFor(logging.DEBUG)
-
# confirm the state machine
if self._confirm_fsm:
if serial.read(1) != b'\x01':
raise RuntimeError(f'State machine #{index} was not confirmed by Bpod')
- if debug:
- logger.debug('State machine #%d confirmed by Bpod', index)
+ logger.debug('State machine #%d confirmed by Bpod', index)
# read the start time of the state machine
t0 = serial.read_uint64()
- if debug:
- logger.debug('%d µs: Starting state machine #%d', t0, index)
- logger.debug('%d µs: State %d', t0, current_state)
+ logger.debug('%d µs: Starting state machine #%d', t0, index)
+ logger.debug('%d µs: State %d', t0, current_state)
# TODO: handle start of state machine
# TODO: handle start of state
@@ -259,13 +255,10 @@ def run(self) -> None:
# handle each event
events = event_data_view[:param]
for event in events:
- if debug and event != 255: # exit event
- event_name = (
- self._event_names[event]
- if event < len(self._event_names)
- else 'Unknown'
+ if event != 255:
+ logger.debug(
+ '%d µs: Event %d - %s)', micros, event, event_names[event]
)
- logger.debug('%d µs: Event: %s (%d)', micros, event_name, event)
# TODO: handle event
# handle state transitions / exit event
@@ -285,14 +278,12 @@ def run(self) -> None:
previous_state = current_state
current_state = target_state
# TODO: handle start of state
- if debug:
- logger.debug('%d µs: State %d', micros, current_state)
+ logger.debug('%d µs: State %d', micros, current_state)
break # only handle the first state transition
elif opcode == 2: # handle softcodes
param -= 1
- if debug:
- logger.debug('Softcode %d', param)
+ logger.debug('Softcode %d', param)
softcode_handler(param)
else:
@@ -301,10 +292,9 @@ def run(self) -> None:
# exit state machine
# read 12 bytes: cycles (uInt32) and micros (uInt64)
cycles, micros = self._struct_exit.unpack(serial.read(12))
- if debug:
- logger.debug(
- '%d µs: Ending state machine #%d (%d cycles)', micros, index, cycles
- )
+ logger.debug(
+ '%d µs: Ending state machine #%d (%d cycles)', micros, index, cycles
+ )
# TODO: handle end of state machine
@@ -804,7 +794,6 @@ def _compile_event_names(self) -> None:
]:
self.event_names.extend(event_name.format(i) for i in range(n))
self.event_names.append('Tup')
- logger.debug('Compiled event names: %s', self.event_names)
def _compile_output_actions(self) -> None:
"""Compile the list of output actions supported by the Bpod hardware."""
@@ -1269,10 +1258,6 @@ def append_events(event0: str, event1: str) -> None:
f'
Date: Tue, 27 Jan 2026 19:37:30 +0000
Subject: [PATCH 8/8] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 409d747b..0769a664 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,8 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- convenience functions for reading and writing integers in `com.ExtendedSerial`.
- `misc.extend_packed` for extending a bytearray by multiple values of the same format.
- `misc.DocstringInheritanceMixin` for inheriting docstrings from base classes.
+- more examples.
### Changed
+- more verbose debug logging during state machine runs.
- replace unmaintained `appdirs` dependency with `platformdirs`.
- switch to zero-based indexing for global counters, timers, conditions and soft-codes.
- added file locking to `misc.SettingsDict`.