Go to the documentation of this file.00001
00002
00003 import time
00004
00005 import pigpio
00006
00007 class sniffer:
00008 """
00009 A class to passively monitor activity on an I2C bus. This should
00010 work for an I2C bus running at 100kbps or less. You are unlikely
00011 to get any usable results for a bus running any faster.
00012 """
00013
00014 def __init__(self, pi, SCL, SDA, set_as_inputs=True):
00015 """
00016 Instantiate with the Pi and the gpios for the I2C clock
00017 and data lines.
00018
00019 If you are monitoring one of the Raspberry Pi buses you
00020 must set set_as_inputs to False so that they remain in
00021 I2C mode.
00022
00023 The pigpio daemon should have been started with a higher
00024 than default sample rate.
00025
00026 For an I2C bus rate of 100Kbps sudo pigpiod -s 2 should work.
00027
00028 A message is printed for each I2C transaction formatted with
00029 "[" for the START
00030 "XX" two hex characters for each data byte
00031 "+" if the data is ACKd, "-" if the data is NACKd
00032 "]" for the STOP
00033
00034 E.g. Reading the X, Y, Z values from an ADXL345 gives:
00035
00036 [A6+32+]
00037 [A7+01+FF+F2+FF+06+00-]
00038 """
00039
00040 self.pi = pi
00041 self.gSCL = SCL
00042 self.gSDA = SDA
00043
00044 self.FALLING = 0
00045 self.RISING = 1
00046 self.STEADY = 2
00047
00048 self.in_data = False
00049 self.byte = 0
00050 self.bit = 0
00051 self.oldSCL = 1
00052 self.oldSDA = 1
00053
00054 self.transact = ""
00055
00056 if set_as_inputs:
00057 self.pi.set_mode(SCL, pigpio.INPUT)
00058 self.pi.set_mode(SDA, pigpio.INPUT)
00059
00060 self.cbA = self.pi.callback(SCL, pigpio.EITHER_EDGE, self._cb)
00061 self.cbB = self.pi.callback(SDA, pigpio.EITHER_EDGE, self._cb)
00062
00063 def _parse(self, SCL, SDA):
00064 """
00065 Accumulate all the data between START and STOP conditions
00066 into a string and output when STOP is detected.
00067 """
00068
00069 if SCL != self.oldSCL:
00070 self.oldSCL = SCL
00071 if SCL:
00072 xSCL = self.RISING
00073 else:
00074 xSCL = self.FALLING
00075 else:
00076 xSCL = self.STEADY
00077
00078 if SDA != self.oldSDA:
00079 self.oldSDA = SDA
00080 if SDA:
00081 xSDA = self.RISING
00082 else:
00083 xSDA = self.FALLING
00084 else:
00085 xSDA = self.STEADY
00086
00087 if xSCL == self.RISING:
00088 if self.in_data:
00089 if self.bit < 8:
00090 self.byte = (self.byte << 1) | SDA
00091 self.bit += 1
00092 else:
00093 self.transact += '{:02X}'.format(self.byte)
00094 if SDA:
00095 self.transact += '-'
00096 else:
00097 self.transact += '+'
00098 self.bit = 0
00099 self.byte = 0
00100
00101 elif xSCL == self.STEADY:
00102
00103 if xSDA == self.RISING:
00104 if SCL:
00105 self.in_data = False
00106 self.byte = 0
00107 self.bit = 0
00108 self.transact += ']'
00109 print (self.transact)
00110 self.transact = ""
00111
00112 if xSDA == self.FALLING:
00113 if SCL:
00114 self.in_data = True
00115 self.byte = 0
00116 self.bit = 0
00117 self.transact += '['
00118
00119 def _cb(self, gpio, level, tick):
00120 """
00121 Check which line has altered state (ignoring watchdogs) and
00122 call the parser with the new state.
00123 """
00124 SCL = self.oldSCL
00125 SDA = self.oldSDA
00126
00127 if gpio == self.gSCL:
00128 if level == 0:
00129 SCL = 0
00130 elif level == 1:
00131 SCL = 1
00132
00133 if gpio == self.gSDA:
00134 if level == 0:
00135 SDA = 0
00136 elif level == 1:
00137 SDA = 1
00138
00139 self._parse(SCL, SDA)
00140
00141 def cancel(self):
00142 """Cancel the I2C callbacks."""
00143 self.cbA.cancel()
00144 self.cbB.cancel()
00145
00146 if __name__ == "__main__":
00147
00148 import time
00149
00150 import pigpio
00151
00152 import I2C_sniffer
00153
00154 pi = pigpio.pi()
00155
00156 s = I2C_sniffer.sniffer(pi, 1, 0, False)
00157
00158 time.sleep(60000)
00159
00160 s.cancel()
00161
00162 pi.stop()
00163