I am currently using the Wire.h library to send information between two Arduinos. I need to use this as my Uno doesnt have enough digi pins for all the stepprs I'm running. The stepper lib I'm using is called Accelstepper and can be found here: AccelStepper: AccelStepper library for Arduino however it not this that is the problem..I dont think!
My master code:
#include <Wire.h>
int flag = 3;
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); //
}
void loop()
{
delay(200);
Wire.requestFrom(1, 1); // request 1 byte from slave device #1. NB why onnly 1 byte, doeasnt the slave send "y" which is 6 bytes?
while(Wire.available()) // slave may send less than requested
{
char command = Wire.read(); // receive a byte as character
if((flag == 1) && (command == 'y'))
{
flag = 2;
menu();
break;
}
else if((flag == 2) && (command == 'y'))
{
flag = 3;
menu();
break;
}
else if((flag == 3) && (command == 'y'))
{
flag = 1;
menu();
break;
}
}
}
void menu()
{
switch (flag)
{
case 1:
Wire.beginTransmission(1); // transmit to device #1
Wire.write("a"); // sends five bytes
Wire.write(100); // sends one byte
Wire.write(150);
Wire.write(75);
Wire.endTransmission(); // stop transmitting
//delay(3000);
break;
case 2:
Wire.beginTransmission(1); // transmit to device #1
Wire.write("b"); // sends five bytes
Wire.write(200); // sends one byte
Wire.write(300);
Wire.write(100);
Wire.endTransmission(); // stop transmitting
//delay(3000);
break;
case 3:
Wire.beginTransmission(1); // transmit to device #1
Wire.write("c"); // sends five bytes
Wire.write(50); // sends one byte
Wire.write(75);
Wire.write(20);
Wire.endTransmission(); // stop transmitting
//delay(3000);
break;
default:
break;
}
}
and my slave code:
#include <Wire.h>
#include <AccelStepper.h>
AccelStepper stepper3(4, 2, 3, 4, 5);
AccelStepper stepper4(4, 6, 7, 8, 9);
AccelStepper stepper5(4, 10, 11, 12, 13);
int flag, x, y, z;
char command; //hold a single character such as "a". Arduino stores it as a number between -128 to127
void setup()
{
// stepper3.setMaxSpeed(50); //Set stepper speed in steps per second
// stepper3.setAcceleration(50);//How many steps the stepper takes to reach MaxSpeed
stepper4.setMaxSpeed(100); //Set stepper speed in steps per second
stepper4.setAcceleration(100); //How many steps the stepper takes to reach MaxSpeed
stepper5.setMaxSpeed(100); //Set stepper speed in steps per second
stepper5.setAcceleration(100); //How many steps the stepper takes to reach MaxSpeed
Wire.begin(1); // join i2c bus with address #1
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event
Serial.begin(9600); // start serial for output
flag = 1;
}
void loop()
{
delay(200);
if (flag == 0)
{
switch (command) //Command defines the name of the cases used in this control structure
{
case 'a':
Serial.println("case = a.");
Serial.println(x);
Serial.println(y);
Serial.println(z);
stepper3.setCurrentPosition(0);
stepper3.setMaxSpeed(x); //Set stepper speed in steps per second
stepper3.moveTo(y);
stepper3.setAcceleration(z);
flag = 1;
break;
case 'b':
Serial.println("case = b.");
Serial.println(x);
Serial.println(y);
Serial.println(z);
stepper4.setCurrentPosition(0);
stepper4.setMaxSpeed(x); //Set stepper speed in steps per second
stepper4.moveTo(y);
stepper4.setAcceleration(z);
flag = 1;
break;
case 'c':
Serial.println("case = c.");
Serial.println(x);
Serial.println(y);
Serial.println(z);
stepper5.setCurrentPosition(0);
stepper5.setMaxSpeed(x); //Set stepper speed in steps per second
stepper5.moveTo(y);
stepper5.setAcceleration(z);
flag = 1;
break;
default:
Serial.println("no case");
}
}
stepper3.runToPosition();
stepper4.runToPosition();
stepper5.runToPosition();
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) //??whats this int howMAny
{
while(1 < Wire.available()) // loop through all but the last
{
command = Wire.read(); // receive byte as a character
x = Wire.read(); // receive byte as an integer
y = Wire.read(); // receive byte as an integer
z = Wire.read(); // receive byte as an integer
// Serial.println("x is...");
// Serial.println(x);
// Serial.println("y is...");
// Serial.println(y);
// Serial.println("z is...");
// Serial.println(z);
}
}
void requestEvent()
{
if (flag == 1)
{
Wire.write("y"); // respond with message of 1 byte
// as expected by master
flag = 0;
}
}
Now all works well and as I would expect, EXCEPT... the Case 2 in my master has been told to send ("b",200,300,100) to the slave however the slave for some reason I cant locate receives in case b (x=200, y=44???, z=100). All other cases retrieve the correct values so why does this one have a problem?
Ah of course, and it was starring right at me in the comments! :* Thanks.
So how can I send more than one byte with Wire.write. It manges to send the letters 'a', 'b', 'c' ok which must be stored as more then a byte? Would I have to define the variable I want to send before as a float or int say:
ie: int val = 300;
Wire.write(val):
Is it default to a byte? Would be grateful if you could explain how this is working.
@ PaulS: I am relatively new to Arduino so didn't really understand what your code is doing there, had a look on the Arduino site but they don't give much explanation on highByte and lowByte...would you mind elaborating. I copied your code in but retrieved on the serial for case b on the slave: 200, 1, 44? Do I need to do something to the slave code? I imagine something must happen to the 'receiveEvent' function, but still how I see this function to operate is I must have the same Wire.read()'s as the amount of data I'm sending? Implementing your code means I have to have a different amount for Case 2 than for Case 1 and 3 in the master. Do you see what I'm saying?
@Constantin: Thanks very much for that bit of info, had a brief look at it and will give it a go!
One last thing, I will be operating 3 x ATMegas standalone, one master with two slaves. I have wired them on a breadboard as shown here: http://arduino.cc/en/Main/Standalone. This may not be the right place for this question, let me know and I'll move it if so but does anyone know if I can run all three off the same crystal? At the moment they each have their own with 22pF caps. Also is there any risk of things running out of sync?
I have been looking to get things working with your suggested Easytransfer library, do you have experience with it? This may be basic coding error on my part with using flags but I cant seem to find the problem: I'm hoping to run two steppers off the slave, depending on the flag state in the master I want to send info on the parameters of the stepper and which one to activate. Through the serial printing the flagslave state I get a bunch of zeros coming through very fast with stepper3 activated until some point where it changes to 1 and carries on operating stepper 3? Sometimes stepper4 will come on once with no change of flag state then return to stepper3? I have read that the function runToPosition() 'blocks' and is suggested to not be run in event loops? Could there be a problem here?
My master code:
/*THIS IS THE MASTER*/
#include <Wire.h>
#include <EasyTransferI2C.h>
int flagmaster = 1;
//create object
EasyTransferI2C ET;
struct SEND_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
int flagslave;
int a;
int b;
int c;
int d;
int e;
int f;
};
//give a name to the group of data
SEND_DATA_STRUCTURE mydata;
//define slave i2c address
#define I2C_SLAVE_ADDRESS 9
void setup(){
Wire.begin();
//start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
ET.begin(details(mydata), &Wire);
}
void loop()
{
if (flagmaster == 1)
{
mydata.a = 100;
mydata.b = 500;
mydata.c = 75;
mydata.flagslave = 1;
flagmaster = 0;
ET.sendData(I2C_SLAVE_ADDRESS);
}
if (flagmaster == 0)
{
mydata.d = 50;
mydata.e = 400;
mydata.f = 20;
mydata.flagslave = 0;
flagmaster = 1;
ET.sendData(I2C_SLAVE_ADDRESS);
}
delay (5000);
}
and slave code:
/*THIS IS THE SLAVE*/
#include <AccelStepper.h>
#include <Wire.h>
#include <EasyTransferI2C.h>
AccelStepper stepper3(4, 2, 3, 4, 6);
AccelStepper stepper4(4, 7, 8, 9, 10);
//int flagslave = 1;
//create object
EasyTransferI2C ET;
struct RECEIVE_DATA_STRUCTURE{
//put your variable definitions here for the data you want to receive
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
int flagslave;
int a;
int b;
int c;
int d;
int e;
int f;
};
//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;
//define slave i2c address
#define I2C_SLAVE_ADDRESS 9
void setup(){
Wire.begin(I2C_SLAVE_ADDRESS);
//start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
ET.begin(details(mydata), &Wire);
//define handler function on receiving data
Wire.onReceive(receive);
Serial.begin(9600);
}
void loop() {
//check and see if a data packet has come in.
if(ET.receiveData() && (mydata.flagslave == 1)){
//this is how you access the variables. [name of the group].[variable name]
//since we have data, we will blink it out.
stepper3.setCurrentPosition(0);
stepper3.setMaxSpeed(mydata.a); //Set stepper speed in steps per second
stepper3.moveTo(mydata.b);
stepper3.setAcceleration(mydata.c);
}
if (ET.receiveData() && (mydata.flagslave == 0))
{
stepper4.setCurrentPosition(0);
stepper4.setMaxSpeed(mydata.d); //Set stepper speed in steps per second
stepper4.moveTo(mydata.e);
stepper4.setAcceleration(mydata.f);
}
stepper3.runToPosition();
stepper4.runToPosition();
Serial.println(mydata.flagslave);
}
void receive(int numBytes) {}
Did you get the examples Bill included with his Library to work? That would be my starting point. Once you can reliably send data from the master to the slaves, it should become easier to control them. I'm not exactly up to speed re: what your master and slave functions are trying to do, as there doesn't seem to be any reason for the flag master to change, for example. I'd try something simple, like making each slave blink its address every time it has been addressed successfully.
Since you're using I2C, use the serial bus to debug. Print everything.
I'm not sure it's permissible to try and use the flagslave data in the if function the way you do. IMO, that && function is going to get you into trouble. You are better off reading in all the data first and then deciding what to do rather than make decisions based on data that you may not have received yet or that may be changing while it is being received. IIRC, the Arduino reads the data in one byte at a time, not the whole message.
Yea I did manage to get Bills examples to work. I have a deadline for my project so have reverted back to the Wire.h lib, got that working ok so it will do for now as I don't have the time to figure out Bill's lib. Thanks for the advice though, will be using it in the future.
buycris:
It manges to send the letters 'a', 'b', 'c' ok which must be stored as more then a byte?
Those character are all stored in one byte.
These two declarations are functionally the same:
char c = 'a';
char c= 99;
Would I have to define the variable I want to send before as a float or int say:
ie: int val = 300;
Wire.write(val):
Is it default to a byte? Would be grateful if you could explain how this is working.
300 represented in binary is 1 0010 1100b. The problem is that Wire.write only can send on 8 bits at a time; The value 300 requires 9 bits, so the function has to choose either the high order or low order to send when you pass it an int. Because int is commonly used when the high order portion is not needed (all 0s), they chose the low order. That means that 0010 1100b in sent which, not surprisingly, is 44.
In order to send a value that needs more than 8 bits, you need to split it up. The code above will allow you to send the high byte (0000 0001b) followed by the low byte (0010 1100b). So when you are reading the bytes on the slave, you will need to know at what point you are receiving a two byte value. From there it is pretty easy to convert back: highByte * 16 * 16 + lowByte. (1 * 16 * 16 + 44 = 300).