2025.3.28
鞋垫数据蓝牙数据的接收和发送
1.服务器端
需要修改服务器端代码,使得PC能同时接收两个蓝牙设备的数据,并分别将这些数据发送到不同的COM串口。首先,我需要理解用户提供的原始代码的结构和功能。原始代码是一个服务器,监听一个端口,接收数据并通过一个固定的COM口发送。用户现在希望扩展为处理两个蓝牙设备,每个设备对应不同的COM口。
首先,我需要确定如何同时处理两个蓝牙连接。通常,这可以通过多线程或多进程来实现,每个线程处理一个客户端连接。由于Python的线程适合I/O密集型任务,这里选择多线程比较合适。
接下来,每个蓝牙设备的数据需要发送到不同的COM口。用户可能有两个蓝牙模块分别连接到不同的COM口,比如COM9和COM10。因此,服务器需要为每个连接分配一个特定的COM口。这可以通过在客户端连接时,根据连接的端口或其他标识来动态分配对应的COM口。
但用户提供的原始代码中,服务器只监听一个端口。为了区分两个蓝牙设备,可能需要让服务器监听两个不同的端口,每个端口对应一个蓝牙设备。例如,蓝牙设备1连接到端口12345,设备2连接到端口12346。这样,服务器可以创建两个监听套接字,分别处理不同端口的连接,并将数据发送到对应的COM口。
或者,可以在同一个端口上接受多个连接,但需要某种方式区分不同的设备。比如,设备在连接后发送一个标识符,服务器根据标识符选择对应的COM口。但这种方法需要设备端配合,可能在实现上更复杂。
考虑到用户之前的交流中,客户端代码已经被修改为使用两个不同的端口(12345和12346)发送数据,因此服务器端也应该监听这两个端口,每个端口对应一个COM口。这样,每个端口的数据独立处理,分别发送到不同的串口。
因此,服务器端代码需要同时监听两个端口,每个端口的连接处理线程中使用不同的串口配置。这可以通过为每个端口创建一个线程,每个线程运行一个监听循环,处理对应端口的连接和数据传输。
另外,需要处理多个串口连接,确保每个串口正确初始化和关闭。同时,异常处理需要完善,避免一个端口的错误影响另一个端口。
现在,我需要编写代码结构,使用多线程监听两个端口,每个端口对应一个COM口。例如,端口12345对应COM9,端口12346对应COM10。每个连接处理线程中,接收到数据后写入对应的串口。
同时,需要测试代码的正确性,确保数据从不同端口接收后发送到正确的COM口。此外,需要考虑资源管理,如正确关闭套接字和串口连接,避免资源泄漏。
可能遇到的问题包括端口占用、串口权限问题,以及多线程间的同步问题。需要确保每个线程独立处理自己的资源,避免竞争条件。
总结修改步骤:
- 创建两个服务器套接字,分别监听两个端口。
- 为每个端口分配对应的COM口参数。
- 使用多线程处理每个服务器的连接。
- 每个连接线程中,接收到数据后发送到对应的COM口。
- 完善异常处理和资源释放。
最后,验证代码是否满足用户需求,即PC接收两个蓝牙设备的数据,并分别发送到不同的COM口。
以下是修改后的服务器端代码,支持 同时接收两个蓝牙设备的数据并分别转发到不同的COM串口:
import socket
import serial
import threading
# 配置:每个蓝牙设备对应独立的端口和串口
DEVICE_CONFIGS = [
{
'server_port': 12345, # 蓝牙设备1的监听端口
'com_port': 'COM9', # 对应要发送的串口
'baud_rate': 115200
},
{
'server_port': 12346, # 蓝牙设备2的监听端口
'com_port': 'COM10', # 另一个串口
'baud_rate': 115200
}
]
class SerialServer(threading.Thread):
def __init__(self, server_port, com_port, baud_rate):
super().__init__()
self.server_port = server_port
self.com_port = com_port
self.baud_rate = baud_rate
self.running = True
def run(self):
# 初始化串口
try:
ser = serial.Serial(self.com_port, self.baud_rate)
print(f"Serial {self.com_port} opened")
except serial.SerialException as e:
print(f"Failed to open {self.com_port}: {e}")
return
# 初始化网络服务
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind(('0.0.0.0', self.server_port))
s.listen()
print(f"Listening on port {self.server_port} for {self.com_port}")
while self.running:
conn, addr = s.accept()
print(f"Connected to {addr} for port {self.server_port}")
with conn:
try:
while self.running:
data = conn.recv(1024)
if not data:
break
# 转发到串口
ser.write(data)
print(f"[Port {self.server_port} -> {self.com_port}] Sent: {data.hex()}")
except (ConnectionResetError, socket.error) as e:
print(f"Connection error on port {self.server_port}: {e}")
break
except Exception as e:
print(f"Server error on port {self.server_port}: {e}")
finally:
ser.close()
def stop(self):
self.running = False
if __name__ == "__main__":
# 启动两个服务线程
threads = []
for config in DEVICE_CONFIGS:
thread = SerialServer(
server_port=config['server_port'],
com_port=config['com_port'],
baud_rate=config['baud_rate']
)
thread.start()
threads.append(thread)
try:
# 主线程保持运行
while True:
pass
except KeyboardInterrupt:
print("\nShutting down...")
for thread in threads:
thread.stop()
for thread in threads:
thread.join()
print("All threads stopped")
主要改进说明:
-
多设备独立通道:
- 每个蓝牙设备对应独立的 端口(12345/12346)和 串口(COM9/COM10)
- 使用线程隔离不同设备的数据流
-
数据流向清晰:
plaintext
蓝牙设备1 --[Port 12345]--> COM9 蓝牙设备2 --[Port 12346]--> COM10
-
增强功能:
- 线程安全退出机制
- 端口复用设置 (
SO_REUSEADDR
) - 详细的错误日志分类
使用说明:
-
硬件连接:
- 蓝牙模块1连接到 COM9
- 蓝牙模块2连接到 COM10
-
客户端配置:
- 蓝牙设备1的客户端连接
192.168.11.88:12345
- 蓝牙设备2的客户端连接
192.168.11.88:12346
- 蓝牙设备1的客户端连接
2.树莓派端
好的,我现在需要帮用户修改他们的Python代码,使其能够同时接收来自两个USB设备(ttyUSB0和ttyUSB1)的数据,并通过WiFi将这两个设备的数据分别传输到PC的不同变量。首先,我需要理解用户的需求和现有代码的结构。
用户现有的代码使用serial库读取一个USB设备的数据,并通过socket发送到指定的服务器IP和端口。现在他们希望扩展这个功能,支持两个USB设备,并且每个设备的数据要发送到不同的变量,可能意味着不同的端口或不同的数据处理方式。
首先,我需要考虑如何同时监听两个串口设备。原来的代码中使用了单个串口对象,现在需要创建两个串口实例,分别对应USB0和USB1。同时,需要确保这两个串口的读取不会互相阻塞,可能需要多线程或者异步处理。
接下来是网络传输部分。用户希望将两个设备的数据分别发送到不同的变量。这里的“不同变量”可能指的是不同的端口,或者服务器端的不同处理端点。因此,可能需要为每个设备创建独立的socket连接,或者使用同一个socket但发送到不同的端口,或者在数据中包含设备标识,让服务器区分来源。
然后要考虑代码的结构是否需要调整。原来的代码在一个循环中读取一个串口的数据,现在需要同时处理两个串口。如果使用多线程,每个串口可以在单独的线程中读取数据,并分别发送。或者使用select库来同时监控多个串口的可读状态,避免阻塞。
另外,异常处理也需要扩展,确保一个设备的错误不会影响另一个设备的运行。例如,如果USB0发生异常,USB1应该继续工作,反之亦然。同时,网络连接也需要稳定,如果连接断开,可能需要重新连接。
还需要考虑数据格式的问题。现有的代码将数据转换为hex字符串发送,可能需要为每个设备的数据添加标识符,比如在数据前加上“USB0:”和“USB1:”,或者使用不同的消息格式,以便服务器区分来源。
现在,我需要验证这些思路是否正确。比如,使用多线程处理两个串口的读取,每个线程独立处理自己的数据发送。或者使用select来同时监听多个串口的事件,避免线程的开销。
然后,编写代码的大致步骤可能是:
- 创建两个串口对象,分别连接到ttyUSB0和ttyUSB1。
- 创建两个socket连接,或者使用同一个socket但发送到不同的端口。
- 使用多线程或select来处理同时读取两个串口的数据。
- 在发送数据时,添加设备标识,或者发送到不同的服务器端口。
- 修改异常处理,确保一个设备的错误不会导致整个程序终止。
- 测试代码,确保两个设备的数据能正确传输并被服务器区分。
需要注意的是,如果两个串口同时有大量数据,可能会影响性能,因此需要考虑缓冲区处理和发送效率。此外,服务器的配置也需要相应调整,以接收并处理来自两个不同源的数据。
最后,测试修改后的代码,确保功能符合用户需求,并处理可能出现的错误情况,比如串口断开、网络中断等。
以下是修改后的代码,实现同时读取两个蓝牙设备(/dev/ttyUSB0
和/dev/ttyUSB1
)的数据,并分别通过WiFi传输到PC的不同端口:
import serial
import socket
import threading
# 设备配置
DEVICES = [
{
'port': '/dev/ttyUSB0',
'baud': 115200,
'server_ip': '192.168.11.88',
'server_port': 12345 # 设备1使用12345端口
},
{
'port': '/dev/ttyUSB1',
'baud': 115200,
'server_ip': '192.168.11.88',
'server_port': 12346 # 设备2使用12346端口
}
]
class BluetoothHandler(threading.Thread):
def __init__(self, config):
super().__init__()
self.config = config
self.ser = None
self.sock = None
self.running = True
def run(self):
try:
# 初始化串口
self.ser = serial.Serial(
self.config['port'],
self.config['baud'],
timeout=0.1
)
# 初始化网络连接
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.config['server_ip'], self.config['server_port']))
print(f"Device {self.config['port']} connected to {self.config['server_ip']}:{self.config['server_port']}")
while self.running:
try:
# 读取串口数据
if self.ser.in_waiting > 0:
data = self.ser.read(self.ser.in_waiting)
if data:
# 直接发送原始字节数据(无标识符)
self.sock.sendall(data)
print(f"[{self.config['port']}] Sent {len(data)} bytes")
except serial.SerialException as e:
print(f"Serial Error ({self.config['port']}): {e}")
break
except Exception as e:
print(f"Connection Failed ({self.config['port']}): {e}")
finally:
self.cleanup()
def cleanup(self):
self.running = False
if self.ser and self.ser.is_open:
self.ser.close()
if self.sock:
self.sock.close()
print(f"Device {self.config['port']} resources released")
if __name__ == "__main__":
threads = []
for device in DEVICES:
handler = BluetoothHandler(device)
handler.start()
threads.append(handler)
try:
while True:
pass
except KeyboardInterrupt:
print("\nStopping threads...")
for t in threads:
t.running = False
for t in threads:
t.join()
print("All threads stopped")
主要改进说明:
- 多设备支持:
- 使用线程为每个蓝牙设备创建独立的数据通道
- 配置文件
DEVICES
支持灵活扩展设备数量
- 数据区分:
- 为每个设备添加唯一标识符
tag
- 使用不同端口号区分数据流(12345和12346)
- 数据格式示例:
DEVICE00:48656c6c6f
(标识+HEX数据)(已更改为不发送标识符)
- 稳定性增强:
- 独立的异常处理机制(单个设备故障不影响其他设备)
- 完善的资源释放机制
- 支持键盘中断退出
主要改动说明:
-
移除标识符
删除formatted
相关代码,直接发送原始字节数据:python
self.sock.sendall(data) # 原代码发送的是带标签的字符串
-
简化配置
移除配置中的'tag'
字段,通过端口号区分设备:python
'server_port': 12345 # 设备1使用12345端口 'server_port': 12346 # 设备2使用12346端口
-
日志优化
打印信息改为通过端口号和串口号标识设备:python
print(f"Device {self.config['port']} connected...")
注意:两个用于连接pc然后使用软件的蓝牙,都必须是从机状态(绿灯),否则无法被软件识别
更改为从机的指令为
"AT+ROLE=2/r/n"