Using Master and 2 Slaves via I2C

Hello. I'm trying to use 3 Arduinos (1 master 2 slaves). First, one slave keeps track of the time and sends it to the master. The master prints it and interprets when the time is right to send a signal to the other slave to activate a servo. The 2nd slave then gets this message and activates the servo, and that's it. I am calling the slaves "drones" and the master "queen" to clarify.

As of now, it immediately begins to move the servo's position up in very small increments until the servo runs out of degrees of movement. The serial monitor just prints "waiting". Any ideas?

Master's Code:

#include <Wire.h>

#define clockDroneAddress 12
#define workingDroneAddress 13

int byteCount;

int seconds;
int minutes;
int hours;
int days;

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

}

void loop() {
  // put your main code here, to run repeatedly:

  sortClockIncoming();

  checkTime();
  
  Serial.print("seconds: ");
  Serial.print(seconds);
  Serial.print("    minutes: ");
  Serial.print(minutes);
  Serial.print("    hours: ");
  Serial.print(hours);
  Serial.print("    days: ");
  Serial.println(days);
}


void checkTime() {
  if (seconds >= 15) {
    Wire.beginTransmission(workingDroneAddress);
    Wire.write(1);
    Wire.endTransmission();
  } else {
    Wire.beginTransmission(workingDroneAddress);
    Wire.write(0);
    Wire.endTransmission();
  }

  //other stuff here too like check time for lighting
}



void sortClockIncoming() {
  while (readI2C(clockDroneAddress) != 255) {
    Serial.println("Waiting");
  }

  for (byteCount = 0; byteCount < 4; byteCount++) {
    switch (byteCount) {
      case 0:
      seconds = readI2C(clockDroneAddress);
      break;

      case 1:
      minutes = readI2C(clockDroneAddress);
      break;

      case 2:
      hours = readI2C(clockDroneAddress);
      break;

      case 3:
      days = readI2C(clockDroneAddress);
      break;
    }
  }
}


byte readI2C(int address) {
  byte byteVal;
  long entry = millis();

  Wire.requestFrom(address, 1);

  while (Wire.available() == 0 && (millis() - entry) < 100) {
    Serial.println("Waiting");
  }

  if ((millis() - entry) < 100) {
    byteVal = Wire.read();
  }

  return byteVal;
}

Clock Slave:

#include <Wire.h>

#define droneAddress 12

int byteCount = 0;

int seconds = 0;
int minutes = 0;
int hours = 0;
int days = 0;

int delaySecond = 1000;

void setup() {
  // put your setup code here, to run once:
  Wire.begin(droneAddress);

  Wire.onRequest(requestEvent);
}

void requestEvent() {
  
  byte byteVal;

  switch(byteCount) {
    case 0:
    byteVal = 255;
    break;

    case 1:
    byteVal = seconds;
    break;

    case 2:
    byteVal = minutes;
    break;

    case 3:
    byteVal = hours;
    break;

    case 4:
    byteVal = days;
    break;
  }

  Wire.write(byteVal);

  byteCount++;

  if (byteCount > 4) {
    byteCount = 0;
  }
}

void count() {
  delay(delaySecond);
  seconds++;

  if (seconds == 60) {
    seconds = 0;
    minutes++;
  }

  if (minutes == 60) {
    minutes = 0;
    hours ++;
  }

  if (hours == 24) {
    hours = 0;
    days++;
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  count();
}

Servo Slave:

#include <Wire.h>
#include <Servo.h>

#define droneAddress 13

Servo servo;

int receivedData;

int servoPn = 2;

void setup() {
  // put your setup code here, to run once:
  Wire.begin(droneAddress);

  Wire.onReceive(receiveEvent);

  pinMode(servoPn, OUTPUT);
  servo.attach(servoPn);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (receivedData == 1) {
    servo.write(180);
  } else if (receivedData == 0) {
    servo.write(0);
  }
  
}

void receiveEvent() {
  receivedData = Wire.read();
}

IMO you should write all bytes at once in the onRequest event.

Welcome to the forum.

The project will be easier with just one Arduino board. But if you like to experiment with the I2C bus, that is okay. Well, not okay, but if you can handle this post, then you might be going in the right direction :face_with_raised_eyebrow:

Which Arduino boards do you use ? Normal Arduino Uno boards ?

Master:

In the loop(), you have no limit for the amount of I2C actions. It is better not to fill the I2C bus to the maximum. Add a delay of 100ms ... 1s at the end of the loop().

Please don't wait in the loop(). Remove the while-statement from the softClockIncoming().

Do not wait after a Wire.requestFrom(). There is no such thing as waiting after a Wire.requestFrom(). That is a myth. Remove that while-statement as well.

There is no synchronization. If a byte is received then it is not known if that is the seconds, hours, days or minutes.
The I2C bus works with packages of data. If you put all 4 bytes in a package, then the resulting code will be easier. Send and receive an array of 4 bytes.

Clock Slave:

Send an array of 4 bytes.

Servo Slave:

A servo motor will introduce a lot of electrical noise. That might disturb the I2C bus.

The variable receivedData is used both in the loop() and in the onReceive handler. Make that variable 'volatile'.

In the loop() the servo is always set, and there nothing to limit the speed of the loop(). It would be better to set the servo when a new value was received and not all the time.
I use a bool variable for that. Set it true in the onReceive handler and check it in the loop() and make it false if used.

volatile byte servoValue;    // value for the servo motor, received over I2C
volatile bool newData;   // 'flag' to tell if a new servoValue was received

Here are twenty 'i's : iiiiiiiiiiiiiiiiiiii
I know they are very expensive, so I gave you twenty to be used in your sketch. Your variable "servoPn" can now be called "servoPin".
If you don't use my 'i's, then I would like to have them back, maybe I can sell them to someone else :face_with_spiral_eyes:

In a project it is often better to keep the "knowledge" and "intelligence" in the Master. You could send a value of 0 ... 180 to the Servo Slave.

1 Like

You have not followed the logical sequence of spreading the program codes for your I2C based Master-2xSlave network.

1. How does Clock Slave track time? Is it counting the content of millisCounter? How often it sends the time to Master?

2. At what interval does Master send command to Servo Slave to move the shaft of the Servo Motor?

3. By how much angle the Servo Slave does move the shaft after receiving command from Master?

1 Like

I'm currently using 2 nanos and 1 mega board, but the mega is only for prototyping and I'm going to switch to 3 nanos instead. I have several questions:

  1. why do I have to remove the while statements in the master's code, and why is it a myth that those are needed?

  2. How do I send data in packages of 4 bytes/how do I send or receive an array?

3.What does making a variable volatile do and why do I need it?

  1. Can you give an example of using the boolean for the servo?

  2. Also, how would I run this on one arduino because I'm going to be using LCD screens and physical user input, so I thought that having delays in the code to count for seconds would interrupt or delay the user's input? Using one arduino while having the ability of user input would be fantastic.

Your questions indicate to me that your knowledge is based on questionable (youtube?) sources and that you should start exploring the IDE examples like BlinkWithoutDelay.

BlinkWithoutDelay: https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay

That is correct

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.