Random SMBus IOError when writing to an Arduino Slave


5 posts
by daneallen » Sun Aug 11, 2013 8:29 pm
This is my first embedded systems project and my first request for help from a forum.

Basically the goal is to get a Christmas light show as directed by the Pi playing a song over FM transmission while parsing a .csv file (the script of lighting control) using a python script. The Pi would send I2C bytes to addressable Arduino slaves (eventually just atmega328P MCUs) which will perform AC phase control on a set of Christmas lights (Wire library on Arduino and SMBus on the Pi). The intent is that the Pi would send a byte to a unique Arduino address to indicate the level of brightness for the lights (i.e. 0-full brightness, 100-full dim, 101-255 commands reserved for basic fade up/down at various rates). The AC phase control works on the Arduino, no real issue there albeit with 2 interrupts (the zero crossing detection and Timer1 for TRIAC control) perhaps this is part of the problem? I've read that the Wire library also uses interrupts, perhaps the problem lies there, as interrupts are conflicting?

I have successfully written information to the Arduino slave, however I continue to get random and unexplainable IOError as follows:
Code: Select all
...
43
44
45
46
47
48
Traceback (most recent call last):
  File "i2c.py", line 8, in <module>
    bus.write_byte(0x30,((int(a))% 2**8))
IOError: [Errno 5] Input/output error


The test python code I'm using for the write is:
Code: Select all
import smbus
import time

bus = smbus.SMBus(1)
a=0

while True:
        bus.write_byte(0x30,((int(a))% 2**8))
        time.sleep(.1)
        #temp = 128-a
        print a
        if(a==99):
                a = 0
        a+=1


I've tried my code with and without the Arduino internal pulll-up resistors disabled, with and without 4k7 external resistors. The Pi and Arduino are connected using the adafruit bi-directional level shifter. I've tried writing at slower speeds (just by modifying the sleep variable).

I honestly just picked this whole thing up as a hobby no more than 3 months ago with a Sparkfun Arduino Uno kit and got hooked. I've learned a ton in that short time, I'm worried though that the solution to this problem may involve either an oscilloscope and or data analyzers which I obviously do not own or even know how to use.

I'm not committed to using python as the controlling script (in fact I have more experience with Java) and it seems that the documentation for the SMBus is lacking. Does anybody have better luck using Java as the I2C controlling language?

Any help at all will be very much appreciated.

For kicks the Arduino code is below:
Code: Select all
/*
AC Light Control
 
 Updated by Robert Twomey <rtwomey@u.washington.edu>
 
 Changed zero-crossing detection to look for RISING edge rather
 than falling.  (originally it was only chopping the negative half
 of the AC wave form).
 
 Also changed the dim_check() to turn on the Triac, leaving it on
 until the zero_cross_detect() turn's it off.
 
 Adapted from sketch by Ryan McLaughlin <ryanjmclaughlin@gmail.com>
 http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230333861/30
 */


#include <TimerOne.h>           // Avaiable from http://www.arduino.cc/playground/Code/Timer1
#include <Wire.h>

// Sample TWI transmission commands
//#define TWI_CMD_MASTER_WRITE 0x10
//#define TWI_CMD_MASTER_READ  0x20



// When there has been an error, this function is run and takes care of it
unsigned char TWI_Act_On_Failure_In_Last_Transmission ( unsigned char TWIerrorMsg );

char ADDRESS = 0x30;

volatile int i=0;               // Variable to use as a counter
volatile boolean zero_cross=0;  // Boolean to store a "switch" to tell us if we have crossed zero
int AC_pin = 11;                // Output to Opto Triac
volatile unsigned int dim = 0;                    // Dimming level (0-100)  0 = on, 100 = 0ff
volatile unsigned int command = 0;
int inc=1;                      // counting up or down, 1=up, -1=down

volatile int caseNumber = 0;
int steps = 100;

int freqStep = 83;    // This is the delay-per-brightness step in microseconds.
// It is calculated based on the frequency of your voltage supply (50Hz or 60Hz)
// and the number of brightness steps you want.
//
// The only tricky part is that the chopper circuit chops the AC wave twice per
// cycle, once on the positive half and once at the negative half. This meeans
// the chopping happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.

// To calculate freqStep you divide the length of one full half-wave of the power
// cycle (in microseconds) by the number of brightness steps.
//
// (1000000 uS / 120 Hz) / 128 brightness steps = 65 uS / brightness step
//
// 1000000 us / 120 Hz = 8333 uS, length of one half-wave.

void setup() {                                      // Begin setup
  pinMode(AC_pin, OUTPUT);                          // Set the Triac pin as output
  attachInterrupt(0, zero_cross_detect, RISING);   // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
  Timer1.attachInterrupt(dim_check, freqStep);     
  // Use the TimerOne Library to attach an interrupt
  // to the function we use to check to see if it is
  // the right time to fire the triac.  This function
  // will now run every freqStep in microseconds.   
  pinMode(9, OUTPUT);
  digitalWrite(9,HIGH);

  Wire.begin(ADDRESS);
  Wire.onReceive(setDimmer);
  Wire.onRequest(getDimmer);
  //Serial.begin(9600);
 
  #ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
  #endif
 
  #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
    // deactivate internal pull-ups for twi
    // as per note from atmega8 manual pg167
    cbi(PORTC, 4);
    cbi(PORTC, 5);
  #else
    // deactivate internal pull-ups for twi
    // as per note from atmega128 manual pg204
    cbi(PORTD, 0);
    cbi(PORTD, 1);
  #endif

}

void zero_cross_detect() {   
  zero_cross = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
  i=0;
  digitalWrite(AC_pin, LOW);       // turn off TRIAC (and AC)
}                                 

// Turn on the TRIAC at the appropriate time
void dim_check() {                   
 
  if(zero_cross == true) {
       
    if(i>=dim) {
      //if(dim != 128){     
        digitalWrite(AC_pin, HIGH); // turn on light
        //digitalWrite(AC_pin, LOW); //the tirac will hold?
      //}
      i=0;  // reset time step counter                         
      zero_cross = false; //reset zero cross detection
    }
    else {
      i++; // increment time step counter                     
    }                               
  }
}

void setDimmer(int howMany){
  while (Wire.available() > 0){
     command = (int)Wire.read();
  }
}

void getDimmer(void){
   Wire.write(command);
}

void loop() {
    if(command>steps){
           caseNumber = command - steps;
    }else{
           dim = command;
           caseNumber = 0;
    }

  switch(caseNumber){
    //fade down
    case 1: //10 second fade
      //durationStep = (10*1000)/128; //over 10 seconds
      dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(100);
        break;
    case 2: //5 second fade
      //durationStep = (5*1000)/128; //over 5 seconds
      dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(50);
        break;
     
      case 3: //2 second fade
       //durationStep = (1*1000)/128; //over 1 seconds
       dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(20);
        break;
       
       case 4: //1 second fade
       //durationStep = (1*1000)/128; //over 1 seconds
       dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(10);
        break;
       
         case 5: //105 - .5 second fade
         //Serial.println("Case 5");
       //durationStep = (1*1000)/128; //over 1 seconds
       dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(5);
        break;
       
         case 6: //106 1 second fadeRate to 100
       //durationStep = (1*1000)/128; //over 1 seconds
       //inc = -1;
       //dim+=inc;
       dim+=1;
        if((dim>=steps)){
         // inc*=-1;
          caseNumber = 0;
          //dim = steps;
        }
        delay(10);
        break;
       
         case 7: //107 1 second fadeRate to 0
       //durationStep = (1*1000)/128; //over 1 seconds
       dim-=1;
        if(dim<=0){
          //inc*=-1;
          caseNumber = 0;
          //dim = 0;
        }
        delay(10);
        break;
       
         case 8: //1.5 second fade
       //durationStep = (1*1000)/128; //over 1 seconds
       dim+=inc;
        if((dim>=steps) || (dim<=0)){
          inc*=-1;
          //caseNumber = 0;
        }
        delay(15);
        break;
     
  }
 
}

Posts: 1
Joined: Sun Aug 11, 2013 7:50 pm
by cloom » Sat Nov 23, 2013 10:43 am
Hello,

I have the same issue, I have lots of random errors but when I do a "watch -n 2" on the "i2cdetect -y 1" I have sometimes different output:
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@pitest ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- 21 -- 23 -- -- -- 27 -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --


If anyone have an idea of what could be wrong here I would love to hear it :)
Posts: 2
Joined: Sat Nov 23, 2013 10:40 am
by cloom » Sat Nov 23, 2013 1:00 pm
ahah I am such a silly goat sometimes!! I did not connect A0 A1 A2 to the ground and the !RESET to +3V
It works fine now I have done this.
Posts: 2
Joined: Sat Nov 23, 2013 10:40 am
by theUser » Tue Dec 17, 2013 10:24 pm
I have been struggling for a few hours with this same problem, and i stumbled upon a very simple work around. I thought it was novel enough to create an account here just to share with you guys. So here it is, as succinctly as possible.

See my diagnostic output for context.


I wrote a short python script which sends the same 24 bytes to the arduino, the arduino then sends it back.
If we see the same data we transmitted echoed back to the pi that confirms the data was received correctly.

The pattern below, which begins in 254 and ends with 255, is the arduino echoing back the data pattern it was sent

The write function then pops IOError5, usually ending the program. I found if I ran i2cdetect there is a device showing at 0x03 that does not exist. The arduino is the only device on the bus at 0x4a. I have no idea what causes this to pop up. You can also see the corrupted data below the output of i2cdetect.

Without calling i2cdetect, reattempts to write to the i2c bus cause the arduino to disappear from the bus completely and have to be reset before being accessible.

The ********important******** part is that i noticed calling i2cdetect seems to somehow help clear/reinitialize the i2c connection.
Instead of the arduino disappearing from the bus like it had been. If a subprocess call is made to i2c detect during the error handling routine, the bus errors seem to be cleaned up and the program can continue reading/writing for a 400-1000 more cycles before again needing to call i2c detect.

Call i2c detect like this (from python)
Code: Select all
        except IOError:
                subprocess.call(['i2cdetect', '-y', '1'])
                flag = 1   # optional signal flag


Below are a snippet of the output of my diagnostic program and the python code that generated it. The arduino code just echoes data received back at the pi
I hope this spares someone else a headache.

Code: Select all
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
413
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
414
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
415
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
416
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
417
IOError in writeData
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          03 -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- 4a -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 1, 6, 255]
there were 417 iterations before error
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
1
[254, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 255]
2



Python Code

Code: Select all
#!/usr/bin/python

import smbus
import sys
import time
import subprocess

bus = smbus.SMBus(1)
slave1 = 0x4A


rxData = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
txData = [254,0,0,0,56,0,0,0,0,0,0,106,0,0,0,0,42,0,5,0,0,0,0,255]
flag = 0

#reads data from the i2c bus
def readData(addy, bytes): #address, number of bytes to read
        data=[0] * bytes
        try:
                for i in range(0, bytes):
                        data[i]= (bus.read_byte(addy))
                return data
        except IOError:
                print("IOError in readData")
                return

def writeData(addy, data):
        try:
                bus.write_i2c_block_data(addy, 0x00, data)
                flag = 0
        except IOError:
                print("IOError in writeData")
                subprocess.call(['i2cdetect', '-y', '1'])
                flag = 1
        return



counter = 0
while True:
        writeData(slave1, txData)
        time.sleep(.001)
        if flag == 0:
                rxData =  readData(slave1, 24)
                if rxData == txData:
                        print(rxData)
                        time.sleep(.001)
                        counter += 1
                        print(counter)
                else:
                        print(rxData)
                        print("there were " + str(counter) + " iterations befor$
                        counter = 0
        else:
                flag = 0



This method even ensures that all transmissions to the arduino from the pi are error free. with minor alterations and another pass of the message back and forth it could easily check errors in the other direction as well.

This is the best I can do until some wizard fixes the drivers.
Cheers
Posts: 5
Joined: Tue Dec 17, 2013 9:45 pm
by theUser » Wed Dec 18, 2013 5:50 am
cloom wrote:ahah I am such a silly goat sometimes!! I did not connect A0 A1 A2 to the ground and the !RESET to +3V
It works fine now I have done this.


Do you mean on the arduino ?
Posts: 5
Joined: Tue Dec 17, 2013 9:45 pm