Examples
This section provides comprehensive examples demonstrating various features of ModbusLink.
Basic Examples
Simple TCP Client
from modbuslink import ModbusClient, TcpTransport
# Create TCP transport
transport = TcpTransport(host='192.168.1.100', port=502)
client = ModbusClient(transport)
try:
# Connect to the server
client.connect()
# Read holding registers
registers = client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
print(f"Registers: {registers}")
# Write single register
client.write_single_register(
slave_id=1,
address=0,
value=1234
)
finally:
client.disconnect()
Simple RTU Client
from modbuslink import ModbusClient, RtuTransport
# Create RTU transport
transport = RtuTransport(
port='COM1',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1
)
client = ModbusClient(transport)
with client:
# Read coils
coils = client.read_coils(
slave_id=1,
start_address=0,
quantity=8
)
print(f"Coils: {coils}")
# Write multiple coils
client.write_multiple_coils(
slave_id=1,
start_address=0,
values=[True, False, True, False]
)
Simple ASCII Client
from modbuslink import ModbusClient, AsciiTransport
# Create ASCII transport
transport = AsciiTransport(
port='COM1',
baudrate=9600,
bytesize=7,
parity='E',
stopbits=1
)
client = ModbusClient(transport)
with client:
# Read holding registers
registers = client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=4
)
print(f"Registers: {registers}")
# Write single register
client.write_single_register(
slave_id=1,
address=0,
value=1234
)
Advanced Examples
Asynchronous Operations
import asyncio
from modbuslink import AsyncModbusClient, AsyncTcpTransport
async def async_modbus_operations():
transport = AsyncTcpTransport(host='192.168.1.100', port=502)
client = AsyncModbusClient(transport)
async with client:
# Concurrent read operations
tasks = [
client.read_holding_registers(slave_id=1, start_address=0, quantity=10),
client.read_holding_registers(slave_id=1, start_address=10, quantity=10),
client.read_holding_registers(slave_id=1, start_address=20, quantity=10)
]
results = await asyncio.gather(*tasks)
for i, registers in enumerate(results):
print(f"Block {i}: {registers}")
# Sequential write operations
for i in range(10):
await client.write_single_register(
slave_id=1,
address=i,
value=i * 100
)
# Run the async function
asyncio.run(async_modbus_operations())
Asynchronous RTU Operations
import asyncio
from modbuslink import AsyncModbusClient, AsyncRtuTransport
async def async_rtu_operations():
transport = AsyncRtuTransport(
port='COM1',
baudrate=9600,
timeout=3.0
)
client = AsyncModbusClient(transport)
async with client:
# Async read holding registers
registers = await client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
print(f"Registers: {registers}")
# Async write multiple registers
await client.write_multiple_registers(
slave_id=1,
start_address=0,
values=[100, 200, 300, 400]
)
asyncio.run(async_rtu_operations())
Asynchronous ASCII Operations
import asyncio
from modbuslink import AsyncModbusClient, AsyncAsciiTransport
async def async_ascii_operations():
transport = AsyncAsciiTransport(
port='COM1',
baudrate=9600,
timeout=3.0
)
client = AsyncModbusClient(transport)
async with client:
# Async read coils
coils = await client.read_coils(
slave_id=1,
start_address=0,
quantity=8
)
print(f"Coils: {coils}")
# Async write single coil
await client.write_single_coil(
slave_id=1,
address=0,
value=True
)
asyncio.run(async_ascii_operations())
Callback Mechanisms
from modbuslink import AsyncModbusClient, AsyncTcpTransport
import asyncio
def on_data_received(data):
print(f"Data received: {data}")
def on_error(error):
print(f"Error occurred: {error}")
async def callback_example():
transport = AsyncTcpTransport(host='192.168.1.100', port=502)
client = AsyncModbusClient(transport)
# Set callbacks
client.set_data_callback(on_data_received)
client.set_error_callback(on_error)
async with client:
# Operations will trigger callbacks
await client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
asyncio.run(callback_example())
Advanced Data Types
from modbuslink import ModbusClient, TcpTransport
transport = TcpTransport(host='192.168.1.100', port=502)
client = ModbusClient(transport)
with client:
# Float32 operations
temperature = 25.6
client.write_float32(
slave_id=1,
start_address=100,
value=temperature
)
read_temp = client.read_float32(
slave_id=1,
start_address=100
)
print(f"Temperature: {read_temp}°C")
# Int32 operations with custom byte/word order
counter_value = -123456
client.write_int32(
slave_id=1,
start_address=102,
value=counter_value,
byte_order='little',
word_order='big'
)
read_counter = client.read_int32(
slave_id=1,
start_address=102,
byte_order='little',
word_order='big'
)
print(f"Counter: {read_counter}")
# UInt32 operations
timestamp = 1640995200 # Unix timestamp
client.write_uint32(
slave_id=1,
start_address=104,
value=timestamp
)
read_timestamp = client.read_uint32(
slave_id=1,
start_address=104
)
print(f"Timestamp: {read_timestamp}")
Performance Testing Examples
Batch Operation Performance
import time
import asyncio
from modbuslink import AsyncModbusClient, AsyncTcpTransport
async def performance_test():
transport = AsyncTcpTransport(host='192.168.1.100', port=502)
client = AsyncModbusClient(transport)
async with client:
# Test batch read performance
start_time = time.time()
# Concurrent read of multiple register blocks
tasks = []
for i in range(10):
task = client.read_holding_registers(
slave_id=1,
start_address=i*10,
quantity=10
)
tasks.append(task)
results = await asyncio.gather(*tasks)
end_time = time.time()
print(f"Reading 100 registers took: {end_time - start_time:.3f}s")
print(f"Average per register: {(end_time - start_time)*1000/100:.2f}ms")
asyncio.run(performance_test())
Connection Pool Example
import asyncio
from modbuslink import AsyncModbusClient, AsyncTcpTransport
class ModbusConnectionPool:
def __init__(self, host, port, pool_size=5):
self.host = host
self.port = port
self.pool_size = pool_size
self.connections = asyncio.Queue(maxsize=pool_size)
async def initialize(self):
for _ in range(self.pool_size):
transport = AsyncTcpTransport(host=self.host, port=self.port)
client = AsyncModbusClient(transport)
await client.connect()
await self.connections.put(client)
async def get_connection(self):
return await self.connections.get()
async def return_connection(self, client):
await self.connections.put(client)
async def close_all(self):
while not self.connections.empty():
client = await self.connections.get()
await client.disconnect()
# Usage example
async def use_connection_pool():
pool = ModbusConnectionPool('192.168.1.100', 502)
await pool.initialize()
try:
# Get connection
client = await pool.get_connection()
# Perform operations
registers = await client.read_holding_registers(
slave_id=1, start_address=0, quantity=10
)
print(f"Read result: {registers}")
# Return connection
await pool.return_connection(client)
finally:
await pool.close_all()
asyncio.run(use_connection_pool())
Error Handling Examples
Comprehensive Error Handling
from modbuslink import ModbusClient, TcpTransport
from modbuslink.common.exceptions import (
ConnectionError, TimeoutError, CRCError,
InvalidResponseError, ModbusException
)
import time
def robust_modbus_client():
transport = TcpTransport(host='192.168.1.100', port=502, timeout=5.0)
client = ModbusClient(transport)
max_retries = 3
retry_delay = 1.0
for attempt in range(max_retries):
try:
client.connect()
# Perform operations
registers = client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
print(f"Successfully read registers: {registers}")
break
except ConnectionError as e:
print(f"Connection failed (attempt {attempt + 1}): {e}")
if attempt < max_retries - 1:
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
except TimeoutError as e:
print(f"Operation timed out (attempt {attempt + 1}): {e}")
if attempt < max_retries - 1:
time.sleep(retry_delay)
except CRCError as e:
print(f"CRC error detected: {e}")
# CRC errors usually indicate communication issues
break
except InvalidResponseError as e:
print(f"Invalid response received: {e}")
break
except ModbusException as e:
print(f"Modbus protocol error: {e}")
print(f"Exception code: {e.exception_code}")
break
except Exception as e:
print(f"Unexpected error: {e}")
break
finally:
try:
client.disconnect()
except:
pass
robust_modbus_client()
Logging Configuration
from modbuslink import ModbusClient, TcpTransport
from modbuslink.utils.logger import setup_logger
import logging
# Configure logging
setup_logger(
name='modbuslink',
level=logging.DEBUG,
log_file='modbus_operations.log',
console_output=True
)
# Create client with logging enabled
transport = TcpTransport(host='192.168.1.100', port=502)
client = ModbusClient(transport)
with client:
# All operations will be logged
registers = client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
client.write_single_register(
slave_id=1,
address=0,
value=1234
)
Integration Examples
Data Acquisition System
import asyncio
import json
from datetime import datetime
from modbuslink import AsyncModbusClient, AsyncTcpTransport
class DataAcquisitionSystem:
def __init__(self, host, port):
self.transport = AsyncTcpTransport(host=host, port=port)
self.client = AsyncModbusClient(self.transport)
self.data_buffer = []
async def start_acquisition(self, interval=1.0):
async with self.client:
while True:
try:
# Read multiple data points
temperature = await self.client.read_float32(
slave_id=1, start_address=100
)
pressure = await self.client.read_float32(
slave_id=1, start_address=102
)
flow_rate = await self.client.read_float32(
slave_id=1, start_address=104
)
# Create data record
record = {
'timestamp': datetime.now().isoformat(),
'temperature': temperature,
'pressure': pressure,
'flow_rate': flow_rate
}
self.data_buffer.append(record)
print(f"Data acquired: {record}")
# Save data periodically
if len(self.data_buffer) >= 10:
await self.save_data()
except Exception as e:
print(f"Acquisition error: {e}")
await asyncio.sleep(interval)
async def save_data(self):
if self.data_buffer:
filename = f"data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, 'w') as f:
json.dump(self.data_buffer, f, indent=2)
print(f"Saved {len(self.data_buffer)} records to {filename}")
self.data_buffer.clear()
# Usage
async def main():
daq = DataAcquisitionSystem('192.168.1.100', 502)
await daq.start_acquisition(interval=2.0)
asyncio.run(main())
Server Examples
Basic TCP Server
from modbuslink import AsyncTcpModbusServer, ModbusDataStore
import asyncio
import logging
async def main():
# Setup logging
logging.basicConfig(level=logging.INFO)
# Create data store
data_store = ModbusDataStore(
coils_size=1000,
discrete_inputs_size=1000,
holding_registers_size=1000,
input_registers_size=1000
)
# Set initial data
data_store.write_coils(0, [True, False, True, False, True, False, True, False])
data_store.write_holding_registers(0, [100, 200, 300, 400, 500])
data_store.write_input_registers(0, [250, 251, 252, 253, 254])
data_store.write_discrete_inputs(0, [False, True, False, True, False, True, False, True])
# Create TCP server
server = AsyncTcpModbusServer(
host="localhost",
port=5020,
data_store=data_store,
slave_id=1,
max_connections=5
)
print("Starting TCP server: localhost:5020")
print("Slave ID: 1")
try:
# Start server
await server.start()
print("TCP server started successfully!")
# Run forever
await server.serve_forever()
except KeyboardInterrupt:
print("\nReceived stop signal")
finally:
print("Stopping server...")
await server.stop()
print("Server stopped")
asyncio.run(main())
RTU Server Example
from modbuslink import AsyncRtuModbusServer, ModbusDataStore
import asyncio
import random
import math
async def simulate_industrial_data(data_store):
"""Simulate industrial equipment data"""
cycle = 0
while True:
try:
cycle += 1
# Simulate temperature sensor data
base_temps = [248, 179, 318, 447, 682]
temp_variations = [base + random.randint(-5, 5) + int(3 * math.sin(cycle * 0.1)) for base in base_temps]
data_store.write_input_registers(0, temp_variations)
# Simulate pressure sensor data
base_pressures = [1015, 1027, 996, 1043, 1004]
pressure_variations = [base + random.randint(-3, 3) + int(2 * math.cos(cycle * 0.15)) for base in base_pressures]
data_store.write_input_registers(10, pressure_variations)
# Simulate motor speed changes
current_speeds = data_store.read_holding_registers(0, 5)
new_speeds = [speed + random.randint(-50, 50) for speed in current_speeds]
new_speeds = [max(500, min(4000, speed)) for speed in new_speeds]
data_store.write_holding_registers(0, new_speeds)
if cycle % 20 == 0:
print(f"Industrial data update #{cycle}")
print(f" Temperature: {temp_variations}")
print(f" Pressure: {pressure_variations}")
print(f" Motor speeds: {new_speeds}")
await asyncio.sleep(2.0)
except Exception as e:
print(f"Data simulation error: {e}")
await asyncio.sleep(2.0)
async def main():
# Create data store
data_store = ModbusDataStore(
coils_size=1000,
discrete_inputs_size=1000,
holding_registers_size=1000,
input_registers_size=1000
)
# Initialize industrial equipment data
data_store.write_coils(0, [True, False, True, True, False, False, True, False]) # Motor status
data_store.write_coils(8, [False, True, False, True, True, False, False, True]) # Valve status
data_store.write_holding_registers(0, [1500, 2800, 3600, 1200, 750]) # Motor parameters
data_store.write_input_registers(0, [248, 179, 318, 447, 682]) # Temperature sensors
data_store.write_discrete_inputs(0, [True, False, True, True, False, True, False, True]) # Limit switches
# Create RTU server
server = AsyncRtuModbusServer(
port="COM3", # Modify according to actual situation
baudrate=9600,
data_store=data_store,
slave_id=1,
parity="N",
stopbits=1,
bytesize=8,
timeout=1.0
)
print("RTU server configuration:")
print(" Port: COM3")
print(" Baudrate: 9600")
print(" Data bits: 8, Stop bits: 1, Parity: None")
print(" Slave ID: 1")
try:
# Start server
await server.start()
print("RTU server started successfully!")
# Start data simulation task
simulation_task = asyncio.create_task(simulate_industrial_data(data_store))
server_task = asyncio.create_task(server.serve_forever())
# Wait for tasks to complete
await asyncio.gather(simulation_task, server_task)
except KeyboardInterrupt:
print("\nReceived stop signal")
except Exception as e:
print(f"\nServer runtime error: {e}")
finally:
print("Stopping server...")
await server.stop()
print("Server stopped")
asyncio.run(main())
ASCII Server Example
from modbuslink import AsyncAsciiModbusServer, ModbusDataStore
import asyncio
import random
import math
async def simulate_laboratory_experiment(data_store):
"""Simulate laboratory experiment process"""
experiment_time = 0
while True:
try:
experiment_time += 1
# Simulate temperature control process
target_temps = data_store.read_holding_registers(0, 5)
current_temps = data_store.read_input_registers(0, 5)
# Temperature gradually approaches target value
new_temps = []
for i, (current, target) in enumerate(zip(current_temps, target_temps)):
diff = target - current
change = diff * 0.1 + random.randint(-2, 2) + math.sin(experiment_time * 0.05) * 1
new_temp = current + change
new_temps.append(int(max(0, min(500, new_temp))))
data_store.write_input_registers(0, new_temps)
# Simulate humidity changes
base_humidity = [45, 52, 38, 48, 55]
humidity_variations = [base + random.randint(-5, 5) + int(2 * math.cos(experiment_time * 0.08)) for base in base_humidity]
humidity_variations = [max(0, min(100, h)) for h in humidity_variations]
data_store.write_input_registers(10, humidity_variations)
# Simulate pH value changes
base_ph = [700, 650, 720, 680, 710]
ph_variations = [base + random.randint(-10, 10) + int(3 * math.sin(experiment_time * 0.03)) for base in base_ph]
ph_variations = [max(0, min(1400, ph)) for ph in ph_variations]
data_store.write_input_registers(20, ph_variations)
if experiment_time % 15 == 0:
print(f"Experiment process simulation #{experiment_time}")
print(f" Temperature: {new_temps}")
print(f" Humidity: {humidity_variations}%")
print(f" pH: {[ph/100.0 for ph in ph_variations]}")
await asyncio.sleep(3.0)
except Exception as e:
print(f"Experiment simulation error: {e}")
await asyncio.sleep(3.0)
async def main():
# Create data store
data_store = ModbusDataStore(
coils_size=1000,
discrete_inputs_size=1000,
holding_registers_size=1000,
input_registers_size=1000
)
# Initialize laboratory equipment data
data_store.write_coils(0, [False, True, False, True, False, False, True, False]) # Heater control
data_store.write_coils(8, [True, True, False, False, True, True, False, False]) # Fan control
data_store.write_holding_registers(0, [250, 300, 180, 220, 350]) # Target temperature
data_store.write_input_registers(0, [248, 298, 178, 218, 348]) # Actual temperature
data_store.write_discrete_inputs(0, [False, True, False, False, True, True, False, True]) # Door switch status
# Create ASCII server
server = AsyncAsciiModbusServer(
port="COM4", # Modify according to actual situation
baudrate=9600,
data_store=data_store,
slave_id=2,
parity="E",
stopbits=1,
bytesize=7,
timeout=2.0
)
print("ASCII server configuration:")
print(" Port: COM4")
print(" Baudrate: 9600")
print(" Data bits: 7, Stop bits: 1, Parity: Even")
print(" Slave ID: 2")
try:
# Start server
await server.start()
print("ASCII server started successfully!")
# Start experiment simulation task
simulation_task = asyncio.create_task(simulate_laboratory_experiment(data_store))
server_task = asyncio.create_task(server.serve_forever())
# Wait for tasks to complete
await asyncio.gather(simulation_task, server_task)
except KeyboardInterrupt:
print("\nReceived stop signal")
except Exception as e:
print(f"\nServer runtime error: {e}")
finally:
print("Stopping server...")
await server.stop()
print("Server stopped")
asyncio.run(main())
Multi-Server Example
from modbuslink import (
AsyncTcpModbusServer,
AsyncRtuModbusServer,
AsyncAsciiModbusServer,
ModbusDataStore
)
import asyncio
import random
class MultiServerManager:
"""Multi-server manager"""
def __init__(self):
self.servers = {}
self.data_stores = {}
self.running = False
async def setup_servers(self):
"""Setup all servers"""
# TCP server
tcp_data_store = ModbusDataStore(coils_size=1000, discrete_inputs_size=1000,
holding_registers_size=1000, input_registers_size=1000)
tcp_data_store.write_coils(0, [True, False, True, False] * 10)
tcp_data_store.write_holding_registers(0, list(range(100, 150)))
tcp_server = AsyncTcpModbusServer(
host="localhost", port=5020, data_store=tcp_data_store, slave_id=1
)
self.servers["tcp"] = tcp_server
self.data_stores["tcp"] = tcp_data_store
# RTU server
rtu_data_store = ModbusDataStore(coils_size=1000, discrete_inputs_size=1000,
holding_registers_size=1000, input_registers_size=1000)
rtu_data_store.write_coils(0, [False, True, False, True] * 8)
rtu_data_store.write_holding_registers(0, [1500, 2800, 3600, 1200, 750])
rtu_server = AsyncRtuModbusServer(
port="COM3", baudrate=9600, data_store=rtu_data_store, slave_id=2
)
self.servers["rtu"] = rtu_server
self.data_stores["rtu"] = rtu_data_store
# ASCII server
ascii_data_store = ModbusDataStore(coils_size=1000, discrete_inputs_size=1000,
holding_registers_size=1000, input_registers_size=1000)
ascii_data_store.write_coils(0, [True, True, False, False] * 8)
ascii_data_store.write_holding_registers(0, [250, 300, 180, 220, 350])
ascii_server = AsyncAsciiModbusServer(
port="COM4", baudrate=9600, data_store=ascii_data_store, slave_id=3,
parity="E", stopbits=1, bytesize=7
)
self.servers["ascii"] = ascii_server
self.data_stores["ascii"] = ascii_data_store
async def start_all_servers(self):
"""Start all servers"""
print("Starting all servers...")
for server_type, server in self.servers.items():
try:
await server.start()
print(f"{server_type.upper()} server started successfully")
except Exception as e:
print(f"{server_type.upper()} server failed to start: {e}")
self.running = True
async def stop_all_servers(self):
"""Stop all servers"""
print("Stopping all servers...")
for server_type, server in self.servers.items():
try:
await server.stop()
print(f"{server_type.upper()} server stopped")
except Exception as e:
print(f"{server_type.upper()} server failed to stop: {e}")
self.running = False
async def simulate_data_changes(self):
"""Simulate data changes"""
cycle = 0
while self.running:
try:
cycle += 1
# Update data for each server
for server_type, store in self.data_stores.items():
if server_type == "tcp":
# Network monitoring data
traffic_data = [random.randint(100, 1000) for _ in range(10)]
store.write_input_registers(50, traffic_data)
elif server_type == "rtu":
# Industrial process data
temp_data = [random.randint(200, 400) for _ in range(5)]
store.write_input_registers(0, temp_data)
elif server_type == "ascii":
# Laboratory data
lab_data = [random.randint(180, 350) for _ in range(5)]
store.write_input_registers(0, lab_data)
# Update counter
store.write_holding_registers(999, [cycle])
if cycle % 20 == 0:
print(f"Data simulation cycle #{cycle}")
await asyncio.sleep(2.0)
except Exception as e:
print(f"Data simulation error: {e}")
await asyncio.sleep(2.0)
async def serve_forever(self):
"""Run forever"""
# Start data simulation task
simulation_task = asyncio.create_task(self.simulate_data_changes())
# Start serve_forever tasks for all servers
server_tasks = []
for server_type, server in self.servers.items():
try:
if await server.is_running():
server_tasks.append(asyncio.create_task(server.serve_forever()))
except Exception as e:
print(f"{server_type.upper()} server serve_forever failed to start: {e}")
# Wait for all tasks to complete
all_tasks = [simulation_task] + server_tasks
await asyncio.gather(*all_tasks, return_exceptions=True)
async def main():
manager = MultiServerManager()
try:
# Configure and start servers
await manager.setup_servers()
await manager.start_all_servers()
print("\nConnection information:")
print(" TCP server: localhost:5020 (Slave ID 1)")
print(" RTU server: COM3@9600,8,N,1 (Slave ID 2)")
print(" ASCII server: COM4@9600,7,E,1 (Slave ID 3)")
print("\nPress Ctrl+C to stop all servers")
# Run forever
await manager.serve_forever()
except KeyboardInterrupt:
print("\nReceived stop signal")
except Exception as e:
print(f"\nMulti-server runtime error: {e}")
finally:
await manager.stop_all_servers()
asyncio.run(main())
Process Control System
import asyncio
from modbuslink import AsyncModbusClient, AsyncTcpTransport
class ProcessController:
def __init__(self, host, port):
self.transport = AsyncTcpTransport(host=host, port=port)
self.client = AsyncModbusClient(self.transport)
self.setpoints = {
'temperature': 25.0,
'pressure': 1013.25
}
async def control_loop(self):
async with self.client:
while True:
try:
# Read process variables
current_temp = await self.client.read_float32(
slave_id=1, start_address=100
)
current_pressure = await self.client.read_float32(
slave_id=1, start_address=102
)
# Simple proportional control
temp_error = self.setpoints['temperature'] - current_temp
pressure_error = self.setpoints['pressure'] - current_pressure
# Calculate control outputs
heater_output = max(0, min(100, 50 + temp_error * 10))
pump_output = max(0, min(100, 50 + pressure_error * 5))
# Write control outputs
await self.client.write_float32(
slave_id=1, start_address=200, value=heater_output
)
await self.client.write_float32(
slave_id=1, start_address=202, value=pump_output
)
print(f"Temp: {current_temp:.2f}°C (SP: {self.setpoints['temperature']}°C), "
f"Heater: {heater_output:.1f}%")
print(f"Pressure: {current_pressure:.2f} mbar (SP: {self.setpoints['pressure']} mbar), "
f"Pump: {pump_output:.1f}%")
except Exception as e:
print(f"Control error: {e}")
await asyncio.sleep(1.0) # 1 second control loop
def set_temperature_setpoint(self, value):
self.setpoints['temperature'] = value
def set_pressure_setpoint(self, value):
self.setpoints['pressure'] = value
# Usage
async def main():
controller = ProcessController('192.168.1.100', 502)
# Start control loop
control_task = asyncio.create_task(controller.control_loop())
# Simulate setpoint changes
await asyncio.sleep(10)
controller.set_temperature_setpoint(30.0)
await asyncio.sleep(10)
controller.set_pressure_setpoint(1020.0)
# Run for a while
await asyncio.sleep(30)
control_task.cancel()
asyncio.run(main())