从 pi 5 主设备向从设备 arduino mega 发送两个字节时出现问题(使用 smbus2 库)

问题描述 投票:0回答:1

因此,我尝试使用 smbus2 库中的

write_byte
函数,并成功使用 Raspberry Pi 5 中的该函数来打开和关闭连接到 Arduino Mega 的 LED。

我的项目涉及一个 3 RPS 平行轴机械手,由 3 个步进器控制它,我正在通过 Pi 5 使用计算机视觉进行数据采集,计算我希望每个机械手电机达到的必要速度、步数和加速度。对于当前的测试,我只为 3 个电机中的每一个发送一组步数、速度和加速度的样本值。由于步长/速度/加速度的值可能高于 255,我认为我需要向 Arduino 发送两个字节,而不是为通过 i2c 发送的每个值发送一个字节,并且必须使用

write_i2c_block_data function
。由于我也无法真正使用串行监视器调试arduino代码,因为考虑到它通过USB电缆直接连接到Pi 5,所以我尝试在程序的不同部分打开LED,发现它没有进入“
receiveEvent
”功能如下代码所示。

这可能是什么问题?我在 Pi 5 端没有收到任何错误,当我运行代码(这是 arduino 代码的初始化部分)时,电机会回家,但电机根本不会响应已发送的 i2c 数据而移动。发送给他们。

RPi 5 上的 Python 代码:

from smbus2 import SMBus
from time import sleep
import struct

addr = 0x8 # bus address
bus = SMBus(1) # indicates /dev/i2c-1

steps = [600, 600, 600]
speeds = [500, 500, 500]
accels = [200, 200, 200]
ByteSteps = [0,0,0]
ByteSpeeds = [0,0,0]
ByteAccels = [0,0,0]

for i in range(0,3):
    ByteSteps[i] = list(struct.pack('>h', steps[i]))
    ByteSpeeds[i] = list(struct.pack('>h', speeds[i]))
    ByteAccels[i] = list(struct.pack('>h', accels[i]))

while True:
    for i in range (0,3):
        bus.write_i2c_block_data(addr, 0, ByteSteps[i])
        sleep(0.001)
        bus.write_i2c_block_data(addr, 0, ByteSpeeds[i])
        sleep(0.001)
        bus.write_i2c_block_data(addr, 0, ByteAccels[i])
        sleep(0.001)
    sleep(2)

这是 Arduino 代码:

#include <Wire.h>
#include <AccelStepper.h>
#include <cvzone.h>

// Define motors config
const int MAX_STEPPERS = 3;
const int STEP_PINS[MAX_STEPPERS] = {3, 6, 9};   // Step pins for motors 1, 2, 3
const int DIR_PINS[MAX_STEPPERS] = {2, 5, 8};    // Direction pins for motors 1, 2, 3

// Create instances of AccelStepper for each motor
AccelStepper steppers[MAX_STEPPERS] = {
  AccelStepper(AccelStepper::DRIVER, STEP_PINS[0], DIR_PINS[0]),
  AccelStepper(AccelStepper::DRIVER, STEP_PINS[1], DIR_PINS[1]),
  AccelStepper(AccelStepper::DRIVER, STEP_PINS[2], DIR_PINS[2])
};

// Array to store received parameters for each motor
volatile long receivedSteps[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 steps
volatile long receivedSpeed[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 speed
volatile long receivedAcceleration[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 acceleration

// Variables for managing serial input and commands
volatile bool runAllowed = false;

// The received integer
volatile int receivedValue = 0;

const int ledPin = 13; 

void setup() {
  // Initialize I2C communication as slave
  Wire.begin(0x8); // Address of the Arduino

  // Setup pin 13 as output and turn LED off at the beginning
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  // Set maximum speed and acceleration for each stepper motor
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setMaxSpeed(100000); // Speed = Steps / second
    steppers[i].setAcceleration(800); // Acceleration = Steps /(second)^2
    steppers[i].disableOutputs(); // Disable outputs initially
    steppers[i].setCurrentPosition(0); // Reset current position to 0
  }
  home();
  // Register function to run when data is received
  Wire.onReceive(receiveEvent);
}

void loop() {
  if (runAllowed) {
    runMotors();
  }
  delay(100);
}

// Function to parse integer from I2C data
int parse_Integer() {
  if (Wire.available() >= 2) { // We expect 2 bytes for the integer
    byte byte1 = Wire.read();
    byte byte2 = Wire.read();
    
    // Combine the two bytes to form the integer
    receivedValue = (byte1 << 8) | byte2;
    return receivedValue;
  }
  return 0;
}

// Function to run when data is received
void receiveEvent(int howMany) {
  digitalWrite(ledPin, '1');
  for (int i = 0; i < MAX_STEPPERS; i++) {
      receivedSteps[i] = parse_Integer();
      receivedSpeed[i] = parse_Integer();
      receivedAcceleration[i] = parse_Integer();
  }
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setAcceleration(receivedAcceleration[i]);
    steppers[i].setMaxSpeed(receivedSpeed[i]);
    steppers[i].move(receivedSteps[i]);
  }
  runAllowed = true;
}

void home() {
  runAllowed = true;
  for (int i = 0; i < MAX_STEPPERS; i++) {
    receivedSteps[i] = 200;
    receivedSpeed[i] = 800;
    receivedAcceleration[i] = 100;
  }
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setAcceleration(receivedAcceleration[i]);
    steppers[i].setMaxSpeed(receivedSpeed[i]);
    steppers[i].move(-1 * receivedSteps[i]);
  }
  runMotors();
  for (int i = 0; i < MAX_STEPPERS; i++) {
    steppers[i].setCurrentPosition(0);
  }
  runAllowed = false;
}

void runMotors() {
  if (runAllowed) {
    while (steppers[0].currentPosition() != -1*receivedSteps[0] && steppers[1].currentPosition() != -1*receivedSteps[1] && steppers[2].currentPosition() != -1*receivedSteps[2]) {
      for (int i = 0; i < MAX_STEPPERS; i++) {
        steppers[i].enableOutputs(); // Enable outputs for all motors
        steppers[i].run(); // Step each motor
      }
      // if (Serial.available() > 0) return;
    }
    runAllowed = false;
  }
  if (!runAllowed) {
    for (int i = 0; i < MAX_STEPPERS; i++) {
      steppers[i].disableOutputs(); // Disable outputs for all motors
    }
  }
}
python arduino raspberry-pi i2c smbus
1个回答
0
投票

将数据组织到

struct
中,然后将传感器的所有值作为字节数组在一个数据包中发送,这似乎对您的场景很有帮助。

当您想要将多个变量连接到一个字节数组中以通过蓝牙、i2c、lora 或任何其他适用于数组的协议发送时,这非常有用。

换句话说,你有一个固定的字节数组,然后将不同(或相同)数据类型和长度的变量挤入其中,同时仍然将其用作字节数组。

下面 Arduino 示例中的联合是:联合是一个固定的内存区域,我们可以以一种格式(作为结构)写入,然后以另一种格式(字节数组)读取。

您也可以反向操作,将其作为字节数组写入,并将其作为结构体读取。

如何在Python中打包字节的示例:

import struct

step = [600, 600, 600]
speed = [500, 500, 500]
accel = [200, 200, 200]
sensor_count = 3

for sensor_id in range(sensor_count):
    bytes_to_send = struct.pack('<hhhh',
                                sensor_id,
                                step[sensor_id],
                                speed[sensor_id],
                                accel[sensor_id])
    print("send packed bytes of", bytes_to_send.hex('_', 2))
    # bus.write_i2c_block_data(addr, 0, bytes_to_send)


给出以下输出:

send packed bytes of 0000_5802_f401_c800
send packed bytes of 0100_5802_f401_c800
send packed bytes of 0200_5802_f401_c800

如何在 Arduino 上解压字节的示例:

typedef struct sensorData_t{
  short sensorId;
  short step;
  short speed;
  short accel;
};

typedef union I2C_Packet_t{
  sensorData_t sensor;
  byte I2CPacket[sizeof(sensorData_t)];
};


const int PACKET_SIZE = sizeof(sensorData_t);
int test_data[PACKET_SIZE] = {0x01, 0x00, 0x58, 0x02, 0xf4, 0x01, 0xc8, 0x00};

I2C_Packet_t read_bytes;

I2C_Packet_t parse_packet() {
  // if (Wire.available() >= PACKET_SIZE) {
    for(int i=0; i < PACKET_SIZE; i++) {
      // read_bytes.I2CPacket[i] = Wire.read();
      read_bytes.I2CPacket[i] = test_data[i];
    }
    return read_bytes;
  // }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() {
  read_bytes = parse_packet();
  Serial.print(read_bytes.sensor.sensorId);
  Serial.print('-');
  Serial.print(read_bytes.sensor.step);
  Serial.print('-');
  Serial.print(read_bytes.sensor.speed);
  Serial.print('-');
  Serial.print(read_bytes.sensor.accel);
  Serial.println();
  delay(1000);
}

并在串行监视器中给出以下输出:

1-600-500-200
1-600-500-200
1-600-500-200
1-600-500-200

注意:我在没有实际连接 RPi 和 Arduino 的 i2c 的情况下对此进行了测试。希望注释掉的代码显示了如果您确实正确连接了设备,则需要什么。

© www.soinside.com 2019 - 2024. All rights reserved.