Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test out AS7341 sensor in liquid color matching #87

Open
Neil-YL opened this issue Oct 23, 2024 · 5 comments
Open

Test out AS7341 sensor in liquid color matching #87

Neil-YL opened this issue Oct 23, 2024 · 5 comments
Assignees

Comments

@Neil-YL
Copy link
Contributor

Neil-YL commented Oct 23, 2024

Currently, we have the charging port and sensor package assembled for the liquid color matching demo. It is necessary to test the performance of the AS7341 color sensor for detecting liquid dye in a well plate within the packaging.

I will start by using the code from the micro-course LED color sensor demo for testing.

@Neil-YL Neil-YL self-assigned this Oct 23, 2024
@Neil-YL
Copy link
Contributor Author

Neil-YL commented Oct 23, 2024

Schematic of Picowbell:
https://learn.adafruit.com/adafruit-proto-under-plate-picowbell/downloads

Raspberry Pi Pico W Pinout:
image

Update: Following setting is wrong, check #87 (comment) for correct setting
SCL - GPIO5 - Pin(7) -Pin(5)
SDA - GPIO4 - Pin(6)-Pin(4)

By charging the i2c to scl=Pin(7),sda=Pin(6), it should be able to set up the connection of AS7341.

I used a script to see if I can obtain data from the sensor (along with the as7341.py and as7341_sensor.py from the micro-course demo)

from machine import I2C, Pin
from as7341_sensor import Sensor  

def test_as7341():
    try:
        
        i2c = I2C(1, scl=Pin(7), sda=Pin(6))  # Use GPIO 5 for SCL and GPIO 4 for SDA
        
        # Pass the i2c object to the Sensor class
        sensor = Sensor(atime=100, astep=999, gain=128, i2c=i2c)

        # Retrieve and print all channel data
        channel_data = sensor.all_channels
        print("Spectral Data (F1 to F8):", channel_data)

        # Retrieve and print all channels along with CLR and NIR data
        channel_clr_nir_data = sensor.all_channels_clr_nir
        print("Spectral Data (F1 to F8, CLR, NIR):", channel_clr_nir_data)

        # Optionally disable the sensor at the end of the test
        sensor.disable()
        print("Sensor disabled.")

    except Exception as e:
        print(f"An error occurred: {e}")

# Run the test
test_as7341()

However it returned:

Detected devices at I2C-addresses:
I2C read_byte at 0xA9, error [Errno 5] EIO
I2C write_byte at 0xA9, error [Errno 5] EIO
I2C write_byte at 0x70, error [Errno 5] EIO
I2C read_byte at 0xA9, error [Errno 5] EIO
I2C write_byte at 0xA9, error [Errno 5] EIO
I2C write_byte at 0x80, error [Errno 5] EIO
I2C write_byte at 0x80, error [Errno 5] EIO
I2C read_byte at 0x92, error [Errno 5] EIO
Failed to contact AS7341 at I2C address 0x39
An error occurred: Failed to contact AS7341, terminating

It seems the controller is not able to communicate with the AS7341 sensor over the I2C setting.

I run the same test code with my micro-course demo(changing the pin back to 27 and 26) it did set up the connection and obtained data from the sensor.

@sgbaird
Copy link
Member

sgbaird commented Oct 23, 2024

Are you using the stemma qt port on the picowbell?

EDIT: Sorry, realizing answer is yes from context

Let's try interchanging some components as a first debugging step.

@Neil-YL
Copy link
Contributor Author

Neil-YL commented Oct 23, 2024

i2c = I2C(0, scl=Pin(5), sda=Pin(4)) # Use GPIO 5 for SCL and GPIO 4 for SDA

This is the correct i2c to use the stemma qt port on the Picowbell:

MPY: soft reboot
Detected devices at I2C-addresses: 0x39
Spectral Data (F1 to F8): [238, 655, 943, 1148, 1953, 2038, 2559, 1464]
Spectral Data (F1 to F8, CLR, NIR): [238, 654, 941, 1145, 1962, 2049, 2571, 1474, 4143, 371]
Sensor disabled.

@Neil-YL
Copy link
Contributor Author

Neil-YL commented Nov 29, 2024

I have modified the wrapped class for AS7341 (AS7341_sensor.py) to add a control for the LED:

    @property
    def LED(self):
        """Get the current state of the onboard LED."""
        return self._led_state

    @LED.setter
    def LED(self, state):
        """Set the LED state."""
        if state:
            self.sensor.set_led_current(10)  # Use set_led_current method in AS7341.py
            self._led_state = True
        else:
            self.sensor.set_led_current(0)
            self._led_state = False

Video:

IMG_0871.mov

It seems that with the LED on, the sensor readings for all channels reach very high values (possibly due to reflection from the enclosure walls). I will have further tests to see if using the LED is necessary.

Blue:

LED is ON.
Sensor Data:
ch583: 56221
ch670: 35873
ch510: 48811
ch410: 5784
ch620: 58872
ch470: 45850
ch550: 58562
ch440: 31257

Red:

LED is ON.
Sensor Data:
ch583: 57048
ch670: 36363
ch510: 49084
ch410: 5825
ch620: 59859
ch470: 45797
ch550: 59112
ch440: 31257

Yellow:

LED is ON.
Sensor Data:
ch583: 57304
ch670: 36397
ch510: 49150
ch410: 5835
ch620: 60002
ch470: 45865
ch550: 59342
ch440: 31278

@Neil-YL
Copy link
Contributor Author

Neil-YL commented Dec 5, 2024

Updates for this week:

I have a short script to test the sensor: have one measurement with command "read" and "yes or no" to turn on or off LED light on the sensor during the measurement.

Click here for sensor test script
import time
from machine import I2C, Pin
from as7341_sensor import Sensor

# Initialize the AS7341 sensor
sensor = Sensor(i2c=I2C(0, scl=Pin(5), sda=Pin(4)))

def read_sensor_data():
    """Read dictionary of sensor data"""
    # Get all channel data from the sensor
    channel_data = sensor.all_channels

    CHANNEL_NAMES = [
        "ch410",
        "ch440",
        "ch470",
        "ch510",
        "ch550",
        "ch583",
        "ch620",
        "ch670",
    ]

    # Return a dictionary that maps channel names to sensor data
    return dict(zip(CHANNEL_NAMES, channel_data))


print("Testing sensor...")
sensor_data = read_sensor_data()
print(sensor_data)


def test_sensor():
    """Interactive testing interface for the AS7341 sensor."""
    print("AS7341 Sensor Test")
    print("==================")
    print("Type 'read' to sample data or 'exit' to quit.")

    while True:
        # Wait for a command from the user
        command = input("Command: ").strip().lower()

        if command == "read":
            # Ask whether to turn on the LED
            led_choice = input("Turn on LED for this reading? (yes/no): ").strip().lower()
            if led_choice == "yes":
                sensor.LED = True  # Turn on LED
                print("LED is ON.")

            # Trigger sensor data reading
            sensor_data = read_sensor_data()
            if sensor_data:
                print("Sensor Data:")
                for wavelength, value in sensor_data.items():
                    #print(f"{wavelength}: {value}")
                    print(f"{value}")
            else:
                print("Failed to read sensor data.")

            # Turn off the LED if it was turned on
            if led_choice == "yes":
                sensor.LED = False  # Turn off LED
                print("LED is OFF.")

        elif command == "exit":
            print("Exiting the program.")
            break
        else:
            print("Invalid command. Type 'read' to sample or 'exit' to quit.")

# Directly call the test_sensor function
test_sensor()

I prepared some colors mixture for several trial tests, each well slot has a volume of 280μL.

image

A1: 70% Red, 10% Yellow, 20% Blue
A2: 20% Red, 10% Yellow, 70% Blue
A3: 35% Red, 5% Yellow, 10% Blue, 50% water (50% dilution of A1)
A4: 10% Red, 5% Yellow, 35% Blue, 50% water (50% dilution of A2)
A5: 100% Yellow
A6: 100% Blue
A7: 100% Red

Followings are findings after several measurements:

  • LED on, readings at a range of 10k-20k, except for wavelength 410, which is around 2k.
  • LED off, readings at a range of 100-400, expect for wavelength 410 around 20 and 440 around 40-60.
  • LED on or off, the readings are very stable and repeatable for each color (Errors: With LED off, most of channels are 0, ±2 for maximum channel, and ±10 with LED on).
  • LED off, A1 and A2 are distinguishable with 5% difference, and A3 and A4 are not very distinguishable (most of channels less than 1% difference, less than repeat measurement errors )
  • LED on, A1 A2 A3 A4 are not distinguishable with difference less than repeat measurement errors
  • LED on, YBR are not very distinguishable

You may refer to this excel file for detail results.

I think we will not use the LED for the measurement, so may not need the LED control for the test code (since we need to update the sensor wrapped class as well).And surprisingly, dilution makes the colors less distinguishable, which is the opposite of what we observe with eyes.

I also tried to convert the sensor readings into RBG value. I did find a Python API for this task but all the measurement were converted into a deep or light orange/yellow color. I decided to skip this part in current stage, since we only need to compare with the target color measured by the same sensor.

For Prefect on OT-2, I think we do not need to install Prefect or modify any codes on OT-2. Just simply include SSH tasks in the Prefect workflow to connect to the OT-2 and then to execute Opentrons protocol code on the robot.

I also found some variables in Jackie’s demo code related to Neopixels, which are not necessary for liquid matching. While some of them might be essential variables, but with the Neopixels related naming from the previous code. Do you think I should put in the effort to modify them, or just leave them as they are? I think if we stick with the same naming, we might not need to modify the code for MQTT from the ‘Hello World’ course too much as well.

Since my Prefect courses are to held in Dec 17-18 so I think I will finish the documentation for the rest of the software (sensor, PicoW MQTT, OT-2 setup reference, etc) before that days and try to finish the Prefect part after the courses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants