pycontrol-core 3.0.0b1__tar.gz → 3.0.0b2__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.
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/PKG-INFO +1 -1
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/changelog.md +13 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/hardware-definitions.md +33 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/__init__.py +1 -1
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/VL53L4.py +53 -32
- pycontrol_core-3.0.0b1/src/pycontrol/firmware/devices/breakout_10port_r0_6_h7.py → pycontrol_core-3.0.0b2/src/pycontrol/firmware/devices/breakout_10port_r0_4_h7.py +3 -3
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/breakout_10port_r0_8_h7.py +1 -1
- pycontrol_core-3.0.0b2/src/pycontrol/firmware/devices/breakout_6port_r0_1_h7.py +33 -0
- pycontrol_core-3.0.0b2/src/pycontrol/firmware/devices/breakout_6port_r0_2_h7.py +34 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/framework.py +1 -1
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/hardware.py +10 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/state_machine.py +17 -0
- pycontrol_core-3.0.0b2/tests/unit/firmware/test_breakout_qwiic.py +21 -0
- pycontrol_core-3.0.0b2/tests/unit/firmware/test_state_machine_variables.py +50 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_vl53l4.py +95 -29
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.complexipy_cache/.gitignore +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.complexipy_cache/CACHEDIR.TAG +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.complexipy_cache/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.complexipy_cache/v/cache/functions +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.gitignore +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.pre-commit-config.yaml +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/.python-version +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/CONTRIBUTING.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/LICENSE.txt +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/addon-settings.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/architecture.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/cli-reference.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/contracts.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/core-walkthrough.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/data-format.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/design-history/simplified-pycontrol-provenance-and-sharing-motivation.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/design-history/simplified-pycontrol-provenance-and-sharing-plan.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/experiment-handlers.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/getting-started.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/maintainability.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/migration-from-official.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/public-api.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/release-acceptance.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/sharing.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/subscriber-api.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/task-handlers.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/testing.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/threading-and-state.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/troubleshooting.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/wire-protocol.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/docs/writing-tasks.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/pyproject.toml +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/controls_context.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/experiment_api.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/loader.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/addons/task_api.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/analysis/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/analysis/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/analysis/data_import.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/collect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/models.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/protocol.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/run.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bundles/zipio.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bus/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bus/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bus/events.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bus/status.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/bus/subscriber.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/board.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/board_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/common.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/data.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/data_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/experiment.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/experiment_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/firmware.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/firmware_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/gui.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/gui_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/run.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/run_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/setups.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/setups_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/vars.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/vars_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/workspace.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/commands/workspace_impl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/cli/main.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/config/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/config/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/config/io.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/config/models.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/hooks.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/models.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/planning.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/run.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/run_recording.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/experiment/supervisor.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/device_helpers.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/LED_driver.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/MCP.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/analog_LED.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/audio_board.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/audio_player.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/breakout_6port_r1_2_f4.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/five_poke.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/frame_logger.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/frame_trigger.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/grid_maze.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/lickometer.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/load_cell.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/nine_poke.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/poke.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/port_expander.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/rotary_encoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/schmitt_trigger.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/solenoid_driver.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/stepper_motor.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/uRFID.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/devices/uart_handler.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/audio.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/profiles.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/timer.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/utility.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/fsutil.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/_queue.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/notify/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/notify/slack.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/integrations/notify/telegram.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/mcu.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/analog.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/decoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/encoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/protocol/messages.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/archive.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/collect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/hashing.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/models.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/provenance/source.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/analog_writer.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/console.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/event_rows.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/recording/native_event_logger.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/device_helpers.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/file_transfer.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/json_repl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/repl/raw_repl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/board_session.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/device_resolution.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/info.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/link.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/phases.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/protocols.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/pump.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/rows.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/services/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/services/firmware.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/services/maintenance.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/services/setup.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/services/sync.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/types.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/session/variables.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/testing/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/testing/fake_board.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/testing/frames.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/base.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/mock.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/replay.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/transport/serial.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace/creation.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace/hwdef_inspect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace/task_inspect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/controls/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/controls/blinker_gui.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/controls/example_dialog.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/experiment_handlers/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/experiment_handlers/notion_logger.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/experiment_handlers/s3_data_exporter.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/plotters/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/task_handlers/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/task_handlers/example_handler.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/task_handlers/run_notify.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/addons/task_handlers/trial_summary.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/devices/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/devices/example_device.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/experiments/blink_demo.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/settings.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/setups.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/workspace_template/tasks/examples/blinker-pattern-h7.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/CLASSIFICATION.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/contracts/test_wire_contract.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/machine.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/micropython.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/pyb.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/ucollections.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/firmware_stubs/ujson.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/blinker_real.in.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/blinker_real.out.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/counter_demo_real.in.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/counter_demo_real.out.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/encoder_real.in.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/frames/encoder_real.out.bin +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/replay.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/blinker_real.meta.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/blinker_real.tsv +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/counter_demo_real.meta.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/counter_demo_real.tsv +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/encoder_real.meta.json +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/encoder_real.tsv +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/encoder_real_encoder.data.npy +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/sessions/encoder_real_encoder.time.npy +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/tasks/blinker_real.task.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/tasks/counter_demo_real.task.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/golden/tasks/encoder_real.task.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_board_session.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_cli_workspace.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_experiment_runner.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_real_hardware_replay.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_repl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/integration/test_run.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/__init__.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/conftest.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_analog_trigger_variables.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_audio.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_device_helpers.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_devices_init.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_hardware_uses_profile.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_profiles.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_rotary_encoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_run_sequence.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_schmitt_trigger_threshold.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_timer_rounding.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/firmware/test_utility_log.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_addons.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_analysis_data_import.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_board_link.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_bundles_collect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_bus_subscriber.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_cli.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_cli_setups.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_config_models.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_device_resolution.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_device_table_consistency.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_experiment_hook_sequencer.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_experiment_planning.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_file_transfer_errors.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_firmware_root.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_firmware_service.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_firmware_status.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_integrations_notify.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_maintenance_service.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_phases.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_protocol_decoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_protocol_encoder.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_provenance.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_public_api.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_raw_repl.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_recording.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_run_bundle_format.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_run_data_durability.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_run_recovery.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_serial_transport.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_session_pump.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_session_rows.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_session_variables.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_setup_identity.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_status_fields.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_subject_id.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_supervisor_join.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_transport_mock.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_workspace.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_workspace_hwdef_inspect.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tests/unit/test_workspace_template.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tools/README.md +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tools/capture_fixtures.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tools/hardware_acceptance.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tools/maintainability_metrics.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/tools/run_tests.py +0 -0
- {pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/uv.lock +0 -0
|
@@ -17,6 +17,19 @@
|
|
|
17
17
|
names, then sync the pyControl framework to the board. The old names are no
|
|
18
18
|
longer exported. Pin mappings are unchanged.
|
|
19
19
|
|
|
20
|
+
### I2C connectors
|
|
21
|
+
|
|
22
|
+
- New `pycontrol.hardware.I2CConnector` for 4-pin JST-SH I2C connectors
|
|
23
|
+
(SparkFun Qwiic / Adafruit STEMMA QT). The six-port H7 breakouts expose one
|
|
24
|
+
as `board.qwiic`, on the same I2C-1 bus as `port_4`.
|
|
25
|
+
- `VL53L4CD` is now only the sensor: `VL53L4CD(i2c, name, sampling_rate,
|
|
26
|
+
address=0x29, enable_pin=None, trigger=None)`. It no longer takes a `port` or
|
|
27
|
+
creates `LED`/`motor` outputs; build those in a nosepoke device instead.
|
|
28
|
+
`i2c_setup(new_address)` and `begin()` are replaced by the `address` argument
|
|
29
|
+
and an automatic bring-up at the first run start (or `setup()`), so several
|
|
30
|
+
sensors on one bus can be constructed independently. Sensors sharing a bus
|
|
31
|
+
need distinct addresses and enable pins; clashes raise `ValueError`.
|
|
32
|
+
|
|
20
33
|
### Add-on naming
|
|
21
34
|
|
|
22
35
|
- Workspace add-ons live in `addons/task_handlers/`,
|
|
@@ -126,6 +126,39 @@ counter.
|
|
|
126
126
|
Timer and pin ownership lasts for the hardware object's lifetime. Switching a
|
|
127
127
|
port between UART and encoder mode while a task is running is not supported.
|
|
128
128
|
|
|
129
|
+
## I2C Connectors
|
|
130
|
+
|
|
131
|
+
Some breakouts have a 4-pin JST-SH I2C connector (SparkFun Qwiic / Adafruit
|
|
132
|
+
STEMMA QT). It is an `I2CConnector` with `SCL`, `SDA` and `I2C` (the bus
|
|
133
|
+
number). The six-port H7 breakouts expose it as `board.qwiic`, wired to
|
|
134
|
+
`port_4`'s DIO_A/DIO_B, so both connectors are the same I2C-1 bus:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from machine import I2C
|
|
138
|
+
from devices.VL53L4 import VL53L4CD
|
|
139
|
+
|
|
140
|
+
i2c = I2C(board.qwiic.I2C, freq=400_000)
|
|
141
|
+
tof = VL53L4CD(i2c, "tof", sampling_rate=40)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Create one `I2C` object per bus and pass it to every device on that bus: a
|
|
145
|
+
second `I2C(1, freq=...)` re-clocks the bus for all of them.
|
|
146
|
+
|
|
147
|
+
## I2C Time-of-Flight Sensors
|
|
148
|
+
|
|
149
|
+
`VL53L4CD(i2c, name, sampling_rate, address=0x29, enable_pin=None, trigger=None)`
|
|
150
|
+
streams `entrance - distance` (mm) as the analog input `name`. `enable_pin`
|
|
151
|
+
drives the chip's XSHUT pin. The chip is brought up when the first run starts,
|
|
152
|
+
once the task file has constructed every device; call `setup()` at import to
|
|
153
|
+
bring a sensor that is alone on its bus up earlier and report wiring problems
|
|
154
|
+
at upload.
|
|
155
|
+
|
|
156
|
+
Sensors that share a bus all start at address 0x29, so each needs an
|
|
157
|
+
`enable_pin` and its own `address`. Each constructor holds its chip in reset;
|
|
158
|
+
at run start each chip boots at 0x29 in turn and moves to its own address
|
|
159
|
+
before the next is released. A clashing address, or a sensor without an
|
|
160
|
+
`enable_pin` on a shared bus, raises `ValueError` at construction.
|
|
161
|
+
|
|
129
162
|
## Analog sources and DAC buffering
|
|
130
163
|
|
|
131
164
|
`AnalogInput` records the ADC's raw codes. A plausible trace does not establish
|
|
@@ -48,24 +48,40 @@ _BOOT_S = 0.002 # Datasheet tBOOT: the chip answers on I2C 1.2 ms after XSHUT r
|
|
|
48
48
|
_BOOT_POLL_ATTEMPTS = 100 # x 1 ms between polls of the firmware status register.
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
# Sensors constructed on each I2C object, so address clashes fail at import.
|
|
52
|
+
_buses = {}
|
|
53
|
+
|
|
54
|
+
|
|
51
55
|
class VL53L4CD(AnalogInput):
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
"""VL53L4CD time-of-flight sensor, streamed as the analog input `name`.
|
|
57
|
+
|
|
58
|
+
Samples are entrance - distance (mm), so they rise as a target approaches.
|
|
59
|
+
enable_pin drives the chip's XSHUT; while it is low the chip is in reset.
|
|
60
|
+
|
|
61
|
+
The chip is brought up when the first run starts, after every device in the
|
|
62
|
+
task file has been constructed (call setup() to do it earlier). Sensors
|
|
63
|
+
sharing an I2C bus each need an enable_pin and their own address: every
|
|
64
|
+
constructor holds its chip in reset, then each chip boots at the default
|
|
65
|
+
address in turn and is moved to its own before the next is released.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self, i2c, name, sampling_rate, address=_DEFAULT_I2C_ADDRESS, enable_pin=None, trigger=None):
|
|
69
|
+
super().__init__(None, name, int(sampling_rate), data_type="h", triggers=[trigger] if trigger else None)
|
|
54
70
|
self.trigger = trigger
|
|
55
71
|
if self.trigger:
|
|
56
|
-
self.trigger.attached_to =
|
|
57
|
-
|
|
72
|
+
self.trigger.attached_to = name
|
|
73
|
+
sharing = _buses.setdefault(id(i2c), [])
|
|
74
|
+
for other in sharing:
|
|
75
|
+
if other._target_address == address:
|
|
76
|
+
raise ValueError("VL53L4CD '{}' and '{}' share address 0x{:02X}.".format(other.name, name, address))
|
|
77
|
+
if other.enable is None or enable_pin is None:
|
|
78
|
+
raise ValueError("VL53L4CDs sharing an I2C bus each need an enable_pin.")
|
|
79
|
+
sharing.append(self)
|
|
80
|
+
self.i2c = i2c
|
|
58
81
|
self._address = _DEFAULT_I2C_ADDRESS
|
|
59
|
-
self._target_address =
|
|
82
|
+
self._target_address = address
|
|
60
83
|
self._powered = False
|
|
61
|
-
|
|
62
|
-
self.LED = DigitalOutput(port.POW_A)
|
|
63
|
-
self.LED.off()
|
|
64
|
-
self.motor = DigitalOutput(port.POW_B)
|
|
65
|
-
self.motor.off()
|
|
66
|
-
assert port.DIO_C is not None, "! VL53L4CD needs a port with DIO_C."
|
|
67
|
-
self.tof_enable = DigitalOutput(port.DIO_C)
|
|
68
|
-
self.tof_enable.off()
|
|
84
|
+
self.enable = DigitalOutput(enable_pin) if enable_pin is not None else None # Starts off: chip in reset.
|
|
69
85
|
|
|
70
86
|
self.buffer = bytearray(2)
|
|
71
87
|
self.get_range_packet = struct.pack(">H", 0x0096)
|
|
@@ -75,15 +91,12 @@ class VL53L4CD(AnalogInput):
|
|
|
75
91
|
self._ranging = False
|
|
76
92
|
self.connection_error = False
|
|
77
93
|
|
|
78
|
-
def
|
|
79
|
-
"""Reset the chip
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
self._target_address = new_address or _DEFAULT_I2C_ADDRESS
|
|
86
|
-
self._power_up()
|
|
94
|
+
def setup(self):
|
|
95
|
+
"""Reset the chip, initialise it, move it to its address and start ranging."""
|
|
96
|
+
try:
|
|
97
|
+
self._power_up()
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
raise RuntimeError("Trouble connecting to VL53L4CD '{}': {!r}".format(self.name, exc))
|
|
87
100
|
|
|
88
101
|
def _power_up(self):
|
|
89
102
|
# XSHUT low is a full hardware reset: it discards the remapped address
|
|
@@ -91,23 +104,25 @@ class VL53L4CD(AnalogInput):
|
|
|
91
104
|
# chip was mid-transaction. The pyboard soft-reset between uploads
|
|
92
105
|
# leaves GPIO state alone, so the chips are otherwise still live at
|
|
93
106
|
# their old addresses when the next task file imports.
|
|
94
|
-
self.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
if self.enable:
|
|
108
|
+
self.enable.off()
|
|
109
|
+
time.sleep(_XSHUT_LOW_S)
|
|
110
|
+
self.enable.on()
|
|
111
|
+
time.sleep(_BOOT_S)
|
|
98
112
|
self._address = _DEFAULT_I2C_ADDRESS
|
|
99
113
|
try:
|
|
100
114
|
self._wait_for_boot()
|
|
101
115
|
except OSError:
|
|
102
116
|
if self._target_address == _DEFAULT_I2C_ADDRESS:
|
|
103
117
|
raise
|
|
104
|
-
# Nothing at the default address:
|
|
105
|
-
#
|
|
118
|
+
# Nothing at the default address: the chip was not reset, so it is
|
|
119
|
+
# still running at the address the previous run gave it.
|
|
106
120
|
self._address = self._target_address
|
|
107
121
|
self._wait_for_boot()
|
|
108
122
|
self._sensor_init()
|
|
109
123
|
if self._address != self._target_address:
|
|
110
124
|
self.set_address(self._target_address)
|
|
125
|
+
self.begin()
|
|
111
126
|
self._powered = True
|
|
112
127
|
|
|
113
128
|
def set_address(self, new_address):
|
|
@@ -119,10 +134,15 @@ class VL53L4CD(AnalogInput):
|
|
|
119
134
|
self.timing_budget = 20
|
|
120
135
|
self.start_ranging()
|
|
121
136
|
|
|
137
|
+
def _initialise(self):
|
|
138
|
+
# Runs once, before the first run's clock starts, after the task file
|
|
139
|
+
# has constructed every sensor and so put every chip in reset.
|
|
140
|
+
if not self._powered:
|
|
141
|
+
self.setup()
|
|
142
|
+
|
|
122
143
|
def _run_start(self):
|
|
123
|
-
if not self._powered: # Re-run
|
|
124
|
-
self.
|
|
125
|
-
self.begin()
|
|
144
|
+
if not self._powered: # Re-run: _run_stop powered the chip down.
|
|
145
|
+
self.setup()
|
|
126
146
|
super()._run_start()
|
|
127
147
|
|
|
128
148
|
def _run_stop(self):
|
|
@@ -131,7 +151,8 @@ class VL53L4CD(AnalogInput):
|
|
|
131
151
|
self.stop_ranging()
|
|
132
152
|
except OSError:
|
|
133
153
|
pass
|
|
134
|
-
self.
|
|
154
|
+
if self.enable:
|
|
155
|
+
self.enable.off()
|
|
135
156
|
self._powered = False
|
|
136
157
|
|
|
137
158
|
def read_sample(self):
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from pycontrol.hardware import Port,
|
|
1
|
+
from pycontrol.hardware import Port, DigitalOutput
|
|
2
2
|
|
|
3
3
|
# MCU family this breakout's pin geometry is wired for. The host reads it
|
|
4
4
|
# statically (pycontrol.workspace.hwdef_inspect) to pre-flight setups.
|
|
5
5
|
MCU = "stm32h743"
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
9
|
-
"""Pin geometry for the ten-port H7 breakout, PCB revision 0.
|
|
8
|
+
class Breakout10PortR0_4H7:
|
|
9
|
+
"""Pin geometry for the ten-port H7 breakout, PCB revision 0.4-0.7"""
|
|
10
10
|
|
|
11
11
|
def __init__(self):
|
|
12
12
|
self.port_1 = Port(DIO_A="B10", DIO_B="B11", DIO_C="A4", POW_A="C2", POW_B="C3", UART=3, I2C=2, DAC=1)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pycontrol.hardware import Port, I2CConnector, DigitalOutput
|
|
2
|
+
|
|
3
|
+
# MCU family this breakout's pin geometry is wired for. The host reads it
|
|
4
|
+
# statically (pycontrol.workspace.hwdef_inspect) to pre-flight setups.
|
|
5
|
+
MCU = "stm32h743"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Breakout6PortR0_1H7:
|
|
9
|
+
"""Pin geometry for the six-port H7 breakout, PCB revision 0.1."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.port_1 = Port(DIO_A="B13", DIO_B="B12", POW_A="E1", POW_B="E4", POW_C="B0", UART=5)
|
|
13
|
+
self.port_2 = Port(DIO_A="A0", DIO_B="A1", POW_A="E5", POW_B="E6", POW_C="D3", UART=4)
|
|
14
|
+
self.port_3 = Port(DIO_A="B10", DIO_B="B11", DIO_C="A4", POW_A="C1", POW_B="C3", UART=3, I2C=2, DAC=1)
|
|
15
|
+
self.port_4 = Port(DIO_A="B8", DIO_B="B9", DIO_C="A5", POW_A="D6", POW_B="D7", I2C=1, DAC=2)
|
|
16
|
+
self.port_5 = Port(DIO_A="A2", DIO_B="A3", DIO_C="D1", POW_A="E10", POW_B="E15", UART=2)
|
|
17
|
+
self.port_6 = Port(DIO_A="C6", DIO_B="C7", DIO_C="B14", POW_A="C0", POW_B="E0", UART=6)
|
|
18
|
+
self.qwiic = I2CConnector(SCL="B8", SDA="B9", I2C=1) # Shares the I2C-1 bus with port_4.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
self.led_red = DigitalOutput("D5")
|
|
22
|
+
self.led_yellow = DigitalOutput("D1")
|
|
23
|
+
self.led_green = DigitalOutput("D4")
|
|
24
|
+
self.led_blue = DigitalOutput("E3", inverted=True)
|
|
25
|
+
|
|
26
|
+
self.BUTTON_A = self.BUTTON = "C13"
|
|
27
|
+
self.BUTTON_B = "C12"
|
|
28
|
+
self.BUTTON_C = "D4"
|
|
29
|
+
self.BUTTON_D = "A15"
|
|
30
|
+
|
|
31
|
+
self.BNC_2 = "C7"
|
|
32
|
+
self.BNC_3 = self.DAC_1 = "A4"
|
|
33
|
+
self.BNC_4 = self.DAC_2 = "A5"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pycontrol.hardware import Port, I2CConnector, DigitalOutput
|
|
2
|
+
|
|
3
|
+
# MCU family this breakout's pin geometry is wired for. The host reads it
|
|
4
|
+
# statically (pycontrol.workspace.hwdef_inspect) to pre-flight setups.
|
|
5
|
+
MCU = "stm32h743"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Breakout6PortR0_2H7:
|
|
9
|
+
"""Pin geometry for the six-port H7 breakout, PCB revision 0.1."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.port_1 = Port(DIO_A="B13", DIO_B="B12", POW_A="E1", POW_B="E4", POW_C="B0", UART=5)
|
|
13
|
+
self.port_2 = Port(DIO_A="A0", DIO_B="A1", POW_A="E5", POW_B="E6", POW_C="D3", UART=4)
|
|
14
|
+
self.port_3 = Port(DIO_A="B10", DIO_B="B11", DIO_C="A4", POW_A="C2", POW_B="C3", UART=3, I2C=2, DAC=1)
|
|
15
|
+
self.port_4 = Port(DIO_A="B8", DIO_B="B9", DIO_C="A5", POW_A="D6", POW_B="D7", I2C=1, DAC=2)
|
|
16
|
+
self.port_5 = Port(DIO_A="A2", DIO_B="A3", DIO_C="D1", POW_A="E10", POW_B="E15", UART=2)
|
|
17
|
+
self.port_6 = Port(DIO_A="C6", DIO_B="C7", DIO_C="B3", POW_A="C0", POW_B="E0", UART=6)
|
|
18
|
+
self.qwiic = I2CConnector(SCL="B8", SDA="B9", I2C=1) # Shares the I2C-1 bus with port_4.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
self.led_red = DigitalOutput("D5", inverted=True)
|
|
22
|
+
self.led_yellow = DigitalOutput("D0", inverted=True)
|
|
23
|
+
self.led_green = DigitalOutput("D4", inverted=True)
|
|
24
|
+
self.led_blue = DigitalOutput("E3", inverted=True)
|
|
25
|
+
|
|
26
|
+
self.BUTTON_A = self.BUTTON = "C13"
|
|
27
|
+
self.BUTTON_B = "C12"
|
|
28
|
+
self.BUTTON_C = "D4"
|
|
29
|
+
self.BUTTON_D = "A15"
|
|
30
|
+
|
|
31
|
+
self.BNC_1 = "D4"
|
|
32
|
+
self.BNC_2 = "D5"
|
|
33
|
+
self.BNC_3 = self.DAC_1 = "A4"
|
|
34
|
+
self.BNC_4 = self.DAC_2 = "A5"
|
{pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/hardware.py
RENAMED
|
@@ -597,6 +597,16 @@ class Port:
|
|
|
597
597
|
return self.UART
|
|
598
598
|
|
|
599
599
|
|
|
600
|
+
class I2CConnector:
|
|
601
|
+
# 4-pin JST-SH I2C connector (SparkFun Qwiic / Adafruit STEMMA QT). Exposes
|
|
602
|
+
# the same I2C attribute as Port, so drivers that only need the bus accept
|
|
603
|
+
# either. It may share its bus with a Port; share one I2C object between them.
|
|
604
|
+
def __init__(self, SCL, SDA, I2C):
|
|
605
|
+
self.SCL = SCL
|
|
606
|
+
self.SDA = SDA
|
|
607
|
+
self.I2C = I2C
|
|
608
|
+
|
|
609
|
+
|
|
600
610
|
# IOExpanderPin -------------------------------------------------------------
|
|
601
611
|
|
|
602
612
|
|
{pycontrol_core-3.0.0b1 → pycontrol_core-3.0.0b2}/src/pycontrol/firmware/pycontrol/state_machine.py
RENAMED
|
@@ -83,8 +83,25 @@ def stop():
|
|
|
83
83
|
event_dispatch_dict["run_end"]()
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def _match_tuples(value, current):
|
|
87
|
+
# Values from the host may have been through JSON (persistent, setup and CLI
|
|
88
|
+
# variables), which turns tuples into lists. Restore the tuples the task declared.
|
|
89
|
+
if isinstance(value, (list, tuple)) and isinstance(current, (list, tuple)):
|
|
90
|
+
if len(value) == len(current):
|
|
91
|
+
items = [_match_tuples(x, c) for x, c in zip(value, current)]
|
|
92
|
+
elif current:
|
|
93
|
+
items = [_match_tuples(x, current[0]) for x in value]
|
|
94
|
+
else:
|
|
95
|
+
items = list(value)
|
|
96
|
+
return tuple(items) if isinstance(current, tuple) or isinstance(value, tuple) else items
|
|
97
|
+
if isinstance(value, dict) and isinstance(current, dict):
|
|
98
|
+
return {k: _match_tuples(x, current[k]) if k in current else x for k, x in value.items()}
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
|
|
86
102
|
def set_variable(v_name, v_value):
|
|
87
103
|
try:
|
|
104
|
+
v_value = _match_tuples(v_value, getattr(variables, v_name, None))
|
|
88
105
|
setattr(variables, v_name, v_value)
|
|
89
106
|
return True
|
|
90
107
|
except Exception:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""The six-port H7 breakouts' Qwiic connector is the I2C-1 bus on port_4's DIO pins."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
("module", "cls"),
|
|
12
|
+
[("breakout_6port_r0_1_h7", "Breakout6PortR0_1H7"), ("breakout_6port_r0_2_h7", "Breakout6PortR0_2H7")],
|
|
13
|
+
)
|
|
14
|
+
def test_qwiic_shares_port_4_i2c_bus(module: str, cls: str) -> None:
|
|
15
|
+
from pycontrol.hardware import I2CConnector
|
|
16
|
+
|
|
17
|
+
board = getattr(importlib.import_module(f"devices.{module}"), cls)()
|
|
18
|
+
|
|
19
|
+
assert isinstance(board.qwiic, I2CConnector)
|
|
20
|
+
assert (board.qwiic.SCL, board.qwiic.SDA) == (board.port_4.DIO_A, board.port_4.DIO_B) == ("B8", "B9")
|
|
21
|
+
assert board.qwiic.I2C == board.port_4.I2C == 1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _variables(**defaults):
|
|
5
|
+
from pycontrol import state_machine as sm
|
|
6
|
+
from pycontrol import utility as ut
|
|
7
|
+
|
|
8
|
+
for name, value in defaults.items():
|
|
9
|
+
setattr(ut.v, name, value)
|
|
10
|
+
sm.variables = ut.v
|
|
11
|
+
return sm, ut.v
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_json_lists_are_restored_to_declared_tuples() -> None:
|
|
15
|
+
sm, v = _variables(thresholds=[(-10, 0), (-10, 0), (-10, 0)], lengths=(300, 10))
|
|
16
|
+
|
|
17
|
+
assert sm.set_variable("thresholds", [[-5, 0], [-6, 1], [-7, 2]]) is True
|
|
18
|
+
assert sm.set_variable("lengths", [200, 20]) is True
|
|
19
|
+
|
|
20
|
+
assert v.thresholds == [(-5, 0), (-6, 1), (-7, 2)]
|
|
21
|
+
assert type(v.thresholds) is list
|
|
22
|
+
assert v.lengths == (200, 20)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_resized_sequence_uses_first_declared_item_as_template() -> None:
|
|
26
|
+
sm, v = _variables(thresholds=[(-10, 0), (-10, 0)])
|
|
27
|
+
|
|
28
|
+
assert sm.set_variable("thresholds", [[1, 2], [3, 4], [5, 6]]) is True
|
|
29
|
+
|
|
30
|
+
assert v.thresholds == [(1, 2), (3, 4), (5, 6)]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_dict_values_are_matched_by_key() -> None:
|
|
34
|
+
sm, v = _variables(windows={"left": (0, 1), "count": 3})
|
|
35
|
+
|
|
36
|
+
assert sm.set_variable("windows", {"left": [2, 3], "right": [4, 5], "count": 4}) is True
|
|
37
|
+
|
|
38
|
+
assert v.windows == {"left": (2, 3), "right": [4, 5], "count": 4}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_values_without_declared_tuples_are_unchanged() -> None:
|
|
42
|
+
sm, v = _variables(items=[1, 2], name="x", pair=(1, 2))
|
|
43
|
+
|
|
44
|
+
assert sm.set_variable("items", [3, 4, 5]) is True
|
|
45
|
+
assert sm.set_variable("name", [1, 2]) is True
|
|
46
|
+
assert sm.set_variable("pair", 7) is True
|
|
47
|
+
|
|
48
|
+
assert v.items == [3, 4, 5]
|
|
49
|
+
assert v.name == [1, 2]
|
|
50
|
+
assert v.pair == 7
|
|
@@ -8,8 +8,6 @@ the chip's firmware boot.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
from types import SimpleNamespace
|
|
12
|
-
|
|
13
11
|
import pytest
|
|
14
12
|
|
|
15
13
|
DEFAULT = 0x29
|
|
@@ -38,15 +36,12 @@ class FakeI2C:
|
|
|
38
36
|
buf[0] = 0x03 # Firmware booted.
|
|
39
37
|
|
|
40
38
|
|
|
41
|
-
def
|
|
42
|
-
return SimpleNamespace(POW_A="X1", POW_B="X2", DIO_C="X3", DIO_A="B8", DIO_B="B9", I2C=1)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _sensor(i2c, name="l_nose"):
|
|
39
|
+
def _sensor(i2c, name="l_nose", address=DEFAULT, enable_pin="X3"):
|
|
46
40
|
from devices.VL53L4 import VL53L4CD
|
|
47
41
|
|
|
48
|
-
sensor = VL53L4CD(
|
|
42
|
+
sensor = VL53L4CD(i2c, name, 40, address=address, enable_pin=enable_pin)
|
|
49
43
|
sensor._sensor_init = lambda: None # type: ignore[method-assign] # Register dump irrelevant here.
|
|
44
|
+
sensor.begin = lambda: None # type: ignore[method-assign] # Ranging configuration irrelevant here.
|
|
50
45
|
return sensor
|
|
51
46
|
|
|
52
47
|
|
|
@@ -60,17 +55,17 @@ def _fast_sleep(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
60
55
|
def test_constructor_holds_chip_in_reset() -> None:
|
|
61
56
|
sensor = _sensor(FakeI2C(present=[DEFAULT]))
|
|
62
57
|
|
|
63
|
-
assert sensor.
|
|
58
|
+
assert sensor.enable.state is False
|
|
64
59
|
|
|
65
60
|
|
|
66
|
-
def
|
|
61
|
+
def test_setup_resets_then_readdresses_chip_found_at_default() -> None:
|
|
67
62
|
i2c = FakeI2C(present=[DEFAULT])
|
|
68
|
-
sensor = _sensor(i2c)
|
|
63
|
+
sensor = _sensor(i2c, address=0x20)
|
|
69
64
|
xshut: list[bool] = []
|
|
70
|
-
sensor.
|
|
71
|
-
sensor.
|
|
65
|
+
sensor.enable.on = lambda: xshut.append(True) # type: ignore[method-assign]
|
|
66
|
+
sensor.enable.off = lambda: xshut.append(False) # type: ignore[method-assign]
|
|
72
67
|
|
|
73
|
-
sensor.
|
|
68
|
+
sensor.setup()
|
|
74
69
|
|
|
75
70
|
assert xshut == [False, True]
|
|
76
71
|
assert i2c.writes[0] == (DEFAULT, STATUS_REG), "first transfer must be the boot poll at the default address"
|
|
@@ -79,22 +74,78 @@ def test_i2c_setup_resets_then_readdresses_chip_found_at_default() -> None:
|
|
|
79
74
|
assert i2c.present == {0x20}
|
|
80
75
|
|
|
81
76
|
|
|
82
|
-
def
|
|
77
|
+
def test_setup_falls_back_to_previous_address_if_xshut_did_not_reset() -> None:
|
|
83
78
|
i2c = FakeI2C(present=[0x20])
|
|
84
|
-
sensor = _sensor(i2c)
|
|
79
|
+
sensor = _sensor(i2c, address=0x20)
|
|
85
80
|
|
|
86
|
-
sensor.
|
|
81
|
+
sensor.setup()
|
|
87
82
|
|
|
88
83
|
assert sensor._address == 0x20
|
|
89
84
|
assert all(addr == 0x20 for addr, _ in i2c.writes)
|
|
90
85
|
assert not any(buf.startswith(ADDRESS_REG) for _, buf in i2c.writes), "no remap needed"
|
|
91
86
|
|
|
92
87
|
|
|
93
|
-
def
|
|
94
|
-
sensor = _sensor(FakeI2C(present=[]))
|
|
88
|
+
def test_setup_names_the_sensor_when_chip_missing() -> None:
|
|
89
|
+
sensor = _sensor(FakeI2C(present=[]), name="c_nose", address=0x40)
|
|
95
90
|
|
|
96
|
-
with pytest.raises(OSError):
|
|
97
|
-
sensor.
|
|
91
|
+
with pytest.raises(RuntimeError, match=r"'c_nose'.*OSError"):
|
|
92
|
+
sensor.setup()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_sensors_sharing_a_bus_need_distinct_addresses_and_enable_pins() -> None:
|
|
96
|
+
i2c = FakeI2C()
|
|
97
|
+
_sensor(i2c, name="l_nose", address=0x20, enable_pin="X1")
|
|
98
|
+
|
|
99
|
+
with pytest.raises(ValueError, match="share address 0x20"):
|
|
100
|
+
_sensor(i2c, name="c_nose", address=0x20, enable_pin="X2")
|
|
101
|
+
with pytest.raises(ValueError, match="enable_pin"):
|
|
102
|
+
_sensor(i2c, name="r_nose", address=0x40, enable_pin=None)
|
|
103
|
+
_sensor(FakeI2C(), name="other_bus", address=0x20, enable_pin=None) # Separate buses do not interact.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_initialise_brings_shared_bus_sensors_up_one_at_a_time() -> None:
|
|
107
|
+
class SharedBus:
|
|
108
|
+
"""Each chip answers only while enabled; two chips at one address NACK, as a collision would."""
|
|
109
|
+
|
|
110
|
+
def __init__(self) -> None:
|
|
111
|
+
self.chips: list[list] = [] # [sensor, address]
|
|
112
|
+
|
|
113
|
+
def _chip(self, addr):
|
|
114
|
+
live = [chip for chip in self.chips if chip[0].enable.state and chip[1] == addr]
|
|
115
|
+
if len(live) != 1:
|
|
116
|
+
raise OSError(19)
|
|
117
|
+
return live[0]
|
|
118
|
+
|
|
119
|
+
def writeto(self, addr, buf):
|
|
120
|
+
chip = self._chip(addr)
|
|
121
|
+
if bytes(buf[:2]) == ADDRESS_REG and len(buf) == 3:
|
|
122
|
+
chip[1] = buf[2]
|
|
123
|
+
|
|
124
|
+
def readfrom_into(self, addr, buf):
|
|
125
|
+
self._chip(addr)
|
|
126
|
+
buf[0] = 0x03
|
|
127
|
+
|
|
128
|
+
bus = SharedBus()
|
|
129
|
+
sensors = [
|
|
130
|
+
_sensor(bus, name=n, address=a, enable_pin=p)
|
|
131
|
+
for n, a, p in (("l", 0x20, "X1"), ("c", 0x30, "X2"), ("r", 0x40, "X3"))
|
|
132
|
+
]
|
|
133
|
+
for sensor in sensors:
|
|
134
|
+
chip = [sensor, DEFAULT]
|
|
135
|
+
bus.chips.append(chip)
|
|
136
|
+
enable_off = sensor.enable.off
|
|
137
|
+
|
|
138
|
+
def reset_on_off(enable_off=enable_off, chip=chip):
|
|
139
|
+
enable_off()
|
|
140
|
+
chip[1] = DEFAULT # XSHUT low discards the remapped address.
|
|
141
|
+
|
|
142
|
+
sensor.enable.off = reset_on_off # type: ignore[method-assign]
|
|
143
|
+
|
|
144
|
+
for sensor in sensors:
|
|
145
|
+
sensor._initialise()
|
|
146
|
+
|
|
147
|
+
assert [chip[1] for chip in bus.chips] == [0x20, 0x30, 0x40]
|
|
148
|
+
assert all(sensor._powered for sensor in sensors)
|
|
98
149
|
|
|
99
150
|
|
|
100
151
|
def test_wait_for_boot_tolerates_nacks_while_firmware_boots() -> None:
|
|
@@ -152,15 +203,30 @@ def test_ranging_timeouts_use_supported_exception(monkeypatch: pytest.MonkeyPatc
|
|
|
152
203
|
getattr(sensor, method)()
|
|
153
204
|
|
|
154
205
|
|
|
206
|
+
def test_sensor_without_enable_pin_sets_up_without_reset() -> None:
|
|
207
|
+
i2c = FakeI2C(present=[DEFAULT])
|
|
208
|
+
sensor = _sensor(i2c, enable_pin=None)
|
|
209
|
+
|
|
210
|
+
assert sensor.enable is None
|
|
211
|
+
sensor.setup()
|
|
212
|
+
assert sensor._address == DEFAULT
|
|
213
|
+
assert i2c.writes[0] == (DEFAULT, STATUS_REG)
|
|
214
|
+
|
|
215
|
+
sensor._ranging = True
|
|
216
|
+
sensor._run_stop()
|
|
217
|
+
assert sensor._ranging is False
|
|
218
|
+
assert sensor._powered is False
|
|
219
|
+
|
|
220
|
+
|
|
155
221
|
def test_run_stop_stops_ranging_and_powers_chip_down() -> None:
|
|
156
222
|
i2c = FakeI2C(present=[DEFAULT])
|
|
157
|
-
sensor = _sensor(i2c)
|
|
158
|
-
sensor.
|
|
223
|
+
sensor = _sensor(i2c, address=0x20)
|
|
224
|
+
sensor.setup()
|
|
159
225
|
sensor._ranging = True
|
|
160
226
|
|
|
161
227
|
sensor._run_stop()
|
|
162
228
|
|
|
163
|
-
assert sensor.
|
|
229
|
+
assert sensor.enable.state is False
|
|
164
230
|
assert (0x20, b"\x00\x87\x00") in i2c.writes # SYSTEM_START = stop
|
|
165
231
|
assert sensor._ranging is False
|
|
166
232
|
assert any(call[0] == "deinit" for call in sensor.timer.calls)
|
|
@@ -168,8 +234,8 @@ def test_run_stop_stops_ranging_and_powers_chip_down() -> None:
|
|
|
168
234
|
|
|
169
235
|
def test_run_start_after_run_stop_powers_chip_back_up() -> None:
|
|
170
236
|
i2c = FakeI2C(present=[DEFAULT])
|
|
171
|
-
sensor = _sensor(i2c)
|
|
172
|
-
sensor.
|
|
237
|
+
sensor = _sensor(i2c, address=0x20)
|
|
238
|
+
sensor.setup()
|
|
173
239
|
sensor._run_stop()
|
|
174
240
|
i2c.present = {DEFAULT} # Powered-down chip comes back at the default address.
|
|
175
241
|
i2c.writes.clear()
|
|
@@ -179,7 +245,7 @@ def test_run_start_after_run_stop_powers_chip_back_up() -> None:
|
|
|
179
245
|
|
|
180
246
|
sensor._run_start()
|
|
181
247
|
|
|
182
|
-
assert sensor.
|
|
248
|
+
assert sensor.enable.state is True
|
|
183
249
|
assert (DEFAULT, ADDRESS_REG + b"\x20") in i2c.writes
|
|
184
250
|
assert began == [True]
|
|
185
251
|
assert any(call[0] == "init" for call in sensor.timer.calls)
|
|
@@ -187,8 +253,8 @@ def test_run_start_after_run_stop_powers_chip_back_up() -> None:
|
|
|
187
253
|
|
|
188
254
|
def test_run_start_without_run_stop_does_not_reinitialise() -> None:
|
|
189
255
|
i2c = FakeI2C(present=[DEFAULT])
|
|
190
|
-
sensor = _sensor(i2c)
|
|
191
|
-
sensor.
|
|
256
|
+
sensor = _sensor(i2c, address=0x20)
|
|
257
|
+
sensor.setup()
|
|
192
258
|
i2c.writes.clear()
|
|
193
259
|
sensor.read_sample = lambda: None # type: ignore[method-assign]
|
|
194
260
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|