Xenoshell
Xenoshell

Reputation: 91

I2C read error while getting data from sensor with pigpio

i have a Tkinter GUI and the acceleration sensor MMA8452Q. I set up the sensor that if a threshold is reached, a interrupt flag gets set and data for a specific period of time gets collected and plotted through the GUI.

Sometimes when I read the data or at any other point of using an I2C read function to read a register i get the error "I2C read failed" (when the error happens in the check_flags() method) and sometimes I get something like this:

interrupt 1 True
4 0 1810275105
Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "/home/pi/.../tkinter/mix/mix_interrupt.py", line 307, in check_flags
    pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
  File "/usr/lib/python3/dist-packages/pigpio.py", line 2874, in i2c_read_byte_data
    return _u2i(_pigpio_command(self.sl, _PI_CMD_I2CRB, handle, reg))
  File "/usr/lib/python3/dist-packages/pigpio.py", line 1011, in _u2i
    raise error(error_text(v))
pigpio.error: 'I2C read failed'

Is there maybe something wrong with my code or should i could the baudrate be the fault of this problem? It doesn't happen all the time, but seemingly at random times. Just to clarify, if the error happens in the try-except-block, my data still gets plotted, but some data gets missing and the plot looks sometimes incomplete

My important code parts:

This is the thread:

threading.Thread(target=self.check_flags).start() #This is in the init method

This function runs in a thread like this and checks if self.interrupt_flag is set

def check_flags(self):
        while(True):
            #print("check the flags")
            if (self.interrupt_flag == True):
                pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
                global scale_variable
                self.x_graph = []
                self.y1_graph = []
                self.y2_graph = []
                self.y3_graph = []
                
                a = datetime.datetime.now()
                one_time = True 
                start_time = time.time()*1000
                while(time.time()*1000-start_time) < self.timeframe:
                    try:
                        b = datetime.datetime.now()
                        delta = b-a
                        y1 = self.readACCx()
                        y2 = self.readACCy()
                        y3 = self.readACCz()
                        self.y1_graph.append(y1)
                        self.y2_graph.append(y2)
                        self.y3_graph.append(y3)
                        self.x_graph.append(delta.total_seconds()*1000)
                        if (y1 > self.threshold or y2 > self.threshold): # this is for catching the whole movement if its longer than the set timeframe
                            self.timeframe += 5          
                    except pigpio.error as e:
                        print("error:", e)
                self.save_data()
                self.plot_data()
                self.interrupt_flag = False

This is one of the functions i use to read sensor data. The other two are almost identical

def readACCx(self): #reads x axis value 12 bit
        global scale_variable
        comp_acc_x2 = pi.i2c_read_byte_data(acc_sensor, OUT_X_MSB)
        comp_acc_x1 = pi.i2c_read_byte_data(acc_sensor, OUT_X_LSB)
        acc_combined = ((comp_acc_x2 << 8) | comp_acc_x1) >>4
        if acc_combined < 2048:
            acc_combined = acc_combined*scale_variable
            return acc_combined
        else:
            acc_combined = acc_combined - 4096
            acc_combined = acc_combined*scale_variable
            return acc_combined

This is my interrupt routine:

def my_callback1(self, gpio, level, tick): #first interrupt function
        self.interrupt_flag = True
        print("interrupt 1", self.interrupt_flag)
        
        print(gpio, level, tick)

EDIT (ALMOST PERFECT) WORKING CODE:

protect = threading.Lock() # this is outside of any class at the top

def check_flags(self): # new data capture method
        while(True):
            #print("check the flags")
            if (self.interrupt_flag == True):
                protect.acquire()
                ff_flag = pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
                print("interrupt cleared", ff_flag)
                global scale_variable
                self.x_graph = []
                self.y1_graph = []
                self.y2_graph = []
                self.y3_graph = []
                
                a = datetime.datetime.now()
                one_time = True 
                start_time = time.time()*1000
                while(time.time()*1000-start_time) < self.timeframe:
                    try:
                        b = datetime.datetime.now()
                        delta = b-a
                        y1 = self.readACCx()
                        y2 = self.readACCy()
                        y3 = self.readACCz()
                        self.y1_graph.append(y1)
                        self.y2_graph.append(y2)
                        self.y3_graph.append(y3)
                        self.x_graph.append(delta.total_seconds()*1000)
                        if (y1 > self.threshold or y2 > self.threshold):
                            self.timeframe += 5
                    except pigpio.error as e:
                        print("error:", e)
                self.save_data()
                self.plot_data()
                self.interrupt_flag = False
                self.timeframe = self.config.getint('setting', 'timeframe')
                print("scale_variable: ", scale_variable)
                protect.release()

Upvotes: 0

Views: 351

Answers (1)

Tim Roberts
Tim Roberts

Reputation: 54698

Your traceback doesn't match your code. The traceback shows the i2c_read_byte_data result not being saved anywhere. The I2C clock must be right, otherwise you'd get nothing at all.

The problem is probably synchronization. When you said "interrupt", that raised a red flag. If your interrupt handler tries to do an I2C operation while your mainline code is also doing one, the two will clash, especially since you have to do MSB and LSB separately. I suggest you use a threading.Lock to make sure your sequences aren't interrupted.

Upvotes: 1

Related Questions