I2C Issues between Core and Uno [Solved!]

I’m having some issues communicating between a Spark Core and an Arduino Uno via I2C.

I have connections between D0 -> A4 and D1 -> A5, both with 4k7 pull-up resistors, GND -> GND and +5 -> Vin.

For now I’m just trying to press a switch on one board and have the on-board LED light on the other. The intension was to use this sketch to confirm I have the I2C communication working before using it in “real” applications. I have the code working between 2 Unos but not to the Core.

Here’s the code I’m running on the Core:

#define LED D7
#define BUTTON D4

#define THIS_ADDRESS 0x9
#define OTHER_ADDRESS 0x8

boolean last_state = HIGH;

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  
  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
}

void loop() {
  if (digitalRead(BUTTON) != last_state){
    last_state = digitalRead(BUTTON);
    Wire.beginTransmission(OTHER_ADDRESS);
    Wire.write(last_state);
    Wire.endTransmission();
  }
}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    boolean b = Wire.read();
    Serial.print(b, DEC);
    digitalWrite(LED, !b);
  }
  Serial.println(); 
}

and the code for the Uno:

#include <Wire.h>

#define LED 13
#define BUTTON 10

#define THIS_ADDRESS 0x8
#define OTHER_ADDRESS 0x9

boolean last_state = HIGH;

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  
  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
}

void loop() {
  if (digitalRead(BUTTON) != last_state){
    last_state = digitalRead(BUTTON);
    Wire.beginTransmission(OTHER_ADDRESS);
    Wire.write(last_state);
    Wire.endTransmission();
  }
}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    boolean b = Wire.read();
    Serial.print(b, DEC);
    digitalWrite(LED, !b);
  }
  Serial.println(); 
}

Any help would be greatly appreciated, thanks in advance.
Colin

Which is supposed to be the I2C Master? The Uno or Core? If it’s the Core is the Master you just need Wire.begin(), i.e., you don’t need to assign it an address. It should be polling the Uno for an event.

The code was taken from this website: http://forum.arduino.cc/index.php/topic,13579.0.html

Maybe I should make it more simple like you suggest and make them specifically master / slave.

Looks like the Arduino’s twi implementation allows for multi-master just fine… just need to Wire.begin(address); for each like you have to set each to it’s own slave address, then use Wire.beginTransmission(); / Wire.send(); / Wire.endTransmission(); whenever you want to act as Master and talk to the other device.

That said, I don’t know if the Spark’s implementation is created equally and if it can support these modes. @timb you’d know better than I as you’re rewriting the I2C library :wink:

Also a little known fact... boolean in Arduino land is typedef'd as uint8_t (i.e. one byte) so this above code works, although it's technically wrong.

In Sparkland, boolean is one bit as far as I know. Try also changing this line to uint8_t b = Wire.read(); should work on the Arduino as well.

Thank you for your help!! Unfortunately it’s still not working. I’ve simplified the code so they should act as Master and Slave now.

Here’s the code on the Uno (Master):

#include <Wire.h>

#define BUTTON 10

#define OTHER_ADDRESS 0x9

boolean last_state = HIGH;

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  
  Wire.begin();
}

void loop() {
  if (digitalRead(BUTTON) != last_state){
    last_state = digitalRead(BUTTON);
    Wire.beginTransmission(OTHER_ADDRESS);
    Wire.write(last_state);
    Wire.endTransmission();
  }
}

and the code for the Core (Slave):

#define LED D7

#define THIS_ADDRESS 0x9

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  
  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
}

void loop() {
}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    uint8_t b = Wire.read();
    digitalWrite(LED, !b);
  }
}

Again, thanks for all your help!
Colin

I overlooked some things before... you are using internal pull-ups on the Arduino... but the Spark core does it differently. Instead of the above code, use this:

pinMode(BUTTON,INPUT_PULLUP);

That should fix your problem :wink:

Also, a better way to debounce the input. Don't read the input twice to hold the state, it's likely going to read in the opposite state when you go to save last_state because all mechanical switches are bouncy. Try this code:

if(digitalRead(BUTTON) == LOW) {   // someone pressed the button!
  delay(100);                      // let the bouncy switch settle
  if(digitalRead(BUTTON) == LOW) { // yeah it's still low!!
    // now run your code
  }
}

BDub, in arduino land writing a high to a desgined INPUT pin activates the pullup. So both formats are correct. :smile:

Thank you, I’ll definitely use that in the future.

Like in my previous post I’ve removed the button / master I2C part on the Spark Core side and it still doesn’t work.

Also, if you have any other I2C example code for linking an Arduino Uno (master) to a Spark Core (slave) then that would be equally useful. This was just intended to be a simple I2C test which I could build on and it’s not going well so far!

Bah, getting confused thinking about this… I see that code is for his Arduino UNO… so that should work as a dedicated Master. It was previously used on the Spark Core master/slave example in the first post though… so relevant, but yeah it’s not necessarily the fix.

I can quickly setup a test jig with an arduino mini pro and a Spark an put a sniffer on it so see what’s happening. Stay tuned.

2 Likes

Try using the Core as a master and the Uno as a slave. The Core can poll the Uno on each loop.

Everything is working! However, there are a couple of things I had to fix. First, as per timb, I made the Spark the master with this code:

#include "application.h"

#define BUTTON D4

#define OTHER_ADDRESS 0x09

int last_state = HIGH;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);

  Wire.begin();
}

void loop()
{
    if (digitalRead(BUTTON) != last_state){
        last_state = digitalRead(BUTTON);
        Wire.beginTransmission(OTHER_ADDRESS);
        Wire.write(last_state);
        Wire.endTransmission();
        }
}

The button “bebounce” that BDub suggested was great but that is not what was needed. The original code was looking for a change in the button and would xmit that change to the slave. Also, note I removed the boolean on the last_state and made it an INT since that’s what digitalRead returns.

On the arduino side, I used this code:

#include <Wire.h>

#define LED 3

#define THIS_ADDRESS 0x09

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
}

void loop() {
}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    uint8_t b = Wire.read();
    digitalWrite(LED, !b);
  }
}

Because I used a 3.3V Arduino pro mini, I put an LED to ground via a resistor on digital pin 3. When I grounded D4 on the Spark, the LED went out. When I let D4 “float”, the LED turned on. :smile:

Awesome, glad you got it working!

I noticed some issues with the slave code in the low level STM32 I2C libraries. I’m fixing that in my overhaul, but for now it’s better to just use the Core as a master if possible!

Let us know if you have any other problems! :smiley:

Thank you so much for all your help. I can’t wait to try this later. :smile:

Hmm, ok I see that now. This should do the trick then, being very responsive, preserving the operation and debouncing.

new_state = digitalRead(BUTTON);
if (new_state != old_state) {
  Wire.beginTransmission(OTHER_ADDRESS);
  Wire.write(new_state);
  Wire.endTransmission();
  old_state = new_state;
  delay(50); // keep from exiting too quickly and re-entering due to switch bounce
}