0

so I'm facing a pretty large problem. I'm experimenting with I2C and using one Arduino that acts as a countdown timer to send the current reading on the timer to another Arduino. I'm pretty new to electronics in general so bear with me.

The way the countdown timer works is it stores the time as a String, and uses substring and toInt() to adjust the timer.

I know that with I2C you can only send some data, Strings not included, so I tried converting the String to an array of characters using toCharArray(). My conversion seems to be working well, as I have tested it with the Serial.println() and it displays the time correctly.

Therefore, I am convinced the problem lies with the Slave code. I'm not sure how to set up the Slave Arduino to receive the actual time. Below, I've pasted links to the code of both the Master and the Slave respectively.

Master: https://pastebin.com/AFjTDZB7

Slave: https://pastebin.com/viNGWn9c

How should I change my slave code so everything works?

Also, when I changed the datatype of "time" in the Slave code from char* to int, all I got in the Serial Monitor was 48. Weird.

Would really appreciate any help and guidance cause I'm pretty stuck. All the tutorials I find online are pretty complicated.

Thanks!

Master Code:

#include < Wire.h >
String time = "10:11";
boolean doubleDigits = false;

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  Serial.println(time);
  sendTime(time);
  int First2 = time.substring(0, 2).toInt();
  if (First2 > 9) {
    doubleDigits = true;
  }

  int Last2 = time.substring(3).toInt();
  if ((time.substring(3) == "00") && (time != "00:00")) {
    First2 = First2 - 1;
    Last2 = 59;
    if (doubleDigits == false) {
      time = "0" + String(First2) + ":" + String(Last2);
    } else {
      time = String(First2) + ":" + String(Last2);
    }
  } else if (time != "00:00") {
    Last2 = Last2 - 1;
    if (Last2 < 10) {
      if (doubleDigits == false) {
        time = "0" + String(First2) + ":0" + String(Last2);
      } else {
        time = String(First2) + ":0" + String(Last2);
      }
    } else {
      if (doubleDigits == false) {
        time = "0" + String(First2) + ":" + String(Last2);
      } else {
        time = String(First2) + ":" + String(Last2);
      }
    }
  }
  if (time.substring(1, 2) == ":") {
    time = "0" + time;
  }
  delay(1000);
}

void sendTime(String time) {
  Wire.beginTransmission(9); //transmit to device 9
  char buffer[32];
  time.toCharArray(buffer, 32);
  Wire.write(buffer);
  Serial.println(buffer);
  Wire.endTransmission(); //stops transmitting
}

//master

Slave Code:

#include <Wire.h>
char* time;

void setup() {
  Wire.begin(9); //start as slave on address 9
  Wire.onReceive(receiveEvent); //attach a function to trigger when something is received
  Serial.begin(9600);
}

void receiveEvent(int bytes) {
  time = Wire.read(); //read one character from the I2C

}

void loop() {
  Serial.println(time);
  delay(1000);
}

//slave

Edit: added code

3
  • Can't see the code, can't answer the question. I would expect 48 if you changed a char* of the time to an int without understanding what you were doing. 48 is ASCII 0. Apr 18, 2017 at 10:49
  • @CodeGorilla, alright, added the code to the main post. Can you take a look please? Would really appreciate it.
    – A. Lordos
    Apr 18, 2017 at 12:36
  • You still need to fix sendTime in the master code, but... Apr 19, 2017 at 11:30

2 Answers 2

0

In the slave code. receiveEvent takes a number of bytes as a parameter, presumably you are supposed to read this many bytes, can you call read with that number? Even if you did you are reading to a char pointer, which will cause issues unless read is allocating the memory, which you should then be releasing. Basically you have a couple of issues here and you need to look at them.

In the Master code. sendTime doesn't look right. I would expect you to have to send a certain number of bytes using the Write function. You are just sending 32 chars, the fact you are sending signed char's rather than unsigned char's shouldn't be an issue (but its technically incorrect).

I don't really understand what you are trying to achieve in the rest of loop, annotation would help :)


OK, If you take this as a new version for the slave you should be able to see it working. It might not be 100%, but you should get something. (I haven't even compiled this code, so you might have to sort out a few bugs)

#include <Wire.h>

#define MaxTimeBufferSize (33)
#define MaxReadSize  (MaxTimeBufferSize - 1)
char timeBuffer[MaxTimeBufferSize];

void setup() 
{
  Serial.begin(9600);  // Init first so you can see any issues during setup.
  Wire.begin(9); //start as slave on address 9
  Wire.onReceive(receiveEvent); //attach a function to trigger when something is received
}

// Assumes the bytes are actually printable ASCII characters.
void receiveEvent(int bytes) 
{
  const int bytesToRead = (bytes < MaxReadSize) ? bytes : MaxReadSize;
  for (int index = 0; index < bytesToRead; ++index)
  {
     timeBuffer[index] = Wire.read(); //read one character from the I2C
  }
  timeBuffer[bytesToRead] = 0; // Null terminate the bytes (so they will print as a string)
}

void loop() 
{
  if (timeBuffer[0])  // Only print the buffer if there is new data
  {
    Serial.println(timeBuffer); // The buffer is NULL terminated.
    timeBuffer[0] = 0;  // Stop the buffer  printing again.
  }
  delay(1000);
}
2
  • Hey, thanks for your reply, @CodeGorilla. I realize that Slave contains a problem, I just don't know how to fix it. That's why I'm asking for help. Also, for the Master code, all it does is act as a countdown clock, by maintaining a String called "time" and uses some toInt(), concatenation and toString() functions to change the value of time every second and send that value to the Slave.
    – A. Lordos
    Apr 18, 2017 at 20:35
  • works like a charm! Thank you, really appreciate it!
    – A. Lordos
    Apr 19, 2017 at 11:29
0

@A.Lordos, the code by @CodeGorilla shows how to read more than one byte as a Slave. It stores the received data in a buffer, and that buffer is printed to the serial monitor to show it.

May I make a suggestion: start all over and do it right.
Forget the String objects and use a integer (int, or unsigned or unsigned long) as a countdown counter for the seconds. Count the seconds, nothing else.
When you need to display the counter, then you can make it in the format of "10:11". But only to display it, the base of the sketch is still that single variable for the seconds. When you need to transmit the counter to the Slave, send the integer variable as binary data (the integer itself), not a text string.

In the Arduino examples, a text is transferred over the I2C bus. That is however almost never done. In most cases binary data of variables is transferred over I2C.

This is a good tutorial: Nick Gammon's Tutorial about I2C

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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