Better wire library

Hi All,

I'm a bit desperate here! few days to go before a festival, my costume using 2 x Atmega382P is not working! They are linked via the I2C bus and I use the wire library but it continually locks up. I have tried all sorts to fix this over the last few days but nothing is helping. Is there a 2 wire library, simple to use as I'm only sending 3 int's 0-255 for RBG values?

I know more info will be required on this post, but I'll wait until asked as I don't want to swamp the post if there is a simple new library or way.

P.S. I am not keen on posting the full code as this will start a back and forth conversation consuming too much valuable time, so here are the core wire commands.

Receiver:


void setup(){
  Wire.begin(8);
  Wire.setWireTimeout(3000,true);  
  Wire.onReceive(ReceiveEvent)
}

void ReceiveEvent() {
  RR = Wire.read(); 
  GG = Wire.read(); 
  BB = Wire.read(); 
}

----------------------------------
Sender:
void setup(){
  Wire.begin();
  Wire.setWireTimeout(3000,true);  
}

void loop(){
  Wire.beginTransmission(8);
  Wire.write(r);
  Wire.write(g);
  Wire.write(b);
  Wire.endTransmission();  
}

Thanks

What you posted certainly won't work and you failed to provide the information needed to fix it. So, you are going to have that back and forth after all.

For posting hints, see the "How to get the best out of the forum" post.

You prolly won't be too keen on posting a schematic either then. But both are necessary.

I2C or SPI or serial comms shouldn't just lock up.

You undoubtedly have a software issue (need to see the code) a power issue (need to see a schematic) or some mechanical or static electrical or other unknown problem.

So we'd like to see the costume and how the boards are arranged and connected, too.

a7

The Arduino Wire library works according to the I2C Standard. If a other library also works according to the I2C standard, then how could it improve things ?

If your I2C bus fails, can you tell more about your I2C bus ?
How long are the wires, how are the wires organized in the cable, is there a voltage level conflict, what are your pullup resistors, are there motors or leds, does the I2C bus have a good GND wire, and so on and so on.

The I2C bus is weak, because its high level is created with a pullup resistor. The I2C bus was not designed to go into a cable. The I2C bus is not fault tolerant. The I2C bus can not deal with crosstalk between SDA and SCL. The I2C bus is not really a communication bus.

In other words: You should NOT have used the I2C bus for your project.

[UPDATE] Oops, forgot the word NOT. Now fixed. thanks jremington.

Did you mean "should not have used the I2C bus"? UART serial would be much better.

1 Like

I second that.

How fast must the transmission of the three bytes be?

2x Atmega328P means what exact type of microcontroller?
Uno?, Nano?
modifying your code from using I2C to serial for sending 3 bytes should be done within 1 hour.

best regards Stefan

if you don't want to post your full code, strip down your codes to two working (= compileable) examples.
Furthermore, provide a schematic including the measured distance between Arduino A and Arduino B.

Failing to provide these minimum requirements in your opening post is indeed wasting our valuable time.

Blasting data across the wires at the highest possible speed, without checking whether the receiver has time to do anything with it, is a recipe for failure.

Does this mean if more than one byte shall be transmitted that this should be done this way

Syntax

Wire.write(data, length)

  • data: an array of data to send as bytes.
  • length: the number of bytes to transmit.

like shown in the reference
https://www.arduino.cc/reference/en/language/functions/communication/wire/write/

My point is that the sending code does not check for errors, has no way of knowing whether the receiver can handle the data stream, and leaves no time for the receiver to do any processing.

OK, sorry guys I just thought trying to keep it what I thought was simple would help, but clearly not :crazy_face:

  • Below are the two sets of code
  • The two Atmega328P's are on a PCB I have made so not an Arduino as such.
  • The I2C lines are PCB tracks (not wires)
  • I did forget the pullup resistors on the PCB but I have soldered some on and it makes no difference.

The problem is as I push the button the lights come on but quite often the light attached to the second chip lights up intermittently.

I will try and get the circuit on here ASAP if needed hope this helps you help me quickly.

P.S. The reason I asked for an alternative library is I had read quite a few posts about the wire library locking up.

Sender Code

#include "RunningAverage.h"
#include <Wire.h>


#define Switch 8

#define RedLed 10
#define GreenLed 9

#define Red 0
#define Yellow 60
#define Green 120
#define Cyan 180
#define Blue 240
#define Magenta 300
int Colours[6] = {Red,Yellow,Green,Cyan,Blue,Magenta};

double hue, saturation, lighting, value;
  

int R_1 = 3;      // LED connected to digital pin 9
int G_1 = 5;      // LED connected to digital pin 9
int B_1 = 6;      // LED connected to digital pin 9

int i = 0;
int fadedwn=255;
int ThisColour;


unsigned int level[7];

int R=0,G=0,B=0;
byte rgb1[3];
byte rgb2[3];
float h_1,s_1,i_1;
float h_2,s_2,i_2;


void RGB1DWN(int fade_time);
void RGB2DWN(int fade_time);
void ResetMEQU7(void);
void hsi2rgb1(float H, float S, float I);
void hsi2rgb1(float H, float S, float I);

RunningAverage level0(17);
RunningAverage level1(17);
RunningAverage level2(17);
RunningAverage level3(17);
RunningAverage level4(17);
RunningAverage level5(17);
RunningAverage level6(17);


void setup() 
{
  Serial.begin(115200);
  Wire.begin();
  Wire.setWireTimeout(3000,true);  
  analogReference(DEFAULT);
  level0.clear();
  level1.clear();
  level2.clear();
  level3.clear();
  level4.clear();
  level5.clear();
  level6.clear();
  pinMode(A0,INPUT);  
  pinMode(A1,OUTPUT);
  pinMode(A2,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(R_1,OUTPUT);
  pinMode(G_1,OUTPUT);
  pinMode(B_1,OUTPUT);
  pinMode(Switch,INPUT_PULLUP);  
  pinMode(13,OUTPUT);
  
  ResetMEQU7();
  randomSeed(analogRead(0));  
  i_1 = 1;
  i_2 = 1;
  ThisColour = Colours[random(5)];
}


void loop() 
{
  int R=0,G=0,B=0;
  unsigned int Level1;
  h_1=180;
  s_1=1;
  i_1=2;
  h_2=180;
  s_2=1;
  i_2=2;

  Serial.println(fadedwn);
  if(digitalRead(Switch)==0){
    fadedwn=255;
    i_1 = 1;
    i_2 = 1;
    analogWrite(GreenLed,fadedwn);
    analogWrite(RedLed,fadedwn);
    hsi2rgb1(ThisColour,s_1,i_1);
    hsi2rgb2(ThisColour,s_2,i_2);
    delay(5);
  } else {
    ThisColour = Colours[random(5)];
    if(fadedwn<20) {
      fadedwn=0;
    } else {
      fadedwn-=20;
    }
    RGB1DWN(2);
    RGB2DWN(2);
    analogWrite(GreenLed,fadedwn);
    analogWrite(RedLed,fadedwn);
  }
}

void RGB1DWN(int fade_time){
    i_1=i_1-0.15;
    if(i_1>1 || i_1<0){
      i_1=0;
    }
    hsi2rgb1(h_1,s_1,i_1);
    delay(fade_time);
}




void RGB2DWN(int fade_time){
    i_2=i_2-0.15;
    if(i_2>1 || i_2<0){
      i_2=0;
    }
    hsi2rgb2(h_2,s_2,i_2);
    //Done via slave
    delay(fade_time);
}


void ResetMEQU7(void){
  digitalWrite(A1,LOW);
  digitalWrite(A2,LOW);
  delay(1);
  // Reset the MSGEQ7 as per the datasheet timing diagram
  digitalWrite(A1,HIGH);
  delay(1);
  digitalWrite(A1,LOW);
  digitalWrite(A2,HIGH);
  delay(1);
}



void hsi2rgb1(float H, float S, float I) {
  int r,g,b;
  
  H = fmod(H,360); // cycle H around to 0-360 degrees
  H = 3.14159*H/(float)180; // Convert to radians.
  S = S>0?(S<1?S:1):0; // clamp S and I to interval [0,1]
  I = I>0?(I<1?I:1):0;
    
  // Math! Thanks in part to Kyle Miller.
  if(H < 2.09439) {
    r = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    g = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    b = 255*I/3*(1-S);
  } else if(H < 4.188787) {
    H = H - 2.09439;
    g = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    b = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    r = 255*I/3*(1-S);
  } else {
    H = H - 4.188787;
    b = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    r = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    g = 255*I/3*(1-S);
  }
  rgb1[0]=r;
  rgb1[1]=g;
  rgb1[2]=b;
  analogWrite(R_1, rgb1[0]);
  analogWrite(G_1, rgb1[1]);
  analogWrite(B_1, rgb1[2]);
}

void hsi2rgb2(float H, float S, float I) {
  int r,g,b;
  
  H = fmod(H,360); // cycle H around to 0-360 degrees
  H = 3.14159*H/(float)180; // Convert to radians.
  S = S>0?(S<1?S:1):0; // clamp S and I to interval [0,1]
  I = I>0?(I<1?I:1):0;
    
  // Math! Thanks in part to Kyle Miller.
  if(H < 2.09439) {
    r = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    g = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    b = 255*I/3*(1-S);
  } else if(H < 4.188787) {
    H = H - 2.09439;
    g = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    b = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    r = 255*I/3*(1-S);
  } else {
    H = H - 4.188787;
    b = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    r = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    g = 255*I/3*(1-S);
  }
  rgb2[0]=r;
  rgb2[1]=g;
  rgb2[2]=b;
  Wire.beginTransmission(8);
  Wire.write(r);
  Wire.write(g);
  Wire.write(b);
  Wire.endTransmission();  
}

Receiver code

#include <Wire.h>

int R = 3;      // LED connected to digital pin 9
int G = 5;      // LED connected to digital pin 9
int B = 6; 
int RR,GG,BB;

void setup() {
  Wire.begin(8);
  Wire.setWireTimeout(3000,true);  
  Wire.onReceive(ReceiveEvent);
  pinMode(R,OUTPUT);
  pinMode(G,OUTPUT);
  pinMode(B,OUTPUT);
}

void loop() {
  analogWrite(R, RR);
  analogWrite(G, GG);
  analogWrite(B, BB);   
  
}

void ReceiveEvent() {
  RR = Wire.read(); 
  GG = Wire.read(); // receive a byte as character
  BB = Wire.read(); // receive a byte as character
}

The I2C bus is never simple, because so many things can go wrong. Some manage to do 10 things wrong and ask for a single small quick fix :grimacing:
Can you be more specific ? What value have the pullup resistors ? 10k is just a little pullup and 1k8 is a lot of pullup (which you may need).
Are there (ground) currents in the same PCB ? Can you show a photo ?

Everyone seems to forget the decoupling capacitors. If you made your own PCB without decoupling capacitors, then your whole project is very noisy.

There is something to say about the software, the Slave should have 'volatile' for the variables that are both used in the interrupt and the loop() and the receiveEvent() should check the 'howMany' parameter, but that does not explain your problem.

in I2C-buses the partners have different roles. One is master (controlling device) one is slave (peripheral device)

I don't have much knowledge about these details but if an Arduinco shall act as a peripheral-slave-device the code looks different than for the usual arduino beeing the master-device.

If you lookup the reference for the command read
https://www.arduino.cc/reference/en/language/functions/communication/wire/read/

the code looks different than yours

How fast must the data be transferred between the two Atmegas?
would a baudrate of 19200 which means 5 bytes with 10 bits (8 data +start/stop-bit)
5 * 10 / 19200 = 0,0026 seconds be fast enough?

5 bytes as you would use a

  • start-character example "<"
  • 3 databytes
  • endcharacter example ">"

If yes then I would simply change to a software-serial-connection

to make sure that the sendbuffer gets not filled up completely I would send back a acknowledge-byte everytime the receiver has received one message

I quogled and found this code that is based on event received

it demonstrates the transfer of single byte.
Your code has to receive three bytes
This means you have to make sure that a received byte is assigned to the right variable
For this the communication must be synchronised

and this thread explains how to transfer multiple bytes

best regards Stefan

a)
I don't have this file:

Therefore the code doesn't compile.
Please upload the file or describe exactly where it can be found (always put a link to the include where it can be retrieved!)

b) Which of your Atmegas freezes? The Master or the Slave?
a heartbeating LED or Serial outputs could help to identify the wrong side.

c) See the IDE Example "slave_receiver".
They only read data if data is available.

```
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}
```

imho this is more stable than to read hardcoded 3 bytes...

Hi noiasca,

here is the link

RunningAverage link

Thanks

Well, I posed the code, and only one reply after been slated for not providing the code :roll_eyes: can anyone comment even if it's "you code looks fine"

Thanks

It is semi-fine :rofl:

If the receiveEvent() would check the number of received bytes, then you have an extra check.

volatile int  RR,GG,BB;

...

void receiveEven( int howMany)
{
  if( howMany == 3)
  {
      RR = Wire.read(); 
      GG = Wire.read();
      BB = Wire.read();
  }
}

I think that is not the cause of your problem. Can you answer my questions ?

No. When you begin a transmission the data you write goes into a buffer which is then sent in one go when you end the transmission. So the way you fill up that buffer has no impact.

I think the point was more having a robust protocol to handle what’s going on that wire.

——

To OP

Also checking for hardware issues on GND plate would be a must. Do you have access to an oscilloscope to monitor what’s going on on those wires?

I'd wish arduino Wire had option to change bufferlength. current is 128 but if you want to send a line for oled for example is 128 long plus command.

Hi Koepel,

Here is the schmatic, as I said in my post I forgot the pullup's but I did fit these after and it made no difference (4K7)

Thanks