如何使用 Arduino IDE 通过 I2C 总线在两个不同地址上写入消息?

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

我有一个主从设置,由一个nodeMCU(主)和一个arduino nano(从)组成。我想在两个不同的 I2C 地址上发送两种不同类型的数据。数据类型 1 是整数,数据类型 2 是字符。

我写了这两段代码,它们在某种程度上达到了我的预期,但结果并不一致。

主控(节点MCU):

#include <Wire.h>

void setup() {
 Serial.begin(9600); /* begin serial for debug */
 Wire.begin(5, 4); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}

void loop() {

 int r =5;
 Wire.beginTransmission(8); /* begin with device address 8 */
 Wire.write(r);  
 Wire.endTransmission();
  delay(100);
 String bod = "message";
 Wire.beginTransmission(9);
 Wire.write(bod.c_str());  
 Wire.endTransmission();
  delay(100);

}

从机(arduino nano)

#include <Wire.h>


void setup() {
  
 /* register request event */
 Serial.begin(9600);           /* start serial for debug */
}

void loop() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent1); /* register receive event */
 Wire.begin(9);                /* join i2c bus with address 9 */
 Wire.onReceive(receiveEvent2); 

 
}

// function that executes whenever data is received from master

void receiveEvent1(int howMany) {
 while (0 <Wire.available()) {
    int c = Wire.read();   
    /* receive byte as int */
    Serial.print(c);           /* print the int */
  }
 Serial.println();             /* to newline */
}

void receiveEvent2(char howMany) {
 while (0 <Wire.available()) {
   char d = Wire.read();   
    /* receive byte as a character */
    Serial.print(d);           /* print the character */
  }
 Serial.println();             /* to newline */
}

我得到的输出也会在短时间内停止:

5
message

5
5
5
10910111511597103101

5
10910111511597103101

message
5
message
5
10910111511597103101
10910111511597103101
10910111511597103101
5
message
message


message
5
message
message


message
5

预期的输出就在那里,但不是以一致的方式,如:

5
message
5
message
5
message

有没有办法得到这样的输出?有没有办法修复输出短时间后停止的问题?

arduino i2c nodemcu master-slave arduino-c++
2个回答
3
投票

我想在两个不同的 I2C 地址上发送两种不同类型的数据。

Arduino 的 Wire 库设计为每个设备都有一个 I2C 地址。它的实现使用一组全局资源。这意味着,如果您动态更改接收 Arduino 设备的地址,那么您还需要同步主设备以了解要使用的地址。

为从设备分配单个地址并让主设备指示正在发送的数据类型会更简单。这可以通过发送 JSON 或键/值对来完成。例如主站可以在任何整数之前发送前缀

int:
,在任何字符串之前发送
str:
。从站可以使用前缀来解释传输模式。

解释输出

10910111511597103101

这是整数字符串

message
的 ASCII 字符。即 109、101、115、115、97、103、101

这里

receiveEvent1()
正在处理通过 I2C 总线发送的数据并将每个字节作为整数输出。

调用

Wire.begin()
中的
Wire.onReceive()
loop()
将更改全局
TwoWire
singleton 中的设置。底层
twi
也有一组资源。

更改地址和接收处理程序(在

loop()
中重复)的结果是一组竞争条件和两个接收处理程序的未定义使用。

这似乎包括

receiveEvent1()
处理“5”和
receiveEvent2()
按照您的预期处理“消息”。它还包括
receiveEvent1()
处理“消息”,从而产生数字序列。

明显的空行可能是

receiveEvent2()
处理“5”,因此可能输出不可打印的 ASCII ENQ(查询)字符。也可能在某些时期,两个处理程序都没有正确配置,并且某些消息被丢弃或忽略。


2
投票

您不应使用 2 个地址与单个从设备进行通信。相反,您应该使用一个地址,并指定您要发送的数据类型。

如何指定要发送的数据类型?

就像 Ben T 所说,您可以使用 JSON 来实现这一点,但我认为这对于您的应用程序来说有点过分了。

需要更少字节来选择要传输的数据类型的更简单的实现是通过软件定义 2 个不同的地址;一种用于接收整数,一种用于接收字符。

在你的主设备中,你会有这样的东西:

// First sending the integer
Wire.beginTransmission(8); //slave address is 0x08
Wire.write(0); // this is your "propriatary" address for sending integers!
Wire.write(r); // write your integer
Wire.endTransmission();

// Then sending the characters
Wire.beginTransmission(8);
Wire.write(1); // this is the address for sending characters!
Wire.write(bod.c_str());
Wire.endTransmission();

在你的奴隶的

setup()
函数中,你应该有类似的东西:

Wire.begin(8); // slave address
Wire.onReceive(dataReceived);

这就是回调

dataReceived
的样子:

void dataReceived() {
    char address = Wire.read(); // get the "propriatary" address
    switch(address) {
        case 0: // receiving integers
            receiveEvent1();
            break;
        case 1: // receiving characters
            receiveEvent2();
            break;
        default:
            break; // if we received an invalid address, do nothing
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.