WHC2021SIC / WHC2021SIC-TeamOnBodyGym

Team On-Body Gym Project Repository for the IEEE World Haptics Conference 2021 Student Innovation Challenge
Creative Commons Attribution Share Alike 4.0 International
0 stars 1 forks source link

Issues reading sensor data with Raspberry Pi Qwiic Hat #2

Closed adildsw closed 3 years ago

adildsw commented 3 years ago

This is in response to the communication we had over the email.

Q: Could you share with us your current code for sensor acquisition with the Raspberry Pi? For sensor data acquisition, we first ran some preliminary tests through various code snippets, the results of which we are specifying below in our response.

Q. Which "conflicts in I2C address" do you face? Could you provide us with a list of I2C addresses? After enabling I2C on Raspberry Pi, without connecting anything to the Pi Qwiic Hat, we run the following command on the command line terminal:

$ i2cdetect -y 1

This yields the following response:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

which shows that a peripheral is connected to address 0x48. Now, based on the documentation of our sensors, we find that the I2C address of both the ADC and the flex sensors is 0x48, which is the same as the occupied I2C address. The Mux and Pulse Sensor have different I2C addresses (0x70 and 0x55, respectively), and thus they show up when we run i2cdetect. However, the address 0x48 shows up regardless of whether we connect anything or not.

Q. Do problems arise only when you try to connect multiple sensors? Not entirely. Sensors with I2C address 0x48 are one part of the problem since there's a ghost peripheral with the same address as 0x48 connected to the Raspberry Pi. However, the primary problem would be the lack of Python libraries for the pulse and flex sensors.

Q. Could you get each sensor to work individually? We obtained the Python libraries for the Qwiic Pi Hat, [Qwiic Mux Breakout] (https://github.com/sparkfun/Qwiic_TCA9548A_Py), and the ADC. We were able to get the Qwiic Pi Hat and the Mux Breakout to work. However, since the ADC had the same I2C address as that of the ghost peripheral, the example code from the library returned 0s. We've not had any success in getting sensor data from the pulse and flex sensor directly through the Pi Hat Qwiic connection since there was no Python library available and we were having a hard time figuring out the documentation for implementing the low-level read/write operations. Using the Arduino connection (as described in our previously sent image), all the sensors worked effortlessly, both individually and simultaneously (code here).

Q. Could you get 1 flex sensor to work, but not many? Due to the I2C address conflict with the ghost peripheral (and also the lack of a compatible Python library), we were not able to get any of the flex sensors to work when directly connected with the Pi Hat. However, both the flex sensors worked fine when connected to the Arduino board through the breakout mux.

Q. Have you tried the configuration we proposed ... ? Yes.

Q. In the picture you sent us, why did you attach many sensors/boards of different types to the mux board, instead of just the 2 same flex sensors boards, since the purpose of Qwiic Mux Breakout is to handle multiple chips with same I2C address? Since we didn't have any success in getting the sensor data using the Qwiic Pi Hat, we switched to using an Arduino Uno board to connect all our Qwiic components. We directly connected the Arduino board to the Raspberry Pi using a USB connection. Now, since the Arduino Uno board has only a single Qwiic connector, we connected the Qwiic Mux Breakout (which has 8 input channels) to the Arduino Uno board. This technically gave us 8 Qwiic connections to the Arduino, the states of which could be controlled individually. Now, we connected all our sensors to the Mux (2 Flex Sensors, 1 ADC, 1 Pulse Sensor), loaded all the corresponding libraries of the sensors for the Arduino, and then wrote a code to individually enable each node in the Mux holding a sensor, read its value, and then disable it.

@WHC2021SIC/sic-chairs It'd be super helpful if you could either provide us with a way to read the sensor values directly through the Pi Qwiic Hat connection, or allow us to substitute the Pi Qwiic Hat with an Arduino Uno for reading the sensor data.

Looking forward to hearing from you!

ChristianFrisson commented 3 years ago

Hi @adildsw and @WHC2021SIC/team-on-body-gym,

Thanks for your replies continuing our email discussion over here. I'll reply step-by-step through a few comments.

I2C address conflicts (0x48)

Problem summary: in your system, 3 Qwiic components are currently assigned I2C address 0x48:

  1. Qwiic HAT for Raspberry Pi
  2. Qwiic Flex Glove Controller
  3. Qwiic 12 Bit ADC - 4 Channel (ADS1015)

I hope that we receive insight on I2C address values of Qwiic Raspberry Pi Hat across teams from this issue: https://github.com/WHC2021SIC/WHC2021SIC-Guide/issues/2

The Qwiic Flex Glove Controller Hookup Guide and the Qwiic 12-Bit ADC Hookup Guide mentions that their I2C address can be changed using the jumpers on the back of the board. Could you try to change the I2C address of one of your flex boards?

ChristianFrisson commented 3 years ago

Hi @adildsw and @WHC2021SIC/team-on-body-gym,

Continuing with next step.

Lack of Python libraries

Have you tried SparkFun's Qwiic_Py Python package for the qwiic system?

Pulse

The SparkFun Pulse Oximeter and Heart Rate Monitor features MAX30101 & MAX32664 chips.

Have you tried SparkFun's Qwiic_MAX3010x_Py Python Module?

See under examples:

Flex (and ADC)

The Qwiic Flex Glove Controller features an ADS1015 chip like the Qwiic 12 Bit ADC - 4 Channel (ADS1015).

Can you try with the Adafruit_Python_ADS1x15 Python code you have already obtained? (Note that this repository is archived.)

Before shipping kits to teams, SIC co-chair Jun Nishida @Jun-n managed to read Qwiic Accelerometer data from Python code using SMBus following this tutorial.

adildsw commented 3 years ago

Hi @ChristianFrisson. Thank you so much for addressing our problems! We changed the jumpers on our Flex Glove sensors and the ADC, and it now works perfectly with the Adafruit_Python_ADS1x15 library.

This just leaves us with the Pulse Sensor. We did try the SparkFun's Qwiic_MAX3010x_Py Python module, but it fails at a particular step. Here's the output for the HeartRate.py file (Note that we incorporated the change in address as 0x55, which is the address for our Pulse Sensor.)

SparkFun MAX3010x Photodetector - Example 4

The Qwiic MAX3010x is connected.
Traceback (most recent call last):
  File "/home/pi/WHC2021SIC/Workspace/heartrate.py", line 67, in <module>
    runExample()
  File "/home/pi/WHC2021SIC/Workspace/heartrate.py", line 54, in runExample
    if sensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange) == False:
  File "/usr/local/lib/python3.7/dist-packages/qwiic_max3010x/qwiic_max3010x.py", line 988, in setup
    self.setPulseAmplitudeRed(powerLevel)
  File "/usr/local/lib/python3.7/dist-packages/qwiic_max3010x/qwiic_max3010x.py", line 646, in setPulseAmplitudeRed
    self._i2c.writeByte(self.address, MAX30105_LED1_PULSEAMP, amplitude)
  File "/usr/local/lib/python3.7/dist-packages/qwiic_i2c/linux_i2c.py", line 226, in writeByte
    return self.i2cbus.write_byte_data(address, commandCode, value)
  File "/usr/local/lib/python3.7/dist-packages/smbus2/smbus2.py", line 455, in write_byte_data
    ioctl(self.fd, I2C_SMBUS, msg)
OSError: [Errno 121] Remote I/O error

The library is perhaps compatible only with the MAX3010x chips since the code fails for the following function: self.setPulseAmplitudeRed(powerLevel), and the architecture of our Pulse sensor does not have any such component. We also tried to peruse the documentation and the C++ library for our given Pulse sensor to try to implement the low-level functions in Python ourselves, but given the vastness of the documentation, it didn't seem feasible.

At this point, we have got almost all the components working directly through the Pi Hat except for the Pulse sensor. If you could guide us to the right resources to acquire data from the Pulse sensor, it'd be great!

Thank you!

ChristianFrisson commented 3 years ago

Hi @adildsw and @WHC2021SIC/team-on-body-gym,

Glad that you got almost all sensors working!

The SparkFun Pulse Oximeter and Heart Rate Monitor Hookup Guide mentions:

This board has two additional pins on it's header: the RESET and MFIO pin. These pins are required for the board to function because they determine if the board enters data collection mode or not. The Hardware Hookup section below will walk you through how to connect this board properly.

Maybe you can try to hookup these RESET and MFIO pins of the pulse sensors, to some GPIO pins of the Raspberry Pi configured as digital outputs?

adildsw commented 3 years ago

Hi @ChristianFrisson!

We tried hooking up the RESET and MFIO pins of the pulse sensor to two of the GPIO pins of the Raspberry Pi configured as digital outputs. We still got the same error.

One thing that confused us is that the Hookup Guide provides the instructions for an Arduino board, and the Arduino library has a constructor where we can provide the MFIO and RESET Pins as follows: SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin);

However, in the Python library, we couldn't find a provision to specify the GPIO pins, the constructor only allowed us to provide a custom I2C address.

ChristianFrisson commented 3 years ago

HI @adildsw,

When you tested sensors previously with your arduino board and your code:

  1. did you have the MFIO and RESET pins physically wired as in the hookup guide (I don't see such cables in the photo of your system)?
  2. did you obtain relevant readings from these printed after line bioHub.readBpm();?

In the setup of your code, this line int result = bioHub.begin(); makes use of the MFIO and RESET pins: https://github.com/sparkfun/SparkFun_Bio_Sensor_Hub_Library/blob/master/src/SparkFun_Bio_Sensor_Hub_Library.cpp

uint8_t SparkFun_Bio_Sensor_Hub::begin( TwoWire &wirePort ) {
...
digitalWrite(_mfioPin, HIGH);
digitalWrite(_resetPin, LOW);
delay(10);
digitalWrite(_resetPin, HIGH);
...
}

Could you port this sequence to python with qwiic-i2c-py?

adildsw commented 3 years ago

Hi @ChristianFrisson,

Testing the pulse sensor with the arduino board yielded relevant readings after printing from bioHub.readBpm() despite not connecting the MFIO and RESET pins, which we were quite surprised by.

Regarding the qwiic-i2c-py library, we are not quite sure how to translate the digitalWrite command into a memory read/write operation. Moreover, during our arduino tests, since we did not have to connect the MFIO and RESET pins to get the results, I'm not quite sure how relevant this would be :(

ChristianFrisson commented 3 years ago

Hi @adildsw,

Pulse pin wiring and byte writing

From your test with the arduino board, it seems that wiring the cables MFIO and RESET pins is not needed. So probably the code quoted in my previous comment is not needed either.

Can you try to run your arduino test again by removing or commenting out the quoted code?

uint8_t SparkFun_Bio_Sensor_Hub::begin( TwoWire &wirePort ) {
...
//digitalWrite(_mfioPin, HIGH);
//digitalWrite(_resetPin, LOW);
//delay(10);
//digitalWrite(_resetPin, HIGH);
...
}

If you still get proper readings, then porting this code quote won't be needed.

Otherwise, qwiic-i2c-py writeByte() may be a candidate for porting arduino digitalwrite(). writeByte also appears in the trace of your error message in your previous comment.

Pulse code

Back to your previous comment and the issue on sensor.setPulseAmplitudeRed(0x0A) # Turn Red LED to low to indicate sensor is running in ex5_HeartRate.py:

Here is the definition of setPulseAmplitudeRed: https://github.com/sparkfun/Qwiic_MAX3010x_Py/blob/9506ef1f95616271c84bb0c6db9cc821285c1af0/qwiic_max3010x/qwiic_max3010x.py#L631

    # ----------------------------------
    # setPulseAmplitudeRed()
    #
    # Set pulse amplitude (mA) of red LED
    # 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
    # See datasheet, page 21

    def setPulseAmplitudeRed(self, amplitude):
        """
            Set pulse amplitude (mA) of red LED

            :param amplitude: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)

            :return: no return value
        """
        self._i2c.writeByte(self.address, MAX30105_LED1_PULSEAMP, amplitude)

with MAX30105_LED1_PULSEAMP = 0x0C

From the name of this variable MAX30105_LED1_PULSEAMP, setPulseAmplitudeRed seems to be specific to or only tested with MAX30105 chips.

Same for next line sensor.setPulseAmplitudeGreen(0) # Turn off Green LED in ex5_HeartRate.py requiring variable MAX30105_LED3_PULSEAMP.

Can you try to run the example without both lines as in the following fork? https://github.com/WHC2021SIC/Qwiic_MAX3010x_Py/blob/max30101/examples/ex5_HeartRate.py

adildsw commented 3 years ago

Hi @ChristianFrisson,

Regarding the Pulse Pin Wiring and Byte Writing, we commented out the lines in the Arduino library that you specified. The pulse sensor worked fine and we were able to retrieve the readings correctly. This corresponds with our idea that these pin connections are irrelevant to our pulse sensor board.

Regarding the Pulse Code, upon directly running the modified code from your fork, we get the same error as the one before:

SparkFun MAX3010x Photodetector - Example 5

The Qwiic MAX3010x is connected.
Place your index finger on the sensor with steady pressure.
Traceback (most recent call last):
  File "/home/pi/Pulse/pulseTest.py", line 135, in <module>
    runExample()
  File "/home/pi/Pulse/pulseTest.py", line 77, in runExample
    if sensor.setup() == False:
  File "/home/pi/.local/lib/python3.7/site-packages/qwiic_max3010x/qwiic_max3010x.py", line 988, in setup
    self.setPulseAmplitudeRed(powerLevel)
  File "/home/pi/.local/lib/python3.7/site-packages/qwiic_max3010x/qwiic_max3010x.py", line 646, in setPulseAmplitudeRed
    self._i2c.writeByte(self.address, MAX30105_LED1_PULSEAMP, amplitude)
  File "/home/pi/.local/lib/python3.7/site-packages/qwiic_i2c/linux_i2c.py", line 226, in writeByte
    return self.i2cbus.write_byte_data(address, commandCode, value)
  File "/home/pi/.local/lib/python3.7/site-packages/smbus2/smbus2.py", line 455, in write_byte_data
    ioctl(self.fd, I2C_SMBUS, msg)
OSError: [Errno 121] Remote I/O error

Upon looking at the logs, we identified the issue to be in the setup() function in the library. We commented out lines 988 - 1006 to remove the conflicts. This allowed us to run the HeartRate.py without any errors. However, we were not able to obtain any sensor readings. We printed the values of irValue and sensor.checkForBeat(irValue), both of which returned 0 and false, respectively. We did keep our index finger on the sensor with steady pressure, and yet there were no relevant readings.

At this point, we are planning on isolating the features associated with the pulse sensor and focusing on the rest of our project. Thanks for all the suggestions!

ChristianFrisson commented 3 years ago

Hi @adildsw,

I think you are in the right direction: now that you have commented out settings blocking runtime execution in def setup(self, powerLevel = 0x1F, sampleAverage = 4, ledMode = 3, sampleRate = 400, pulseWidth = 411, adcRange = 4096) in qwiic_max3010x.py, you may want to port the required settings initialization from uint8_t SparkFun_Bio_Sensor_Hub::configBpm(uint8_t mode) from SparkFun_Bio_Sensor_Hub_Library.cpp.

adildsw commented 3 years ago

Hi @ChristianFrisson,

We went ahead without the pulse sensor and modified the features to fit the new situation. Regardless, thank you so much for all the help, this project wouldn't have been possible without all the assistance that you and the other @WHC2021SIC/sic-chairs provided us, with the whole conflicting addresses, missing libraries, and an additional ADC board allowance!

We hope you'll like our work!

ChristianFrisson commented 3 years ago

Hi @adildsw and @WHC2021SIC/team-on-body-gym,

Congrats for having pivoted around the many challenges you have encountered during the process and good luck for the presentation of your project!