You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Raspberry Pi与Arduino I2C通信问题:如何同步响应且适配短周期命令?

Alright, let's tackle this I2C communication issue you're facing between Raspberry Pi (master) and Arduino (slave) paired with a DALI power supply. The core problems here are getting fresh responses from Arduino after its 40ms DALI operation, while also handling high-frequency commands that can't afford to wait around. Let's break this down with practical solutions and code examples.

Problem Breakdown

First, let's clarify why you're seeing stale responses:

  • When RPi sends a command and immediately reads the response, Arduino's loop() hasn't finished the 40ms DALI operation yet, so it returns old cached data.
  • Some commands need to run on a <100ms cycle, so blocking RPi to wait for the full 40ms DALI process isn't feasible.

Solution: State Machine + Dual Data Buffers

The fix relies on splitting your commands into two categories, each handled with dedicated logic on Arduino, and matching RPi code to interact with those states.

1. Commands That Need Fresh DALI Responses (Wait for Arduino)

For commands like "toggle light and get updated status", we'll have Arduino track a processing state, and RPi polls until the operation is done.

2. High-Frequency Periodic Commands (No Wait Needed)

For commands that just need the latest DALI state on a tight cycle, Arduino will maintain a real-time buffer that gets updated periodically in loop(), so RPi can read it instantly.


Arduino Code Implementation

This code uses a state machine to track ongoing DALI operations, plus a buffer for fast updates:

#include <Wire.h>

#define I2C_SLAVE_ADDR 0x08

// Define command types to distinguish our two use cases
enum CommandType {
  CMD_GET_DALI_FRESH_STATUS = 0x01, // Requires waiting for DALI
  CMD_GET_DALI_FAST_STATUS = 0x02   // High-frequency, use cached data
};

// Track command state and data buffers
struct CmdHandler {
  CommandType activeCmd;
  bool isProcessing;
  byte freshDaliResponse;
  byte fastDaliBuffer;
};

CmdHandler cmdHandler;

void setup() {
  Wire.begin(I2C_SLAVE_ADDR);
  Wire.onReceive(receiveRpiCommand);
  Wire.onRequest(sendResponseToRpi);
  
  // Initialize state
  cmdHandler.isProcessing = false;
  cmdHandler.freshDaliResponse = 0x00;
  cmdHandler.fastDaliBuffer = 0x00;
}

void loop() {
  // Update fast buffer every 50ms (fits under 100ms cycle)
  static unsigned long lastFastUpdate = 0;
  if (millis() - lastFastUpdate >= 50) {
    lastFastUpdate = millis();
    cmdHandler.fastDaliBuffer = readDaliQuickState(); // Replace with your DALI read
  }

  // Handle ongoing DALI operations for fresh commands
  if (cmdHandler.isProcessing) {
    static unsigned long processStartTime = 0;
    // Wait for 40ms DALI operation to complete
    if (millis() - processStartTime >= 40) {
      cmdHandler.freshDaliResponse = readDaliFinalState(); // Replace with your DALI read
      cmdHandler.isProcessing = false;
    }
  }
}

// Receive command from RPi
void receiveRpiCommand(int byteCount) {
  if (byteCount >= 1) {
    byte cmdByte = Wire.read();
    cmdHandler.activeCmd = (CommandType)cmdByte;
    
    if (cmdHandler.activeCmd == CMD_GET_DALI_FRESH_STATUS) {
      cmdHandler.isProcessing = true;
      processStartTime = millis(); // Mark start of DALI process
    }
    // Fast commands don't need processing state set
  }
}

// Send response back to RPi
void sendResponseToRpi() {
  switch(cmdHandler.activeCmd) {
    case CMD_GET_DALI_FRESH_STATUS:
      if (cmdHandler.isProcessing) {
        Wire.write(0xFF); // Send "still processing" flag to RPi
      } else {
        Wire.write(cmdHandler.freshDaliResponse); // Send fresh data
      }
      break;
    case CMD_GET_DALI_FAST_STATUS:
      Wire.write(cmdHandler.fastDaliBuffer); // Send cached fast data
      break;
    default:
      Wire.write(0xEE); // Send unknown command error
      break;
  }
}

// Replace these two functions with your actual DALI communication code
byte readDaliQuickState() {
  // Simulate fast DALI status read
  return random(0x00, 0xFF);
}

byte readDaliFinalState() {
  // Simulate full DALI operation response
  return random(0x00, 0xFF);
}

Raspberry Pi Python Code

This code interacts with Arduino's state machine, handling both command types appropriately:

import smbus2
import time

I2C_SLAVE_ADDR = 0x08
CMD_FRESH_STATUS = 0x01
CMD_FAST_STATUS = 0x02

# Initialize I2C bus (use 0 for RPi 1, 1 for RPi 3/4/Zero)
i2c_bus = smbus2.SMBus(1)

def get_fresh_dali_status():
    """Get updated status after Arduino completes DALI operation"""
    i2c_bus.write_byte(I2C_SLAVE_ADDR, CMD_FRESH_STATUS)
    
    # Poll until processing is done (add timeout to avoid infinite loop)
    timeout_start = time.time()
    while time.time() - timeout_start < 0.1: # 100ms max wait
        response = i2c_bus.read_byte(I2C_SLAVE_ADDR)
        if response != 0xFF: # 0xFF = still processing
            return response
        time.sleep(0.005) # Wait 5ms between polls
    return None # Return None if timeout

def get_fast_dali_status():
    """Get latest cached status for high-frequency queries"""
    i2c_bus.write_byte(I2C_SLAVE_ADDR, CMD_FAST_STATUS)
    return i2c_bus.read_byte(I2C_SLAVE_ADDR)

# Test the fresh status command
print("Fresh DALI status:", get_fresh_dali_status())

# Test high-frequency command (80ms cycle, under 100ms)
print("\nHigh-frequency status updates:")
for i in range(5):
    print(f"Update {i+1}: {get_fast_dali_status()}")
    time.sleep(0.08)

Key Notes for Reliability

  • Add Timeouts: The RPi's polling loop includes a 100ms timeout to prevent hanging if Arduino gets stuck.
  • Avoid Command Collisions: If RPi sends a new command while Arduino is processing another, add logic in receiveRpiCommand to return a "busy" response (e.g., 0xDD) instead of overwriting the active command.
  • Optimize DALI Calls: If your DALI library supports asynchronous operations, you can replace the millis-based wait with interrupts to free up Arduino's loop even more.

内容的提问来源于stack exchange,提问作者Benas Laurinaitis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:34:24