1

I've been researching for 2 days now trying to figure out how to receive a string via I2C on an Arduino. There are plenty of questions online but no real solutions or full tutorials... and sadly I threw my C++ book out years ago.

The code below compiles without error and receives the string correctly. (There are several commented out blocks that I've used to verify the data is being received.) The issue is printing the full received command string. I am unable to make it print in receiveEvent() or in the loop(). Ultimately I need it as a string such as "WIN-23-200" where I will explode the string by "-" to do something but I still can't get a hold of it.

If anyone can give me a nod in the right direction I'd appreciate it.

Thanks!

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

volatile boolean receiveFlag = false;
char temp[32];
String command;

void setup() {
  // initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
  Serial.println("Ready!");

}

void loop() {
  delay(1000);
  if (receiveFlag == true) {
    Serial.println("inFlag");
    Serial.println(command);
    receiveFlag = false;
  }
}

void receiveEvent(int howMany) {
  //works as expected
  /*while (0 < Wire.available()) { // loop through all
    char c = Wire.read();
    Serial.print(c);
    }*/

  int count = 0;
  while (0 < Wire.available()) { // loop through all
    temp[count] = Wire.read();
    count++;
  }
  //temp[32-1]='\0'; //make sure its 0 terminated, didn't matter
  command = temp; //put char array into a string variable named command

  Serial.print(temp); // prints nothing....

  //Verify Array is Loaded..works great.
  //for (int i = 0; i < count; i++)
  //{
  //  Serial.println(temp[i]);
  //}
  receiveFlag = true;
}
  • 2
    We need to see both, the sketch of the master and the sketch of the slave. Please don't use the String object and certainly not in the function receiveEvent. Don't use Serial.print from within the receiveEvent as well. The receiveEvent is an interrupt function and the Serial.print uses interrupts. Use the howMany parameter or the Wire.available to fill the temp[] array. Be sure to zero-terminate the text in the array. Set the flag. In the loop read the flag and print the temp[] array. Remove the delay from the loop, you can check the flag as often as possible. Has the text a fixed length? – Jot Dec 20 '17 at 6:55
  • Thank you very much for the talkthrough.. super helpful to know what matters especially in a new(old) language. Got it working thanks to this. – Andy Theimer Dec 20 '17 at 12:16
1

onReceive is intended for use by slaves. master must request data from slave and wait for answer. look at MasterReader example from the Wire library

0

Here's the working code. The hangup was my Raspberry Pi Master was adding a "cmd byte" to the string before transit. So when I would send "test" the Arduino received 5 bytes; "0test". Based on feedback here I added the null after ea char and then shifted the array to eliminate the first byte and the Serial.print started working.

Here's the working RPi Master Python code:

# -*- coding: utf-8 -*-
import smbus
import time
# for RPI version 1, use bus = smbus.SMBus(0)
bus = smbus.SMBus(1)

# This is the address we setup in the Arduino Program
address = 0x04

#http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2
def writeData(value):
    byteValue = StringToBytes(value)    
    bus.write_i2c_block_data(address,0x00,byteValue) #first byte is 0=command byte.. just is.
    return -1


def StringToBytes(val):
        retVal = []
        for c in val:
                retVal.append(ord(c))
        return retVal

while True:
    print("sending")
    writeData("test")   
    time.sleep(5)

    print('OPEN');
    writeData("OPEN-00-00")
    time.sleep(7)

    print('WIN');
    writeData("WIN-12-200")
    time.sleep(7)

And the Arduino Slave Code

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

volatile boolean receiveFlag = false;
char temp[32];
String command;

void setup() {
  // initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
  Serial.println("Ready!");

}

void loop() {

  if (receiveFlag == true) {
    Serial.println(temp);
    receiveFlag = false;
  }
}

void receiveEvent(int howMany) {

  for (int i = 0; i < howMany; i++) {
    temp[i] = Wire.read();
    temp[i + 1] = '\0'; //add null after ea. char
  }

  //RPi first byte is cmd byte so shift everything to the left 1 pos so temp contains our string
  for (int i = 0; i < howMany; ++i)
    temp[i] = temp[i + 1];

  receiveFlag = true;
}

Hopefully this helps someone else, I searched for along time to put this together!

  • A couple resources that were helpful to me as well: bus i2c command list: link Raspberry Pi bus cmd paramter explained well: link – Andy Theimer Dec 20 '17 at 12:22
  • Well done. You don't have to shift it in receiveEvent. You can do "int cmd=Wire.read();" before the first for-loop. You can use the 'cmd' or not, and use it just to read the first byte. I would just read the data into the array and solve any problem or any cmd byte in the loop. When you are printing the received data, an interrupt with new data could occur during that, and you would print the first half of old data and the second half of new data. It can be avoided by copying the temp[] to a local array while the interrupts are turned off. Can you make temp[] also 'volatile' ? just to be sure. – Jot Dec 20 '17 at 13:43
  • Just one remark: your code is safe only because the Wire library has a 32 byte buffer, otherwise it can pollute the memory. My suggestion is to change the code in one of these ways: 1) char temp[32]; becomes char temp[BUFFER_LENGTH]; (BUFFER_LENGTH is defined in Wire.h) or 2) check that howMany is less than the length of the temp buffer. 2 is more robust, but 1 does less operations – frarugi87 Dec 20 '17 at 14:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.