首页 > 编程问答 >仅当 Minicom 在 macOS 上运行时,与 Arduino 的串行通信才有效

仅当 Minicom 在 macOS 上运行时,与 Arduino 的串行通信才有效

时间:2024-07-26 07:39:03浏览次数:14  
标签:python rust arduino serial-port tauri

我的 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");
    }
  }
}

问题

  1. 为什么脚本仅在 minicom 运行时才工作?
  2. minicom 可能设置哪些特定配置或状态,我需要在脚本中复制?
  3. 如何调整我的 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();

调试步骤 :

  1. 监视串口 : 使用串口监视器(如 Arduino IDE 中的串口监视器或 macOS 上的 screen 命令)来观察发送和接收的数据。这将有助于确定问题是出在数据传输还是接收方面。
  2. 测试不同的设置 : 尝试不同的波特率、数据位、停止位和校验位设置,以查看是否有任何组合可以解决问题。
  3. 简化代码 : 将的代码简化为仅发送和接收基本数据的最简版本。这将有助于隔离问题。

如果以上步骤均未解决问题,请提供以下信息,以便我为提供更具体的帮助:

  • 使用的 Arduino 板的型号。
  • 使用的 macOS 版本。
  • 使用的 Rust 和 Python 库的版本。
  • 的 Arduino 代码和 Python/Rust 代码的完整版本。
  • 在串口监视器中看到的任何错误消息或意外输出。

标签:python,rust,arduino,serial-port,tauri
From: 78793315

相关文章