我的 macOS 系统和 Arduino 设备之间的串行通信遇到问题。除非 minicom 正在运行,否则我的 Rust 和 Python 脚本都无法向 Arduino 发送消息。下面是问题的详细描述:
问题总结
- 环境:macOS(mac硅胶M3 max)、Arduino、Rust、Python
- 串口:/dev/cu.usbmodem101
- 波特率:9600
- 行为:
- 当 minicom 未运行时:Rust 和 Python 脚本都无法成功向 Arduino 发送消息。 arduino RX 灯会亮起,但内置 LED 会闪烁 3 次,这不是预期的行为。
- 当 minicom 运行时:Rust 和 Python 脚本都可以正常工作,并且可以向 Arduino 发送具有预期结果的消息。
- 即使不使用 minicom,只需从 Arduino IDE 中的 SerialMonitor 发送正确的 json 即可完美工作。
Tauri 应用程序中的 rust 片段。
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use serialport::SerialPort;
use std::time::Duration;
use serde::Serialize;
#[cfg(feature = "hardware-support")]
use std::fs::File;
#[tauri::command]
fn get_temperature() -> Result<f32, String> {
#[cfg(feature = "hardware-support")]
{
// Path to the temperature sensor's device file
let path = "/sys/bus/w1/devices/28-01191ee2142f/w1_slave"; // Replace '28-xxxx' with your sensor's ID
let mut file = File::open(path).map_err(|e| e.to_string())?; // Convert the error to a String;
let mut contents = String::new();
file.read_to_string(&mut contents).map_err(|e| e.to_string())?;
// The temperature data is usually at the end of the second line, after 't='
let temp_str = contents.split_whitespace().last().unwrap().split("=").nth(1).unwrap();
let temp_raw: i32 = temp_str.parse().unwrap();
// Convert raw temperature to Celsius
Ok(temp_raw as f32 / 1000.0)
}
#[cfg(not(feature = "hardware-support"))]
{
println!("Hardware support is not enabled. Temp running in stub mode.");
Ok(69.69)
}
}
#[derive(Serialize)]
struct BlinkData {
blinks: u8,
}
#[derive(Serialize)]
struct BlinkMessage {
message_type: String,
data: BlinkData,
}
#[tauri::command]
fn send_blink_message(port_name: &str, num_blinks: u8) -> Result<(), String> {
// Open the serial port
let mut port = serialport::new(port_name, 9600).open().expect("Failed to open port");
// Create the JSON message
let message = BlinkMessage {
message_type: String::from("blink_led"),
data: BlinkData { blinks: num_blinks },
};
// Serialize the message to JSON
let json_message = match serde_json::to_string(&message) {
Ok(json) => json,
Err(e) => return Err(format!("Failed to serialize message: {}", e)),
};
// Send the JSON message over the serial port
if let Err(e) = port.write_all(json_message.as_bytes()) {
return Err(format!("Failed to write to port: {}", e));
}
// Print the JSON message to the console for debugging
println!("JSON Message Sent: {}", json_message);
// Add a newline to indicate the end of the message
if let Err(e) = port.write_all(b"\n") {
return Err(format!("Failed to write newline to port: {}", e));
}
Ok(())
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![get_temperature, send_blink_message])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
带有大量串行特定设置的 Python 示例。
import serial
import json
import time
def configure_serial_port(port_name):
ser = serial.Serial(
port=port_name,
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=10,
xonxoff=False,
rtscts=False,
dsrdtr=False
)
ser.dtr = False
ser.rts = False
ser.close()
def send_blink_message(port_name, num_blinks):
try:
# Configure the serial port settings
configure_serial_port(port_name)
# Open the serial port
ser = serial.Serial(port_name, 9600, timeout=10)
# Add a short delay to ensure the Arduino is ready to receive the message
time.sleep(0.1)
# Create the JSON message
message = {
"message_type": "blink_led",
"data": {"blinks": num_blinks}
}
# Serialize the message to JSON
json_message = json.dumps(message)
# Print the JSON message for debugging
print("JSON Message:", json_message)
# Send the JSON message over the serial port
print("Sending JSON message...")
ser.write(json_message.encode('utf-8'))
# Add a newline to indicate the end of the message
print("Sending newline...")
ser.write(b"\n")
# Flush the port to ensure all data is sent
print("Flushing port...")
ser.flush()
# Add a delay to ensure everything is sent before exiting the function
time.sleep(0.1)
print("Message sent to Arduino")
ser.close()
return True
except serial.SerialException as e:
print(f"Failed to open port: {e}")
return False
if __name__ == "__main__":
port_name = "/dev/cu.usbmodem101" # Change this to your port
num_blinks = 3 # Set the number of blinks
send_blink_message(port_name, num_blinks)
这是草图
#include <ArduinoJson.h>
// Define the built-in LED pin
const int ledPin = LED_BUILTIN;
void setup() {
// Initialize the built-in LED pin as an output
pinMode(ledPin, OUTPUT);
// Start the serial communication at 9600 baud rate
Serial.begin(9600);
}
void loop() {
// Check if there is any data available on the serial port
if (Serial.available() > 0) {
// Read the incoming serial data
String incomingMessage = Serial.readStringUntil('\n');
// Parse the JSON message
StaticJsonDocument<200> jsonDoc;
DeserializationError error = deserializeJson(jsonDoc, incomingMessage);
// Check if parsing was successful
if (!error) {
// Extract the message type and blinks value
const char* message_type = jsonDoc["message_type"];
int blinks = jsonDoc["data"]["blinks"];
// Check if the message type is 'blink_led'
if (strcmp(message_type, "blink_led") == 0) {
// Blink the LED the specified number of times
for (int i = 0; i < blinks; i++) {
digitalWrite(ledPin, HIGH); // Turn the LED on
delay(500); // Wait for 500 milliseconds
digitalWrite(ledPin, LOW); // Turn the LED off
delay(500); // Wait for 500 milliseconds
}
}
} else {
// If parsing failed, print an error message
Serial.println("Failed to parse JSONS");
}
}
}
问题
- 为什么脚本仅在 minicom 运行时才工作?
- minicom 可能设置哪些特定配置或状态,我需要在脚本中复制?
- 如何调整我的 Rust 和 Python 代码无需运行 minicom 即可工作?
我在 ChatGPT 上花费了很多时间、很多天的时间。来自知识渊博的人的任何见解或建议将不胜感激。谢谢你!
听起来遇到的问题是 macOS 上串行端口的默认状态与 Arduino 所需的状态不一致。 Minicom 通过在打开端口时设置某些串行端口控制信号来解决此问题,而的 Rust 和 Python 脚本则没有。
以下是一些可能的原因以及如何解决它们:
1. DTR/RTS 信号
- 问题 : Arduino 板通常使用 DTR(数据终端就绪)或 RTS(请求发送)信号进行复位。 Minicom 默认可能会断言(设置为高电平)这些信号之一,而的脚本可能没有。当 DTR 或 RTS 被断言,然后又取消断言时,Arduino 会复位。这就是为什么在 Minicom 连接时的代码可以工作的原因,因为 Minicom 会执行复位操作,使 Arduino 准备好接收数据。
- 解决方案 : 在的 Rust 和 Python 脚本中,尝试在打开串口后立即切换 DTR 或 RTS 信号。
Python (使用 pyserial 库):
```python import serial import time
ser = serial.Serial('/dev/cu.usbmodem101', 9600)
# 切换 DTR 信号 ser.dtr = False # 取消断言 DTR time.sleep(0.1) # 短暂延迟 ser.dtr = True # 断言 DTR ```
Rust (使用 serialport 库):
```rust use std::time::Duration; use serialport::SerialPort;
let mut port = serialport::new("/dev/cu.usbmodem101", 9600) .open() .expect("无法打开端口");
// serialport 库没有直接控制 DTR/RTS 的方法,但可以使用以下方法: port.as_mut().unwrap().set_rts(true).unwrap(); // 断言 RTS port.as_mut().unwrap().set_rts(false).unwrap(); // 取消断言 RTS thread::sleep(Duration::from_millis(100)); ```
2. 波特率协商
- 问题 : 某些 Arduino 板在启动时需要进行短暂的波特率协商。如果的脚本发送数据太快,Arduino 可能无法正确接收。
- 解决方案 : 在打开串口并发送任何数据之前,添加短暂的延迟。
python
import time
time.sleep(2) # 等待 2 秒
rust
use std::thread;
use std::time::Duration;
thread::sleep(Duration::from_secs(2));
3. 代码中的错误
- 问题 : 的代码中可能存在其他错误,例如错误的串口名称或数据格式问题。
- 解决方案 : 仔细检查的代码,确保所有内容都正确无误。尝试在发送数据后刷新串口缓冲区,以确保所有数据都已发送到 Arduino。
python
ser.flush() # 刷新输出缓冲区
rust
port.flush().unwrap();
调试步骤 :
-
监视串口
: 使用串口监视器(如 Arduino IDE 中的串口监视器或 macOS 上的
screen
命令)来观察发送和接收的数据。这将有助于确定问题是出在数据传输还是接收方面。 - 测试不同的设置 : 尝试不同的波特率、数据位、停止位和校验位设置,以查看是否有任何组合可以解决问题。
- 简化代码 : 将的代码简化为仅发送和接收基本数据的最简版本。这将有助于隔离问题。
如果以上步骤均未解决问题,请提供以下信息,以便我为提供更具体的帮助:
- 使用的 Arduino 板的型号。
- 使用的 macOS 版本。
- 使用的 Rust 和 Python 库的版本。
- 的 Arduino 代码和 Python/Rust 代码的完整版本。
- 在串口监视器中看到的任何错误消息或意外输出。