Casting Variables for I2C

This still doesn't look like the two grounds are connected. Even when both share the same USB host/supply I'd not consider this common GND.

Usually that's a sign for some deadlock situation where the device OS and the application block eachother for some reason (I2C is one of the usual suspects).

In most of your pics you have no LiPo connected. I'd suggest to always have one connected when the Electron should go online as the cellular module may require demand current in bursts that might be too much for your USB supply.

That doesn't make any differenc - just as the number eight wouldn't be any different whether you had 8 apples or 8 pears or 8 matchsticks - 8 == 0008 == 0x0008 == 0b1000 == (1+1+1+1+1+1+1+1)

@mstanley is it possible I have a combination of issues?

This GND for the breadboard should be going to the GND of the Arduino or to the Electron?

Does my Wire.requestFrom() call look correct?

Not "or". It should go to the Arduino and the Electron to be common GND for both.

2 Likes

Following up from the Safe Mode thread, I 've read that the i2C doesn't transmit the data until the Wire.endTransmission is called. Is this true? I cannot get the breathing cyan with it called.

I'm implementing the GND with a jumper wire now...

1 Like

Implemented. Current setup is below.


I've been able to get my Mode issue resolved. Took out Wire.endTransmission(), and the Arduino is still getting hung up on the Master side. I think I will still need to use wire.endTransmission() but will change my mode.
Slave Code update:

void setup() {
    Serial.begin(9600);
    Wire.begin(8); // i2c bus #8
    Wire.onRequest(requestEvent);  // register event
    Serial.println("setup complete.");
}

void loop() {
    delay(500);
    requestEvent();
}

void requestEvent() {
   
  // generate some simulated data
  //float val_1 = 11.08765; //dummy values - do not change   (random(200000) - 1000) / 100000.0;
  //float val_2 =-66.01234; //dummy values - do not change   (random(200000) - 1000) / 100000.0;
  float val_1 = +42.409671;
  float val_2 = -71.097330;
  char* val_3 = NULL;

  
  // transmit the simulated data
  //Wire.write((char*)&val_1); // respond with message of 6 bytes, SENDS CRAP DATA
  //Wire.write((uint8_t*)&val_1, 9); //TESTING // WORKS TO END OF -11.087654/113 NEED 1 MORE DIGIT
  Serial.println("val_1 is: ");Serial.println(val_1,5);
  if (val_1>1) {
    //Wire.begin(8);
    Serial.println("val_1 is ok.");
  }

  String sendstring1 = String(val_1);
  Serial.println("VAR is: "+sendstring1);
  Serial.println(sizeof(sendstring1)); // sizeof(sendstring1) is 16. verified.
  //Wire.write((uint8_t*)&val_1, 5);  // currently trying to send as data. 4/28/19
//  Wire.beginTransmission(0x8);
  Wire.write(sendstring1);  //testing sending as a string 4/28/19
  //Wire.write((uint8_t*)&val_2, sizeof(val_2));// testing 4/27/19 commented to isolate
  //Wire.write((char*)&val_1, sizeof(float)); //THIS ERRORED FROM JOSH
//  Wire.endTransmission();

//*/
}

@ScruffR what would you suggest?

I fear I have to rebuild your setup to see what’s happening first hand :blush:

BTW, why are you calling requestEvent() form loop().
This function is supposed to be triggered by the master. The slave has no business just chatting away without being asked.


I’ve tested communication between two Photons with this code and it works both directions

//#define MASTER
#define SLAVE

const byte addr = 4;
SerialLogHandler Logger(LOG_LEVEL_ALL);
char msg[32] = "dummy";

#ifdef MASTER
void setup() {
  Wire.begin();                     // join i2c bus as master
  Particle.function("send", fnSend);
  Particle.function("recv", fnRecv);
}

int fnSend(const char *arg) {
  strcpy(msg, arg);
  
  Wire.beginTransmission(addr);         // transmit to slave
  Wire.print(msg);                      // buffer data
  Wire.endTransmission();               // stop transmitting (=initiate transfer)

  return strlen(msg);
}

int fnRecv(const char *arg) {
  Wire.requestFrom(addr, strlen(msg));  // request as many bytes as we had sent before
  Wire.readBytes(msg, strlen(msg));
  Serial.print(msg);                    // print the character

  return strlen(msg);
}
#endif

// ----------------------------------------------------------------------------------------------


#ifdef SLAVE
void setup() {
  Wire.begin(addr);                     // join i2c bus with address #4
  Wire.onReceive(receiveEvent);         // register event
  Wire.onRequest(requestEvent);         // register event
  Serial.begin(115200);                 // start serial for output
}

void receiveEvent(int howMany) {
  int i = 0;
  
  Serial.printf("%d: ", howMany);       // we are expecting x bytes
  Wire.readBytes(msg, howMany);         // read them
  msg[howMany] = '\0';                  // terminate the string
  Serial.println(msg);                  // print the string
}

void requestEvent() {
  Wire.print(msg);                      // respond with message we got sent last
}
#endif

I have yet to dig up an Arduino UNO and try with an Electron.


Update:
I’ve now tested that code with Electron and Arduino UNO and it works too
This is the Arduino Master code

#include <Wire.h>

const int addr = 0x04;
char      msg[32] = "test\r\n";

void setup() {
  Wire.begin();                         // join i2c bus (address optional for master)
  Serial.begin(115200);                 // start serial for output
  
  // by this time the Electron should already be breathing cyan to receive the data
  Wire.beginTransmission(addr);         // start transmission to intended slave
  Wire.print(msg);                      // send to slave what we want to be sent back later
  Wire.endTransmission();               // finish transmission (=initiate transfer)
}

void loop() {
  int count = strlen(msg);
  memset(msg, 0x00, sizeof(msg));       // clear buffer to be sure we actually receive data
  Wire.requestFrom(addr, count);        // request back as many bytes we prevsiously sent to slave
  Wire.readBytes(msg, count);           // read all the requested bytes into buffer again
  Serial.println(msg);
  delay(500);
}

As it turns out, pull-up resistors are not needed. The Arduino’s own on-board pull-ups suffice.

Wiring:

UNO    -  Electron
 5V    -   Vin
GND    -   GND
A4/SDA - D0/SDA
A5/SCL - D1/SCL

@ScruffR,
So we are still not getting any output from the master arduino. My first troubleshooting step was to unplug and replug in the 5v-Vin wire, with no luck. I am also not getting any output from my Electron -

I call this since I am requesting the message from the Arduino and need to send from Electron to arduino. Ideally Id want to see the output from the electron but don't see it since we dont call it, or have a loop function. ( I am also using your second program directly above) ..... Why is a loop function not present on the electron?
Also, for these statements,
#define
#ifdef MASTER
#endif
I always thought of those as preprocessor commands, why are there two sets relating to #if/endif?

For kicks, I also tried your first set of code for the master, it wouldn't even compile, and threw an error on 'serialloghandler', was this defined in a library that wasn't included?

Thanks,

That's not how it's done.
As I said, the slave has no business requesting things from his master or bothering him without explicitly being told so.

The way how it's done is that the master requests the data and the slave responds and that's what the Wire.onRequest() call is for: It hooks up a handler function that will automatically be called when the request has been noticed.

That's also the answer to this question

No loop() is needed as the SLAVE doesn't do anything else but wait for the request.
And for the MASTER I have setup particle functions that will either send or request data and these are handled by the system and not by loop().
I could have added a void loop() { } but for what? So I just left it out.

Since my code above runs just fine between my Uno and my Electron I don't know what's wrong on your side.
Test my code and see (you obviously need to undefine the MASTER setting and define SLAVE in my Electron code - see below).

These are preprocessor commands. My code contains both the MASTER and the SLAVE code and depending on which of the two #define statements you have active, the respective code will be built. If you'd happen to have both active (which wouldn't be as intended) then you'd run into troubles - so keep them mutually exclusive.

That suggests that you are targeting a really old device OS version (pre 0.6.0).

1 Like

In regards to the serialloghandler, I was trying to run it on an arduino hence the error. When he uses the preprocessor commands, are those lines of code updoaded on both devices? I'm somewhat confused on that. is undefining the same thing as commenting out code?

This should be included in the slave and master correct?

Would I only adjust the line of code from
#define SLAVE
to
#define MASTER?

UPDATE I was able to get output: first upload, then subsequent up loads:
Arduino:
image
image

subsequent uploads:
(same thing for the Arduino)
image
Shouldn't I be getting the same output for the subsequent uploads? I also did not power cycle the other board when making changes to the other, does this play a factor? Why can I not get the same output as the first time I uploaded?

Thx
MP

Actually Wire.h is not needed for Particle devices - it's part of the system Particle.h which is auto-included in .ino files.

You have this in the combined code

//#define MASTER
#define SLAVE

This mean that the first line is not active so MASTER is not defined but the second line is active, so the code will build for the SLAVE

These AT command logs look like the output of the serial log handler that also prints out system. If you don't want them just comment the SerialLogHandler line.

image

In my first iteration, the count variable is 6 and then goes to zero. Why is this on the Arduino Side?

On the Particle side, I now can’t get any output to the Putty terminal. I have print statements in receiveEvent(int howMany) which are not getting invoked. Do these last two questions have to do with each other?

I also cant get the AT Serialprintlogger output to replicate that output, nor can I get it to replicate the " 6: test " in the post on May 31st. Is there a reason for this?

I can’t say anymore than that communication works for me as expected.
Sure, there can be plenty of problems, but when done correctly it just works without fail.

I have tested with an ARGON and a Arduino Leonardo

Argon APP :

#define MASTER
// #define SLAVE

const byte addr = 4;
SerialLogHandler Logger(LOG_LEVEL_WARN);
char msg[32] = "dummy";
const int testLoopDelay = 5000;
int loopCount;

#ifdef MASTER
void setup() {
  Wire.begin();                     // join i2c bus as master
  Particle.function("send", fnSend);
  Particle.function("recv", fnRecv);
}

int fnSend(const char *arg) {
  strcpy(msg, arg);
  
  Wire.beginTransmission(addr);         // transmit to slave
  Wire.print(msg);                      // buffer data
  Wire.endTransmission();               // stop transmitting (=initiate transfer)

  return strlen(msg);
}

int fnRecv(const char *arg) {
  Wire.requestFrom(addr, strlen(msg));  // request as many bytes as we had sent before
  Wire.readBytes(msg, strlen(msg));
  Serial.println(msg);                    // print the character

  return strlen(msg);
}
#endif

void loop() {
    unsigned long prevMillis;
    char buf[32];
    
    delay(testLoopDelay);
    
    sprintf(buf, "Loop count = %d", loopCount++);
    fnSend(buf);
    fnRecv(buf);

    
    
}

// ----------------------------------------------------------------------------------------------


#ifdef SLAVE
void setup() {
  Wire.begin(addr);                     // join i2c bus with address #4
  Wire.onReceive(receiveEvent);         // register event
  Wire.onRequest(requestEvent);         // register event
  Serial.begin(115200);                 // start serial for output
}

void receiveEvent(int howMany) {
  int i = 0;
  
  Serial.printf("%d: ", howMany);       // we are expecting x bytes
  Wire.readBytes(msg, howMany);         // read them
  msg[howMany] = '\0';                  // terminate the string
  Serial.println(msg);                  // print the string
}

void requestEvent() {
  Wire.print(msg);                      // respond with message we got sent last
}
#endif

Leonardo Sketch:

#include <Wire.h>

//#define MASTER
#define SLAVE

const byte addr = 4;
//SerialLogHandler Logger(LOG_LEVEL_ALL);
char msg[32] = "dummy";

#ifdef MASTER
void setup() {
  Wire.begin();                     // join i2c bus as master
  Particle.function("send", fnSend);
  Particle.function("recv", fnRecv);
}

int fnSend(const char *arg) {
  strcpy(msg, arg);
  
  Wire.beginTransmission(addr);         // transmit to slave
  Wire.print(msg);                      // buffer data
  Wire.endTransmission();               // stop transmitting (=initiate transfer)

  return strlen(msg);
}

int fnRecv(const char *arg) {
  Wire.requestFrom(addr, strlen(msg));  // request as many bytes as we had sent before
  Wire.readBytes(msg, strlen(msg));
  Serial.print(msg);                    // print the character

  return strlen(msg);
}
#endif

// ----------------------------------------------------------------------------------------------


#ifdef SLAVE
void setup() {
  Serial.begin(115200);
  while(!Serial);
  Serial.print("\nSetup....");
  Wire.begin(addr);                     // join i2c bus with address #4
  Wire.onReceive(receiveEvent);         // register event
  Wire.onRequest(requestEvent);         // register event
  Serial.begin(115200);                 // start serial for output
}

void receiveEvent(int howMany) {
  int i = 0;
  Serial.print("\nReceive data ");
  Serial.print(howMany);                // we are expecting x bytes
  Serial.print(" bytes [");
  Wire.readBytes(msg, howMany);         // read them
  msg[howMany] = '\0';                  // terminate the string
  Serial.print(msg);                  // print the string
  Serial.print("]");
}

void requestEvent() {
  Serial.print("\nSend reply to request....");
  Wire.print(msg);                      // respond with message we got sent last
}
#endif

void loop() {
  
}

Screen shot of both serial monitors (Argon on top, Leonardo below)

Connections

ARGON SDA/SCL through 3v3-5v level shifter to Leonardo and GND to GND

All working as expected. so the issue is not in @ScruffR sample code - perhaps post your code and we can review?

1 Like

Hi Guys,
I was able to get my project to work finally, albeit the inverse setup I originally intended, with the Arduino as the master. This current setup is with the Electron as the Master. Here is my setup with code and output to follow.


Arduino slave:

#include <Wire.h>

void setup() {
Wire.begin();
  Serial.begin(9600);
  Serial.print("setup has completed.");//mp
}

void loop() {
  Wire.beginTransmission(1);
  Wire.write(234);  //modify this! //numerical 256 is the limit on this -MP
  Wire.endTransmission();
  Serial.println("loop finished.");
  delay(1000); //mp
}

ELECTRON MASTER:

#include <Wire.h>
int wireVal = 0;
int led = D7;
String incomingString= "";

void setup() {
    pinMode(led, OUTPUT);
    Serial.begin(9600);
    Wire.begin(1);
    Wire.onReceive(receiveData);
}

void loop() {
    digitalWrite(led, wireVal);
}

void receiveData (int byteCount) {
    while (0 < Wire.available()) {
    wireVal = Wire.read();
    Serial.println("wireVal is: ");
    Serial.println(wireVal);
    }
}

image

If I wanted to transmit a string, I would define a string variable on both boards correct? So what would my syntax look like for this type of data transmission over i2c?

Also, if I actually wanted to flip roles arduino would be the master instead. Would I need resistors?

I don't know how to put it, but you are not assigning the roles correctly.
Your Electron is still the slave (due to Wrie.begin(1)) and Arduino the master (due to Wire.begin() and its chattyness - talking without being asked).

However,

Nope, since the Arduino already brings its resistors irrespective of its role as slave or master you won't need any extra.

As for strings, I'd stay away of String objects but use a character array of proper size for the longest expected string. But if you need to use String I'd use String.reserve(maxLen) to make sure the string won't need to grow and get relocated.

How do you know it's chatty without being asked? It is giving me the exact output I wrote in the code...

I also understand that the Arduino is still requesting the information, making this the slave. I should also be able to set int wireVal to an integer and transfer it to the arduino, I would just need to swap programs and boards, is this correct?

It also appears that there is a max of 2 bytes of data when using i2C, as wire.write(double) does not compile whereas wire.write(int) does? Using serial.println(sizeof(variable int)) gives 2 and 4 respectively.

How would this work if sizeof(string) is 6 bytes?? If I substitute int's with doubles or strings in Wire.write(), I am getting one of two errors, either no matching function, or call of overloaded write() ambiguous. Is there is a limit on bytes? Perhaps I may need to do some conversions of data types?

Thx.

Not by the I2C standards. The device that is initiating the communication is the master
Sure you can redefine any word to your private interpretation that that will produce unnecessary confusion especially when you get an answer that adheres to the standard terminology but you'd misinterpret according to your private dictonary.

By definition an I2C master has no address but a slave has. Hence the device featuring the Wire.begin(X) is the slave and the device with Wire.begin() (no address provided) is the master.

BTW, in common terms, who'd be considered the master in: "Hey X, fetch me a glass of wine!"
The one requesting the wine or X who will run and fetch it?

I think I have answered that one already very early on in this thread.

On top of @ScruffR suggestion on way that I like to approach this is thinking in any kind of data (int, float, string, struct, as an array of bytes.

I2C is not capable of sending double or a float, but it's capable of sending one byte at a time. So knowing your data type size and having a pointer to it, and thinking that it's a array of bytes, you could use:

Wire.write(array[1]), to send one byte at time
or
Wire.write(array, size), or more explicit, Wire.write(&array[0], size) to send the entire array at once.

Using ScruffR suggestion your string is the array, and the size is the length of that string.

@Gildons' suggestion is exactly what I meant there

You can cast any variable into an "array" of bytes and acquire its size in bytes via sizeof()