Two Teensy LC with I2C

Status
Not open for further replies.

alexandros

Well-known member
I'm trying to establish communication between two Teensy LC via I2C. I've connected 4.7k pull-up resistors on pins 18 and 19, and connected the ground pins. I tried both powering up the two Teensies via USB,or connecting the 5V pins, so that the master (which is also printing to the serial monitor) provides power to the slave.
I'm trying the following code for the master:
Code:
#include <Wire.h>

int x = 0;
void setup() {
  // Start the I2C Bus as Master
  Wire.begin(); 
  Serial.begin(115200);
}

void loop() {
  Wire.beginTransmission(9); // transmit to device #9
  Wire.write(x);              // sends x 
  Wire.endTransmission();    // stop transmitting
  Serial.println(x);
  x++; // Increment x
  if (x > 5) x = 0; // `reset x once it gets 6
  delay(500);
}
and for the slave:
Code:
#include <Wire.h>

int LED = 13;
int x = 0;
void setup() {
  // Define the LED pin as Output
  pinMode (LED, OUTPUT);
  // Start the I2C Bus as Slave on address 9
  Wire.begin(9); 
  // Attach a function to trigger when something is received.
  Wire.onReceive(receiveEvent);
}

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

void loop() {
  //If value received is 0 blink LED for 200 ms
  if (x == '0') {
    digitalWrite(LED, HIGH);
    delay(200);
    digitalWrite(LED, LOW);
    delay(200);
  }
  //If value received is 3 blink LED for 400 ms
  if (x == '3') {
    digitalWrite(LED, HIGH);
    delay(400);
    digitalWrite(LED, LOW);
    delay(400);
  }
}
Which I got from this instructable https://www.instructables.com/id/I2C-between-Arduinos/
But I can't get them to work. Is there something I'm missing here?
 
Pretty sure this code will not work on *any* Arduino board. It's transmitting numbers 0 to 5, but the receiver is testing for characters '0' and '3', which are ASCII numbers 48 and 51.

At the very least, fix the receiver code to check for actual numbers 0 and 3. (no single quotes)

Code:
  //If value received is 0 blink LED for 200 ms
  if (x == 0) {
    digitalWrite(LED, HIGH);
    delay(200);
    digitalWrite(LED, LOW);
    delay(200);
  }
  //If value received is 3 blink LED for 400 ms
  if (x == 3) {
    digitalWrite(LED, HIGH);
    delay(400);
    digitalWrite(LED, LOW);
    delay(400);
  }


Edit: looks like the top comment (of 113 total) on the instructables page also points out the error in the code.

Dear cornelam, thanks for your very instructive guide as to how to connect the Arduino Mega with the Arduino Mega. However. It is key that you test your software before you upload it :)..

For newcomers they may be completely lost, when they now connect the things and they do not work.

In your example, the Master-code is sending a 0 and a 3, while the Slave-code is testing up against a '0' and a '3' (the slave is thereby testing the value 48 and the value 51, in ASCII, and not the values 0 and 3 sent by the master.)

Furthermore the import of the Wire.h library in both the good files you did, has ended up in the initial comment (line 1), and one of them is missing the Wire.h

When it did not work, I debugged the Slave first, and copied the blinking function into the receiving interrupt function - and now it started blinking. My code looked like this: (The digitalWrite's etc. made by me). Then I knew that something was transmitted from the master to the slave. The rest is history.
 
Thanks for pointing it out. I got a bit confused with how to treat incoming data with I2C, now it's clear.

I've worked out a basic bi-directional communication between two Teensies. Now I'd like to have the master set the address of the slave. I'm using signals sent from the master arriving at interrupt pins of the slave, where the latter is waiting for an OK LOW signal in a second interrupt, before it goes on and sets its own address.
In the slave Teensy, I'm stalling the code in the setup() function until the pulse in the interrupt pin which sets a boolean to true has received a LOW signal. In the master Teensy I'm using a push button to send these pulses to the slave.
It doesn't seem to work. Would appreciate it if someone can point out whether such a technique would work and what I need to correct.
Here's the code for the master:
Code:
#include <Wire.h>

byte receivedByte;
byte sendByte = 0;
byte brokenVal[2];

int wireAddrAccumPin = 2;
int wireAddrSetPin = 3;
int swPin = 4;
bool wireAddrSet = false;
const int wireAddr = 9;
int accum = 0;

void setup() {
  pinMode(wireAddrAccumPin, OUTPUT);
  pinMode(wireAddrSetPin, OUTPUT);
  pinMode(swPin, INPUT_PULLUP);
  digitalWrite(wireAddrAccumPin, HIGH);
  digitalWrite(wireAddrSetPin, HIGH);

  Wire.begin();
  Serial.begin(9600);
}

void setWireAddr() {
  for (int i = 0; i < wireAddr; i++) {
    digitalWrite(wireAddrAccumPin, LOW);
    delay(10);
    digitalWrite(wireAddrAccumPin, HIGH);
    delay(10);
    accum++;
  }
  digitalWrite(wireAddrSetPin, LOW);
  digitalWrite(wireAddrSetPin, HIGH);
  Serial.print("Address passed is ");
  Serial.println(accum);
  wireAddrSet = true;
}

void loop() {
  int swVal = digitalRead(swPin);
  if (!swVal && !wireAddrSet) {
    setWireAddr();
  }

  if (wireAddrSet) {
    Wire.beginTransmission(wireAddr);
    Wire.write(1);
    Wire.endTransmission();

    Wire.requestFrom(wireAddr, 1);
    while (Wire.available()) {
      receivedByte = Wire.read();
    }

    if (receivedByte) {
      Wire.beginTransmission(wireAddr);
      Wire.write(2);
      Wire.endTransmission();  
      int index = 0;
      Wire.requestFrom(wireAddr, 2);
      while (Wire.available()) {
        brokenVal[index++] = Wire.read();
      }
      int assembledVal = brokenVal[0] + (brokenVal[1] * 128);
      Serial.print("Pot val: ");
      Serial.println(assembledVal);
    }
  }
  delay(10);
}

And here's the code for the slave:
Code:
#include <Wire.h>
#define POTDELTA 8
#define INTERRUPT_PIN1 2
#define INTERRUPT_PIN2 3

int potVal = 0;

byte dataToSend = 0;
byte brokenVal[2];
int onReceiveNum = 0;

volatile int wireCh = 0;
volatile bool wireChSet = false;

void setup() {
  pinMode(INTERRUPT_PIN1, INPUT_PULLUP);
  pinMode(INTERRUPT_PIN2, INPUT_PULLUP);
  
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN1), accumWireAddr, FALLING);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN2), setWireAddr, FALLING);

  while (!wireChSet) {
    // wait until we receive all the pulses from the master
    // so we can set the desired i2c address
  }

  Wire.begin(wireCh);
  // the function below is registered for data received by the master
  Wire.onReceive(receiveEvent);
  // the function below is registered for data requested by the master
  Wire.onRequest(requestEvent);
}

void accumWireAddr() {
  wireCh++;
}

void setWireAddr() {
  wireChSet = true;
}

void loop() {
  int curPotVal = analogRead(0);
  if (abs(curPotVal - potVal) > POTDELTA) {
    potVal = curPotVal;
    brokenVal[0] = potVal & 0x007f;
    brokenVal[1] = potVal >> 7;
    dataToSend = 1;
  }
}

void receiveEvent(int howMany) {
  // the byte received tells us what the next request will expect 
  onReceiveNum = Wire.read();
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  if (onReceiveNum == 1) {
    Wire.write(dataToSend);
  }
  else {
    Wire.write(brokenVal, 2);
    dataToSend = 0;
  }
}

The communication works fine if I hard code the address of the slave in both sketches, but now with this sort of dynamic address setting.
 
Last edited:
Take a look at File > Examples > Wire > Scanner.

Maybe some useful ideas in there?
Well, this sketch scans all address and waits for a 0 error message. This means that the slaves connected to the master have fixed addresses. What I'd like to do is have the master set an address to the slave (not via I2C, but via interrupt signals). In the code I posted above, the address the master is trying to pass to the slave is fixed in the master's code, but that's only a test. I actually want to be able to set "any" address, not hard coded.
In short, the sketch you suggested doesn't provide something useful for what I need.
Thanks though.
 
Fiddling a bit I realized that when the Teensy boots up, pulses are sent through its pins, so the slave Teensy was receiving the address set pulse before the actual address was sent (it was an accumulated value). Adding a small delay and zeroing the address and the address set values solved the problem. Below is the code that works. I'm now using two push buttons to choose between two different addresses, to make sure it works.

This is the master code:
Code:
#include <Wire.h>

byte receivedByte;
byte sendByte = 0;
byte brokenVal[2];

int wireAddrAccumPin = 2;
int wireAddrSetPin = 3;
int swPin1 = 4;
int swPin2 = 5;
int wireAddr;
bool wireAddrSet = false;
int accum = 0;

void setup() {
  pinMode(wireAddrAccumPin, OUTPUT);
  pinMode(wireAddrSetPin, OUTPUT);
  pinMode(swPin1, INPUT_PULLUP);
  pinMode(swPin2, INPUT_PULLUP);
  digitalWrite(wireAddrAccumPin, HIGH);
  digitalWrite(wireAddrSetPin, HIGH);

  Wire.begin();
  Serial.begin(9600);
}

void setWireAddr(int addr) {
  wireAddr = addr;
  for (int i = 0; i < wireAddr; i++) {
    digitalWrite(wireAddrAccumPin, LOW);
    delay(10);
    digitalWrite(wireAddrAccumPin, HIGH);
    delay(10);
    accum++;
  }
  digitalWrite(wireAddrSetPin, LOW);
  digitalWrite(wireAddrSetPin, HIGH);
  Serial.print("Address passed is ");
  Serial.println(accum);
  wireAddrSet = true;
}

void loop() {
  int swVal1 = digitalRead(swPin1);
  int swVal2 = digitalRead(swPin2);
  if (!swVal1 && !wireAddrSet) {
    setWireAddr(swPin1);
  }
  if (!swVal2 && !wireAddrSet) {
    setWireAddr(swPin2);
  }

  if (wireAddrSet) {
    Wire.beginTransmission(wireAddr);
    Wire.write(1);
    Wire.endTransmission();

    Wire.requestFrom(wireAddr, 1);
    while (Wire.available()) {
      receivedByte = Wire.read();
    }

    if (receivedByte) {
      Wire.beginTransmission(wireAddr);
      Wire.write(2);
      Wire.endTransmission();  
      int index = 0;
      Wire.requestFrom(wireAddr, 2);
      while (Wire.available()) {
        brokenVal[index++] = Wire.read();
      }
      int assembledVal = brokenVal[0] + (brokenVal[1] * 128);
      Serial.print("Pot val: ");
      Serial.println(assembledVal);
    }
  }
  delay(10);
}
And here's the slave code:
Code:
#include <Wire.h>
#define POTDELTA 8
#define INTERRUPT_PIN1 2
#define INTERRUPT_PIN2 3

int potVal = 0;

int ledPin = 13;

byte dataToSend = 0;
byte brokenVal[2];
int onReceiveNum = 0;

volatile int wireCh = 0;
volatile int wireChSet = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  pinMode(INTERRUPT_PIN1, INPUT_PULLUP);
  pinMode(INTERRUPT_PIN2, INPUT_PULLUP);
  
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN1), accumWireAddr, FALLING);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN2), setWireAddr, FALLING);

  delay(100);
  wireChSet = wireCh = 0;

  while (wireChSet < 1) {
    // wait until we receive all the pulses from the master
    // so we can set the desired i2c address
  }

  Wire.begin(wireCh);
  // the function below is registered for data received by the master
  Wire.onReceive(receiveEvent);
  // the function below is registered for data requested by the master
  Wire.onRequest(requestEvent);
  digitalWrite(ledPin, HIGH);
}

void accumWireAddr() {
  wireCh++;
}

void setWireAddr() {
  wireChSet++;
}

void loop() {
  int curPotVal = analogRead(0);
  if (abs(curPotVal - potVal) > POTDELTA) {
    potVal = curPotVal;
    brokenVal[0] = potVal & 0x007f;
    brokenVal[1] = potVal >> 7;
    dataToSend = 1;
  }
}

void receiveEvent(int howMany) {
  // the byte received tells us what the next request will expect 
  onReceiveNum = Wire.read();
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  if (onReceiveNum == 1) {
    Wire.write(dataToSend);
  }
  else {
    Wire.write(brokenVal, 2);
    dataToSend = 0;
  }
}
 
Status
Not open for further replies.
Back
Top