docs: Update documentation for improved clarity and consistency

Revised various sections across multiple documentation files to reflect
updated methods (`run_plc` replacing manual setup with `cycleloop`) and
adjust for new default parameters (e.g., `autorefresh`). Enhanced
descriptions for timers, Cycletools usage, and new method explanations.
Removed outdated or redundant examples and updated system requirements.

Signed-off-by: Sven Sager <akira@narux.de>
This commit is contained in:
2026-02-17 12:05:58 +01:00
parent bae85e1b09
commit 3294c5e980
7 changed files with 71 additions and 314 deletions

View File

@@ -36,7 +36,6 @@ Gateway modules provide generic IOs (like ``Input_1``, ``Output_1``, etc.) that
rpi.io.Input_1.replace_io( rpi.io.Input_1.replace_io(
"temperature", # New IO name "temperature", # New IO name
"h", # struct format: signed short "h", # struct format: signed short
defaultvalue=0 # Default value
) )
# Use the custom IO by its new name # Use the custom IO by its new name
@@ -70,6 +69,7 @@ Common format codes for ``replace_io`` (see `Python struct format characters <ht
* ``'i'`` - signed int (-2147483648 to 2147483647) * ``'i'`` - signed int (-2147483648 to 2147483647)
* ``'I'`` - unsigned int (0 to 4294967295) * ``'I'`` - unsigned int (0 to 4294967295)
* ``'f'`` - float (32-bit) * ``'f'`` - float (32-bit)
* ``'d'`` - float (64-bit)
Multiple Custom IOs Multiple Custom IOs
------------------- -------------------
@@ -82,8 +82,8 @@ Define multiple custom IOs programmatically by replacing generic gateway IOs:
# Replace multiple gateway IOs with custom definitions # Replace multiple gateway IOs with custom definitions
# Assuming a gateway module with Input_1, Input_2, Output_1, Output_2 # Assuming a gateway module with Input_1, Input_2, Output_1, Output_2
rpi.io.Input_1.replace_io("temperature", "h", defaultvalue=0) rpi.io.Input_1.replace_io("temperature", "h")
rpi.io.Input_2.replace_io("humidity", "h", defaultvalue=0) rpi.io.Input_2.replace_io("humidity", "h")
rpi.io.Output_1.replace_io("setpoint", "h", defaultvalue=700) rpi.io.Output_1.replace_io("setpoint", "h", defaultvalue=700)
rpi.io.Output_2.replace_io("control_word", "H", defaultvalue=0) rpi.io.Output_2.replace_io("control_word", "H", defaultvalue=0)
@@ -126,12 +126,10 @@ Create an INI-style configuration file (``replace_ios.conf``):
[temperature] [temperature]
replace = Input_1 replace = Input_1
frm = h frm = h
defaultvalue = 0
[humidity] [humidity]
replace = Input_2 replace = Input_2
frm = h frm = h
defaultvalue = 0
[setpoint] [setpoint]
replace = Output_1 replace = Output_1
@@ -181,7 +179,9 @@ This is useful for:
Watchdog Management Watchdog Management
=================== ===================
The hardware watchdog monitors your program and resets the system if it stops responding. The hardware watchdog monitors your program and resets the system if it stops responding. If you
use the software watchdog will will only works if you use RevPiPyControl as runtime for your python
program.
How the Watchdog Works How the Watchdog Works
----------------------- -----------------------
@@ -197,8 +197,6 @@ Cyclic Watchdog Toggle
import revpimodio2 import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def main_cycle(ct): def main_cycle(ct):
# Toggle every 10 cycles (200ms @ 20ms) # Toggle every 10 cycles (200ms @ 20ms)
if ct.flank10c: if ct.flank10c:
@@ -207,7 +205,7 @@ Cyclic Watchdog Toggle
# Your control logic # Your control logic
ct.io.output.value = ct.io.input.value ct.io.output.value = ct.io.input.value
rpi.cycleloop(main_cycle) revpimodio2.run_plc(main_cycle)
Event-Driven Watchdog Toggle Event-Driven Watchdog Toggle
----------------------------- -----------------------------
@@ -234,134 +232,6 @@ Event-Driven Watchdog Toggle
rpi.handlesignalend() rpi.handlesignalend()
rpi.mainloop() rpi.mainloop()
Conditional Watchdog
--------------------
Enable watchdog only when system is operational:
.. code-block:: python
def machine_with_watchdog(ct):
if ct.first:
ct.var.state = "IDLE"
ct.var.watchdog_enabled = False
# Enable watchdog only in RUNNING state
if ct.var.state == "RUNNING":
if not ct.var.watchdog_enabled:
ct.var.watchdog_enabled = True
print("Watchdog enabled")
# Toggle watchdog
if ct.flank10c:
ct.core.wd_toggle()
else:
ct.var.watchdog_enabled = False
# State machine logic
if ct.var.state == "IDLE":
if ct.io.start_button.value:
ct.var.state = "RUNNING"
elif ct.var.state == "RUNNING":
ct.io.motor.value = True
if ct.io.stop_button.value:
ct.var.state = "IDLE"
rpi = revpimodio2.RevPiModIO(autorefresh=True)
rpi.cycleloop(machine_with_watchdog)
Combining Paradigms
===================
Combine cyclic and event-driven programming for optimal results.
Cyclic Control with Event UI
-----------------------------
Use cyclic for time-critical control, events for user interface:
.. code-block:: python
import revpimodio2
import threading
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def cyclic_control(ct: revpimodio2.Cycletools):
"""Fast control loop."""
if ct.first:
ct.var.setpoint = 50.0
ct.var.running = False
if ct.var.running:
# Fast control logic
error = ct.var.setpoint - ct.io.sensor.value
if error > 5:
ct.io.actuator.value = True
elif error < -5:
ct.io.actuator.value = False
def on_setpoint_change(ioname, iovalue):
"""Event handler for user setpoint changes."""
print(f"New setpoint: {iovalue}")
# Access ct.var from event requires thread-safe approach
# In practice, use shared data structure or message queue
def on_start(ioname, iovalue):
print("System started")
def on_stop(ioname, iovalue):
print("System stopped")
# Register user events
rpi.io.start_button.reg_event(on_start, edge=revpimodio2.RISING)
rpi.io.stop_button.reg_event(on_stop, edge=revpimodio2.RISING)
rpi.io.setpoint_input.reg_event(on_setpoint_change, delay=100)
# Run cyclic loop in background
threading.Thread(
target=lambda: rpi.cycleloop(cyclic_control),
daemon=True
).start()
# Run event loop in main thread
rpi.handlesignalend()
rpi.mainloop()
Event Triggers with Cyclic Processing
--------------------------------------
Use events to trigger actions, cyclic for processing:
.. code-block:: python
import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def cyclic_processor(ct):
"""Process work queue."""
if ct.first:
ct.var.work_queue = []
# Process queued work
if ct.var.work_queue:
item = ct.var.work_queue.pop(0)
process_item(item)
def on_new_item(ioname, iovalue):
"""Queue work from events."""
# Note: Accessing ct.var from events requires synchronization
# This is a simplified example
print(f"New item queued from {ioname}")
rpi.io.trigger1.reg_event(on_new_item, edge=revpimodio2.RISING)
rpi.io.trigger2.reg_event(on_new_item, edge=revpimodio2.RISING)
rpi.cycleloop(cyclic_processor)
Performance Optimization Performance Optimization
======================== ========================
@@ -487,23 +357,19 @@ Track and handle I/O errors:
.. code-block:: python .. code-block:: python
rpi = revpimodio2.RevPiModIO(autorefresh=True) maxioerrors = 10 # Exception after 10 errors
rpi.maxioerrors = 10 # Exception after 10 errors
def main_cycle(ct): def main_cycle(ct):
# Check error count periodically # Check error count periodically
if ct.flank20c: if ct.flank20c:
if rpi.ioerrors > 5: if rpi.core.ioerrorcount > maxioerrors:
print(f"Warning: {rpi.ioerrors} I/O errors detected") print(f"Warning: {rpi.core.ioerrorcount} I/O errors detected")
ct.io.warning_led.value = True ct.io.warning_led.value = True
# Normal logic # Normal logic
ct.io.output.value = ct.io.input.value ct.io.output.value = ct.io.input.value
try: revpimodio2.run_plc(main_cycle)
rpi.cycleloop(main_cycle)
except RuntimeError as e:
print(f"I/O error threshold exceeded: {e}")
Best Practices Best Practices
============== ==============
@@ -600,35 +466,6 @@ Document complex logic:
# State machine implementation # State machine implementation
# ... # ...
Testing
-------
Test your code thoroughly:
.. code-block:: python
def test_temperature_control(ct):
"""Test temperature control logic."""
if ct.first:
ct.var.cooling_active = False
ct.var.test_temp = 20.0
# Simulate temperature increase
if ct.var.test_temp < 80:
ct.var.test_temp += 0.5
# Test control logic
temp = ct.var.test_temp
if temp > 75 and not ct.var.cooling_active:
assert ct.io.cooling.value == True
ct.var.cooling_active = True
if temp < 65 and ct.var.cooling_active:
assert ct.io.cooling.value == False
ct.var.cooling_active = False
Logging Logging
------- -------
@@ -676,6 +513,7 @@ Always validate external inputs:
"""Validate setpoint range.""" """Validate setpoint range."""
if 0 <= iovalue <= 100: if 0 <= iovalue <= 100:
rpi.io.setpoint.value = iovalue rpi.io.setpoint.value = iovalue
rpi.io.error_led.value = False
else: else:
print(f"Invalid setpoint: {iovalue}") print(f"Invalid setpoint: {iovalue}")
rpi.io.error_led.value = True rpi.io.error_led.value = True

View File

@@ -78,10 +78,11 @@ Adjust cycle time to match your needs:
.. code-block:: python .. code-block:: python
rpi = revpimodio2.RevPiModIO(autorefresh=True) rpi = revpimodio2.RevPiModIO()
rpi.cycletime = 100 # Set to 100ms rpi.cycletime = 100 # Set to 100ms
rpi.autorefresh_all()
**Important:** Faster cycle times consume more CPU. Choose the slowest cycle time that meets your requirements. **Important:** Faster cycle times consume more CPU. Choose the slowest cycle time that meets your requirements. Default values will fit most needs.
Error Handling Error Handling
-------------- --------------
@@ -90,10 +91,10 @@ Configure I/O error threshold:
.. code-block:: python .. code-block:: python
rpi.maxioerrors = 10 # Raise exception after 10 errors maxioerrors = 10 # Raise exception after 10 errors
# Check error count # Check error count
if rpi.ioerrors > 5: if rpi.core.ioerrorcount > maxioerrors:
print("Warning: I/O errors detected") print("Warning: I/O errors detected")
Core Objects Core Objects

View File

@@ -42,8 +42,6 @@ Simple Cycle Loop
import revpimodio2 import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def main_cycle(ct: revpimodio2.Cycletools): def main_cycle(ct: revpimodio2.Cycletools):
"""Execute each cycle.""" """Execute each cycle."""
if ct.io.start_button.value: if ct.io.start_button.value:
@@ -51,10 +49,17 @@ Simple Cycle Loop
if ct.io.stop_button.value: if ct.io.stop_button.value:
ct.io.motor.value = False ct.io.motor.value = False
rpi.cycleloop(main_cycle) revpimodio2.run_plc(main_cycle)
# .run_plc is a shortcut for:
# rpi = revpimodio2.RevPiModIO(autorefresh=True)
# rpi.handlesignalend()
# rpi.cycleloop(main_cycle)
The ``main_cycle`` function is called repeatedly at the configured cycle time (typically 20-50ms). The ``main_cycle`` function is called repeatedly at the configured cycle time (typically 20-50ms).
**Info:** ``rpi.handlesignalend()``
Understanding Cycle Time Understanding Cycle Time
------------------------- -------------------------
@@ -68,10 +73,9 @@ Adjust cycle time to match your needs:
.. code-block:: python .. code-block:: python
rpi = revpimodio2.RevPiModIO(autorefresh=True) revpimodio2.run_plc(main_cycle, cycletime=100) # 100ms = 10 Hz
rpi.cycletime = 100 # 100ms = 10 Hz
**Important:** Faster cycle times consume more CPU. Choose the slowest cycle time that meets your requirements. **Important:** Faster cycle times consume more CPU but will detect fast changes of input values.
Cycletools Object Cycletools Object
================= =================
@@ -109,8 +113,7 @@ Use ``ct.first`` and ``ct.last`` for setup and teardown:
ct.io.motor.value = False ct.io.motor.value = False
print(f"Total cycles: {ct.var.counter}") print(f"Total cycles: {ct.var.counter}")
rpi = revpimodio2.RevPiModIO(autorefresh=True) revpimodio2.run_plc(main_cycle)
rpi.cycleloop(main_cycle)
Persistent Variables Persistent Variables
==================== ====================
@@ -155,6 +158,7 @@ Detect input changes efficiently without storing previous values:
print("Button released!") print("Button released!")
rpi = revpimodio2.RevPiModIO(autorefresh=True) rpi = revpimodio2.RevPiModIO(autorefresh=True)
rpi.handlesignalend()
rpi.cycleloop(main_cycle) rpi.cycleloop(main_cycle)
Edge types: Edge types:
@@ -176,17 +180,16 @@ Toggle flags alternate between True/False at regular intervals:
.. code-block:: python .. code-block:: python
def main_cycle(ct): def main_cycle(ct):
# Blink LED - flag5c alternates every 5 cycles # Blink LED - flag5c alternates every cycle
ct.io.blink_led.value = ct.flag5c ct.io.blink_led.value = ct.flag1c
# Different blink rates # Different blink rates
ct.io.fast_blink.value = ct.flag2c # Every 2 cycles ct.io.fast_blink.value = ct.flag5c # Every 5 cycles
ct.io.slow_blink.value = ct.flag20c # Every 20 cycles ct.io.slow_blink.value = ct.flag20c # Every 20 cycles
**Available toggle flags:** **Available toggle flags:**
* ``ct.flag1c`` - Every cycle * ``ct.flag1c`` - Every cycle
* ``ct.flag2c`` - Every 2 cycles
* ``ct.flag5c`` - Every 5 cycles * ``ct.flag5c`` - Every 5 cycles
* ``ct.flag10c`` - Every 10 cycles * ``ct.flag10c`` - Every 10 cycles
* ``ct.flag20c`` - Every 20 cycles * ``ct.flag20c`` - Every 20 cycles
@@ -218,12 +221,12 @@ Flank flags are True for exactly one cycle at regular intervals:
Timers Timers
====== ======
RevPiModIO provides three timer types based on PLC standards. All timers are specified in cycle counts. RevPiModIO provides three timer types based on PLC standards. All timers are specified in cycle counts or milliseconds.
On-Delay Timer (TON/TONC) On-Delay Timer (TON/TONC)
-------------------------- --------------------------
Output becomes True only after input is continuously True for specified cycles: Output becomes True only after input is continuously True for specified cycles (use ton with milliseconds value instead of cycles):
.. code-block:: python .. code-block:: python
@@ -253,7 +256,7 @@ Output becomes True only after input is continuously True for specified cycles:
Off-Delay Timer (TOF/TOFC) Off-Delay Timer (TOF/TOFC)
--------------------------- ---------------------------
Output stays True for specified cycles after input goes False: Output stays True for specified cycles or milliseconds after input goes False (use tof with milliseconds value instead of cycles):
.. code-block:: python .. code-block:: python
@@ -280,7 +283,7 @@ Output stays True for specified cycles after input goes False:
Pulse Timer (TP/TPC) Pulse Timer (TP/TPC)
-------------------- --------------------
Generates a one-shot pulse of specified duration: Generates a one-shot pulse of specified duration (use tp with milliseconds value instead of cycles):
.. code-block:: python .. code-block:: python
@@ -305,25 +308,6 @@ Generates a one-shot pulse of specified duration:
* Acknowledgment pulses * Acknowledgment pulses
* Retriggerable delays * Retriggerable delays
Converting Time to Cycles
--------------------------
Calculate cycles from desired time:
.. code-block:: python
# At 20ms cycle time:
# 1 second = 50 cycles
# 100ms = 5 cycles
# 2 seconds = 100 cycles
def main_cycle(ct):
cycle_time_ms = rpi.cycletime
desired_time_ms = 1500 # 1.5 seconds
cycles_needed = int(desired_time_ms / cycle_time_ms)
ct.set_tonc("my_delay", cycles_needed)
State Machines State Machines
============== ==============
@@ -345,28 +329,29 @@ Simple State Machine
ct.io.yellow_led.value = False ct.io.yellow_led.value = False
ct.io.red_led.value = False ct.io.red_led.value = False
# After 100 cycles (2s @ 20ms), go to yellow # After 2 seconds, go to yellow
ct.set_tonc("green_time", 100) ct.set_ton("green_time", 2000)
if ct.get_tonc("green_time"): if ct.get_ton("green_time"):
ct.var.state = "YELLOW" ct.var.state = "YELLOW"
elif ct.var.state == "YELLOW": elif ct.var.state == "YELLOW":
ct.io.green_led.value = False ct.io.green_led.value = False
ct.io.yellow_led.value = True ct.io.yellow_led.value = True
ct.set_tonc("yellow_time", 25) # 500ms ct.set_ton("yellow_time", 500)
if ct.get_tonc("yellow_time"): if ct.get_ton("yellow_time"):
ct.var.state = "RED" ct.var.state = "RED"
elif ct.var.state == "RED": elif ct.var.state == "RED":
ct.io.yellow_led.value = False ct.io.yellow_led.value = False
ct.io.red_led.value = True ct.io.red_led.value = True
ct.set_tonc("red_time", 150) # 3s ct.set_ton("red_time", 3000)
if ct.get_tonc("red_time"): if ct.get_ton("red_time"):
ct.var.state = "GREEN" ct.var.state = "GREEN"
rpi = revpimodio2.RevPiModIO(autorefresh=True) rpi = revpimodio2.RevPiModIO(autorefresh=True)
rpi.handlesignalend()
rpi.cycleloop(traffic_light) rpi.cycleloop(traffic_light)
Complex State Machine Complex State Machine
@@ -396,8 +381,8 @@ Complex State Machine
ct.io.yellow_led.value = True ct.io.yellow_led.value = True
# 2-second startup delay # 2-second startup delay
ct.set_tonc("startup", 100) ct.set_ton("startup", 2000)
if ct.get_tonc("startup"): if ct.get_ton("startup"):
ct.var.state = "RUNNING" ct.var.state = "RUNNING"
print("Running") print("Running")
@@ -422,8 +407,8 @@ Complex State Machine
# State: STOPPING - Controlled shutdown # State: STOPPING - Controlled shutdown
elif ct.var.state == "STOPPING": elif ct.var.state == "STOPPING":
# Coast motor for 1 second # Coast motor for 1 second
ct.set_tofc("coast", 50) ct.set_tof("coast", 1000)
ct.io.motor.value = ct.get_tofc("coast") ct.io.motor.value = ct.get_tof("coast")
if not ct.io.motor.value: if not ct.io.motor.value:
ct.var.state = "IDLE" ct.var.state = "IDLE"
@@ -432,7 +417,7 @@ Complex State Machine
# State: ERROR - Fault condition # State: ERROR - Fault condition
elif ct.var.state == "ERROR": elif ct.var.state == "ERROR":
ct.io.motor.value = False ct.io.motor.value = False
ct.io.red_led.value = ct.flag2c # Blink red ct.io.red_led.value = ct.flag5c # Blink red
if ct.changed(ct.io.ack_button, edge=revpimodio2.RISING): if ct.changed(ct.io.ack_button, edge=revpimodio2.RISING):
if not ct.io.error_sensor.value: if not ct.io.error_sensor.value:
@@ -442,8 +427,7 @@ Complex State Machine
if ct.last: if ct.last:
print(f"Total production: {ct.var.production_count}") print(f"Total production: {ct.var.production_count}")
rpi = revpimodio2.RevPiModIO(autorefresh=True) revpimodio.run_plc(machine_controller)
rpi.cycleloop(machine_controller)
Practical Examples Practical Examples
================== ==================
@@ -476,14 +460,17 @@ Temperature monitoring with hysteresis control:
# Warning if too hot # Warning if too hot
if temp > 85: if temp > 85:
ct.io.warning_led.value = ct.flag2c # Blink ct.core.a1green.value = False
ct.core.a1red.value = ct.flag5c # Blink
else:
ct.core.a1green.value = ct.flag5c # Blink
ct.core.a1red.value = False
# Emergency shutdown # Emergency shutdown
if temp > 95: if temp > 95:
ct.io.emergency_shutdown.value = True ct.io.emergency_shutdown.value = True
rpi = revpimodio2.RevPiModIO(autorefresh=True) revpimodio2.run_plc(temperature_monitor)
rpi.cycleloop(temperature_monitor)
Production Counter Production Counter
------------------ ------------------
@@ -520,8 +507,7 @@ Count production items with start/stop control:
print(f"Final count: {ct.var.total_count}") print(f"Final count: {ct.var.total_count}")
ct.var.total_count = 0 ct.var.total_count = 0
rpi = revpimodio2.RevPiModIO(autorefresh=True) revpimodio2.run_plc(production_counter)
rpi.cycleloop(production_counter)
Best Practices Best Practices
============== ==============
@@ -544,7 +530,7 @@ Minimize processing time in each cycle:
**Guidelines:** **Guidelines:**
* Avoid blocking operations (network, file I/O) * Avoid blocking operations (network, file I/O)
* Use flank flags for expensive operations * Use flank flags for expensive operations or even Threads
* Keep cycle time ≥20ms for stability * Keep cycle time ≥20ms for stability
Use Appropriate Cycle Time Use Appropriate Cycle Time

View File

@@ -94,29 +94,6 @@ Register callbacks for IO value changes:
* ``revpimodio2.FALLING`` - True to False transition * ``revpimodio2.FALLING`` - True to False transition
* ``revpimodio2.BOTH`` - Any change (default) * ``revpimodio2.BOTH`` - Any change (default)
Lambda Functions
----------------
Use lambda for simple callbacks:
.. code-block:: python
rpi = revpimodio2.RevPiModIO(autorefresh=True)
# Simple lambda callback
rpi.io.button.reg_event(
lambda name, val: print(f"Button: {val}")
)
# Lambda with edge filter
rpi.io.start_button.reg_event(
lambda name, val: print("Started!"),
edge=revpimodio2.RISING
)
rpi.handlesignalend()
rpi.mainloop()
Multiple Events Multiple Events
--------------- ---------------
@@ -212,12 +189,15 @@ Debouncing with Edge Detection
Timer Events Timer Events
============ ============
Execute callbacks at regular intervals independent of IO changes: The timer is started when the IO value changes and executes the passed
function - even if the IO value has changed in the meantime. If the
timer has not expired and the condition is met again, the timer is NOT
reset to the delay value or started a second time.
.. code-block:: python .. code-block:: python
def periodic_task(ioname, iovalue): def periodic_task(ioname, iovalue):
"""Called every 500ms.""" """Called after 500ms."""
print(f"Periodic task: {iovalue}") print(f"Periodic task: {iovalue}")
rpi = revpimodio2.RevPiModIO(autorefresh=True) rpi = revpimodio2.RevPiModIO(autorefresh=True)
@@ -255,8 +235,8 @@ Timer Event Parameters
**Parameters:** **Parameters:**
* ``interval`` - Milliseconds between calls * ``interval`` - Delay in milliseconds.
* ``prefire`` - If True, trigger immediately on registration * ``prefire`` - If True, trigger immediately after starting the mainloop.
Threaded Events Threaded Events
=============== ===============
@@ -425,29 +405,6 @@ Sensor Logging
rpi.handlesignalend() rpi.handlesignalend()
rpi.mainloop() rpi.mainloop()
Periodic Status Report
----------------------
.. code-block:: python
import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def status_report(ioname, iovalue):
"""Print system status every 10 seconds."""
print("=== Status Report ===")
print(f"Temperature: {rpi.core.temperature.value}°C")
print(f"CPU Frequency: {rpi.core.frequency.value} MHz")
print(f"IO Errors: {rpi.core.ioerrorcount.value}")
print()
# Status report every 10 seconds
rpi.io.dummy.reg_timerevent(status_report, 10000, prefire=True)
rpi.handlesignalend()
rpi.mainloop()
Threaded Data Processing Threaded Data Processing
------------------------- -------------------------
@@ -515,19 +472,6 @@ For slow operations, use threaded events:
rpi.io.trigger.reg_event(slow_task, as_thread=True) rpi.io.trigger.reg_event(slow_task, as_thread=True)
Use Debouncing
--------------
Always debounce mechanical inputs:
.. code-block:: python
# Good - Debounced button
rpi.io.button.reg_event(callback, delay=30)
# Poor - No debounce (may trigger multiple times)
rpi.io.button.reg_event(callback)
Handle Errors Gracefully Handle Errors Gracefully
------------------------- -------------------------
@@ -543,18 +487,6 @@ Protect callbacks from exceptions:
print(f"Error in callback: {e}") print(f"Error in callback: {e}")
rpi.io.output.value = False # Safe state rpi.io.output.value = False # Safe state
Check IO Existence
------------------
Verify IOs exist before registering events:
.. code-block:: python
if "optional_button" in rpi.io:
rpi.io.optional_button.reg_event(callback)
else:
print("Optional button not configured")
Clean Up Threads Clean Up Threads
---------------- ----------------

View File

@@ -24,13 +24,12 @@ Quick Example
**Cyclic Programming**:: **Cyclic Programming**::
import revpimodio2 import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def main(ct): def main(ct):
if ct.io.button.value: if ct.io.button.value:
ct.io.led.value = True ct.io.led.value = True
rpi.cycleloop(main) revpimodio2.run_plc(main)
**Event-Driven Programming**:: **Event-Driven Programming**::

View File

@@ -5,7 +5,7 @@ Installation
System Requirements System Requirements
=================== ===================
* Python 3.7 or higher * Python 3.2 or higher
* Revolution Pi hardware (Core, Core3, Connect, Compact, Flat) * Revolution Pi hardware (Core, Core3, Connect, Compact, Flat)
* piCtory configuration tool * piCtory configuration tool
@@ -24,6 +24,9 @@ Log out and log back in for the group change to take effect.
Installing RevPiModIO Installing RevPiModIO
===================== =====================
RevPiModIO is preinstalled on your Revolution Pi. It is distributed as debian package and will be
updated by `apt`.
Using pip Using pip
--------- ---------

View File

@@ -65,8 +65,6 @@ For continuous operation, use a cyclic loop:
import revpimodio2 import revpimodio2
rpi = revpimodio2.RevPiModIO(autorefresh=True)
def main_cycle(ct: revpimodio2.Cycletools): def main_cycle(ct: revpimodio2.Cycletools):
"""Called every cycle (default: 20-50ms).""" """Called every cycle (default: 20-50ms)."""
@@ -91,7 +89,7 @@ For continuous operation, use a cyclic loop:
print("Program stopped") print("Program stopped")
# Run cyclic loop # Run cyclic loop
rpi.cycleloop(main_cycle) revpimodio2.run_plc(main_cycle)
Event-Driven Program Event-Driven Program
-------------------- --------------------