The AS7262 is a  6 Channel Spectral Sensor by Pimoroni that can be programmed in Python.  This guide shows you how to setup the sensor on a Raspberry Pi and then how to use python to get the results.

SpectralSensor as7262 RaspberryPi

 A Spectral Sensor is a device that can show the intensity of different wave lengths of colour across the visible and invisible light spectrum. In Astronomy these types of devices can detect what elements are present in stars and planets by looking for low readings in the entire spectral wavelengths. As different elements absorb different wave lengths of light the scientist can look for these low reading and know what elements are present. These devices will be expensive and a bit advanced for hobby electronics so if you want to dabble in spectral scanning Pimoroni offer the AS7262 Spectral Sensor designed for the Raspberry Pi.  It is capable of readings 6 wave lengths of light, so no planet detecting with this device. Though you can detect the intensity of roughly the most vibrant shades of Red, Orange, Yellow, Green, Blue and Violet.   

The as7262 Spectral Sensor

The as7262 board contains the colour sensor and two 4000k leds to illuminate an object with. The colour sensor contains two photo diodes, the first one can detect the colours Yellow, Green, Blue & Violet only. The second one detects colours Red, Orange, Yellow and Green.  Each colour sensor can be accessed separately using mode 0 or mode 1, these are continuous scanning modes. Mode 2 gets continuous readings from both photo diodes and gives results for all 6 channels. Mode 3 also reads all 6 channels but is for single scan on command.

The exposure time can be set with the integration setting. This can be set in a range of 1 to 255 which is then mutiplyed by 0.0028 sec to have a total integration time. That is a range from 0.0028 second to 0.71 second.

Different light sources have different spectral profiles which affect how colours are seen. So it is important to make sure your scans are done in consistent lighting conditions. Even the distance the sensor is from the object will affect the intensity of the results. So it would be a good idea to create a housing for the sensor so you can make sure the environment is the same for each of the scans.

As the sensor board has two 4000k LED's onboard and midday light is above 5500k I wanted to see what the difference was when the LED's and daylight was used to illuminate an A4 sheet of paper.  These graphs show the results from three different light conditions. The sensor was located 5cm above the paper with integration set to 20, gain set to 1. For the first test with the LEDs on, the current was set at 25 otherwise the LED was off.

  • The first one is using the two LED's on the sensor in a dark room to scan a white A4 sheet of paper.
  • The second is the exactly same setup and settings but outside at noon in non-direct sunlight.
  • The third is again at noon but indoors next to a window, about 2 meters from test two.

as7262 spectral sensor LED illuminted

Here you can see the onboard LED's show stronger orange and green wave lengths with low red and violet but the overall intensity was 5. 

as7262 spectral sensor Daylight Noon

Day light has a different spectral range than the led with blue and violet being the stronger wave lengths. The intensity is well above the LED's at nearly 300.

as7262 spectral sensor Daylight Window

The effect of a double glazed window on daylight is to reduce the intensity by two thirds. The spectral scale across the wave lengths is the same.

So from this example you can see that objects illuminated with the built in LED's will give you different results compared to daylight.

Setting up a Pimoroni AS7262 6 Channel Spectral Sensor on the Raspberry Pi:

The AS7262 uses the i2c gpio pins to communicate to the Raspberry Pi. If you haven't already, you will need i2c enabled in the interfaces section of raspi-config or Raspberry Pi Configuration.

In a terminal window enter sudo raspi-config  or on the desktop goto the menu and select Preferences and Raspberry Pi Configuration. Then go to the Interfaces section and enable i2c.

 Raspi Config enable i2c

The AS7262 module has been designed so it can be connected straight to the left side of the GPIO connector at pin positions 1.3.5.7.& 9.

as7262 GPIO pins RaspberryPi

   To check everything is setup use the command: i2cdetect -y 1

This should show 49 like this if it is all working ok. If not recheck your connections.

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

With the Sensor connected, next install the python library with sudo pip3 install as7262

Now the Raspberry Pi is setup and the Spectral Sensor is connected, we can try it out. This python3 script will get readings for all 6 colour channels and display the results as intensity of each wave length. The results are in the order of Red, Orange, Yellow, Green , Blue & Violet.

Example 1: Output the 6 channel values


#!/usr/bin/python3
#This script uses a as7262 6 colour spectral scanner from Pimoroni and displays the resulting values.
#raspberryconnect.com

from as7262 import AS7262

as7262 = AS7262()

as7262.set_gain(1) # 1, 3.7, 16, 64
as7262.set_integration_time(10) #1 to 255 x 2.8ms exposure
#mode 0 - bank 1 only continuous, mode 1 - bank 2 only continuous, mode 2 - both banks continuous, mode 3 - both banks single read 
as7262.set_measurement_mode(2) #2 all colours continuous
as7262.set_illumination_led_current(12.5) #12.5mA 25mA 50mA 100mA
as7262.set_illumination_led(1) # led on

def main():
	try:
		while 1:
			values = as7262.get_calibrated_values() #get values from scan
			spec = [float(i) for i in list(values)] #convert results from string to float
			print(spec)
	except KeyboardInterrupt:
			as7262.set_measurement_mode(3) #Switch to scan on demand
			as7262.set_illumination_led(0) #light off

if __name__ == '__main__':
    main()

 This will turn on the led's and continuously scan and display the results like this. Red, Orange, Yellow, Green , Blue & Violet. (ctrl + c to exit)

Also available at as7262-example1-basicscan.txt


[7.560032844543457, 10.0905179977417, 5.567821502685547, 3.222388505935669, 1.1915769577026367, 1.1165746450424194]
[7.560032844543457, 10.0905179977417, 6.495791435241699, 3.222388505935669, 1.1915769577026367, 1.1165746450424194]
[7.560032844543457, 10.0905179977417, 6.495791435241699, 3.222388505935669, 1.1915769577026367, 1.1165746450424194]
[7.560032844543457, 10.0905179977417, 5.567821502685547, 3.222388505935669, 1.1915769577026367, 1.1165746450424194]

Example 2: Display the results on a graph

The next example shows how to add a graph which represents the results of the scan

for this you will need to install the python library matplotlib and numpy

sudo pip3 install matplotlib numpy


#!/usr/bin/python3
#This script uses a as7262 6 colour spectral scanner from Pimoroni and displays a colour bar graph.
#raspberryconnect.com

from as7262 import AS7262
import matplotlib.pyplot as plt
import numpy as np

as7262 = AS7262()

as7262.set_gain(1) # 1, 3.7, 16, 64
as7262.set_integration_time(10) #1 to 255 x 2.8ms exposure
#mode 0 - bank 1 only continuous, mode 1 - bank 2 only continuous, mode 2 - both banks continuous, mode 3 - both banks single read 
as7262.set_measurement_mode(2) #2 all colours continuous
as7262.set_illumination_led_current(12.5) #12.5mA 25mA 50mA 100mA
as7262.set_illumination_led(1) # led on

def bargraph(self):
    label = ("Red","Ora","Yel","Gre","Blu","Vio")
    y_pos = np.arange(len(label))
    barcol = ('red','orange','yellow','green','blue','violet') #Graph bar colours
    plt.bar(y_pos,self,color=(barcol))
    plt.xticks(y_pos,label)
    plt.show()

try:
    while True:
        values = as7262.get_calibrated_values() #get values from scan
        spec = [float(i) for i in list(values)] #convert results from string to float
        print(spec)
        bargraph(spec)

except KeyboardInterrupt:
    as7262.set_measurement_mode(3) #switch to single scan mode
    as7262.set_illumination_led(0) 'led off


Also available at as7262-example2-scangraphs.txt

Example 3: Convert Spectral Scan values to CIE XYZ colour space

 If you want to represent the results of the scan as a colour then the spectral data needs to be converted to a RGB colour space.  This will only give you a representation of the scan result as a computer screen cannot show the colour shades available in the spectrum, First you need to convert the scan to the CIE XYZ colour space as this has the closes match to the range of human colour vision. 

The conversion used the values; observer='10', illuminant='D65' 

Observer 10 refers to the colour standard. The alternate option is 2 but 10 is the recommended standard. Illuminant D65 refers to the standard illumination of the scene in Noon Daylight. As the as7262 scanner does not sense illumination  D65 is used for colour conversions so will also cause an error in the colour representation.


#!/usr/bin/python3
#This script uses a as7262 6 colour spectral scanner from Pimoroni and displays the 
#Spectral results and XYZ colour RGB colour co-coordinates.
#raspberryconnect.com

from as7262 import AS7262

from colormath.color_objects import SpectralColor,XYZColor
from colormath.color_conversions import convert_color

as7262 = AS7262()

as7262.set_gain(1) # 1, 3.7, 16, 64
as7262.set_integration_time(10) #1 to 255 x 2.8ms exposure
#mode 0 - bank 1 only continuous, mode 1 - bank 2 only continuous, mode 2 - both banks continuous, mode 3 - both banks single read 
as7262.set_measurement_mode(2) #2 all colours continuous
as7262.set_illumination_led_current(12.5) #12.5mA 25mA 50mA 100mA
as7262.set_illumination_led(1) # led on

def spectral_to_xyz(self):
    """Convert Scan to RGB Colour"""
    spc = SpectralColor(
        observer='10', illuminant='D65',
        spec_650nm=str(self[0]), 
        spec_600nm=str(self[1]),
        spec_570nm=str(self[2]),
        spec_550nm=str(self[3]),
        spec_500nm=str(self[4]),
        spec_450nm=str(self[5]))
    xyz = convert_color(spc, XYZColor) #convert spectral signals to XYZ colour space
    return xyz
    
def cleanup():
        as7262.set_measurement_mode(3) #deactivate scanner to scan on command
        as7262.set_illumination_led(0) #switch off led

def main():    
    try:
        a = input("Press a key when ready, enter q to quit")
        while a != "q":
            values = as7262.get_calibrated_values() #get values from scan
            spec = [float(i) for i in list(values)] #convert results from string to float
            xyzcol = spectral_to_xyz(spec) #convert spectral scan to xyz colour space values
            print("Spectral Scan ROYGBV:",spec)
            print("XYZ Color",xyzcol)        
            a = input() #wait for user
        cleanup()

    except KeyboardInterrupt:
        cleanup()

if __name__ == '__main__':
    main()

Also available at as7262-example3-scan2xyz.txt

The results will look like this


Spectral Scan ROYGBV: [5.880025863647461, 7.0633625984191895, 6.495791435241699, 6.444777011871338, 3.57473087310791,1.1165746450424194]
XYZ Color XYZColor (xyz_x:1.6150 xyz_y:1.7803 xyz_z:0.2601)

Example 4: Convert Spectral Scan values to sRGB

Now if you do want to represent the colour on screen you will need to convert it to sRGB before the Red, Green, Blue values can be used.

The problem is RGB is in a range of 0-1 or 0-255. As you can see from the XYZ colour values above, they are higher than 1. This means the sRGB colours need to be scaled down which will add more error in to the results. This example shows you how to convert from the xyz colours to sRGB. There is an option to scale values over 255 to 255. 

 print("RGB:",convert_to_rgb(xyzcol,1))  if you don't want sRGB to be scaled then change 1 to 0 . print("RGB:",convert_to_rgb(xyzcol,0))


#!/usr/bin/python3
#This script uses a as7262 6 colour spectral scanner from Pimoroni and displays the 
#Spectral results and XYZ colour RGB colour co-coordinates and sRGB values.
#raspberryconnect.com

from as7262 import AS7262

from colormath.color_objects import SpectralColor,XYZColor,sRGBColor
from colormath.color_conversions import convert_color

as7262 = AS7262()

as7262.set_gain(1) # 1, 3.7, 16, 64
as7262.set_integration_time(10) #1 to 255 x 2.8ms exposure
#mode 0 - bank 1 only continuous, mode 1 - bank 2 only continuous, mode 2 - both banks continuous, mode 3 - both banks single read 
as7262.set_measurement_mode(2) #2 all colours continuous
as7262.set_illumination_led_current(12.5) #12.5mA 25mA 50mA 100mA
as7262.set_illumination_led(1) # led on

def spectral_to_xyz(self):
    """Convert Scan to RGB Colour"""
    spc = SpectralColor(
        observer='10', illuminant='D65',
        spec_650nm=str(self[0]), 
        spec_600nm=str(self[1]),
        spec_570nm=str(self[2]),
        spec_550nm=str(self[3]),
        spec_500nm=str(self[4]),
        spec_450nm=str(self[5]))
    xyz = convert_color(spc, XYZColor) #convert spectral signals to XYZ colour space
    return xyz
    
def convert_to_rgb(self,clip):
    """Convert colour object to RGB for Screen Colours as r,g,b"""
    #Colour object can be XYZColor, sRGBColor or other colormath color object
    #clip of 1 will restrict the max value to 255, possibly alter the colour.
    rgbcol = convert_color(self, sRGBColor,is_upscaled=False)  #convert to sRGB screen colours
    if clip == 1:
        #Clip RB Values to 255
        rgbcol = sRGBColor(rgbcol.clamped_rgb_r,rgbcol.clamped_rgb_g,rgbcol.clamped_rgb_b) #limit RGB max to 1
    rgbcol = rgbcol.get_upscaled_value_tuple() # convert from 0-1 to 0-255
    return rgbcol[0],rgbcol[1],rgbcol[2] #return r,g,b values 0-255
    
def cleanup():
        as7262.set_measurement_mode(3) #deactivate scanner to scan on command
        as7262.set_illumination_led(0) #switch off led

def main():    
    try:
        a = input("Press a key when ready, enter q to quit")
        while a != "q":
            values = as7262.get_calibrated_values() #get values from scan
            spec = [float(i) for i in list(values)] #convert results from string to float
            xyzcol = spectral_to_xyz(spec) #convert spectral scan to xyz colour space values
            print("Spectral Scan ROYGBV:",spec)
            print("XYZ Color",xyzcol)        
            print("RGB:",convert_to_rgb(xyzcol,1))
            a = input() #wait for user
        cleanup()

    except KeyboardInterrupt:
        cleanup()

if __name__ == '__main__':
    main()

Also available at as7262-example4-scan2rgb.txt

The results look like this


Spectral Scan ROYGBV: [5.880025863647461, 4.03620719909668, 0.9279702305793762, 1.0741294622421265, 1.1915769577026367, 1.1165746450424194]
XYZ Color XYZColor (xyz_x:0.6209 xyz_y:0.4801 xyz_z:0.2491)
RGB: (255, 151, 124)

Use a Spectral Sensor for Identification

Not quite the intended use for a spectral scanner but as the results of a spectral scan of an object are the same under the same lighting conditions, you can use it to identify objects based on the colour signature.

This means you could create a set of cards for different actions. When one is placed in front of the scanner, it could trigger an action. For example you could print out your favorite albumn covers. Then place one in front of the spectral scanner, then with a little bit of programming you could get that album to play. You could also use it as a security feature by only allowing access when the correct image is presented to the spectral scanner. Though this is very sensitive so it needs to be done in some type of housing so the same lighting and picture position is used for the scan to match.

This is an example of a program that can scan an items and log the results in a text file with a reference name. Then when that item is scanned again, in exactly the same conditions as it was recorded, it will identify the object.

 


#!/usr/bin/python3
#This script uses a as7262 6 colour spectral scanner from Pimoroni
#It will log scans in a file and then will match new scans against logged scans
#RaspberryConnect.com

from as7262 import AS7262
import csv

as7262 = AS7262()

as7262.set_gain(1) # 1 3.7 16 64
as7262.set_integration_time(30) #(1 to 255 x 2.8ms)
as7262.set_measurement_mode(2) #all colours continous
as7262.set_illumination_led_current(12.5) #12.5mA 25mA 50mA 100mA
as7262.set_illumination_led(1) # led on

def log(self,value):
    """save Log to current folder"""
    with open('./ScanLog-Items.txt', 'a') as f:
        wr = csv.writer(f)
        self.append(value)
        wr.writerow(self)

def scan():
    multiscan=[]
    for i in range(5): #scan 5 times, use most common results
        values = as7262.get_calibrated_values()
        spec = [str(i) for i in list(values)]
        multiscan.append(spec)
    r = checkscan(multiscan)
    return r

def checkscan(self):
    """Take 5 scan results and use the most common scan values"""
    cnt=0
    for x in range(len(self)):
        for i in range(len(self)):
            if self[x] == self[i]:
                cnt +=1
            if cnt >= 4:
                return self[x]
        cnt=0
    return 0

def comparescan(self):
    """Check log for a match with the scan and return the name"""
    m = "non"
    with open('./ScanLog-Items.txt', 'r') as f:
        r = csv.reader(f)
        for row in r:
            if row[:6] == self:
                m = row[6]
        return m

try:
    mode = ""
    sc = 0
    print("Enter m to match items")
    print("Enter s to scan and log new items")
    print("Enter q to quit")
    a = input()
    if a == "m":
        mode = "m"
        a = input("\nPress enter to start")
    elif a == "s":
        mode = "s"
        a = input("Enter a name for the item to be scanned: ")
    while a != "q":
        #Match Mode
        if mode == "m":
            while sc == 0:
                sc = scan()
            x = comparescan(sc)
            if x != "non":
                print("\nMatch with:  ",x)
            else:
                print("No Match Found")
            sc = 0
            a = input("\nPress Enter to Scan for Match: ")
        #Scan Mode
        elif mode == "s":
            while sc == 0:
               sc = scan()
            log(sc,a)
            print(a," Logged")
            a = ""
            sc = 0
            a = input("\nEnter a name for the item to be scanned: ")
    as7262.set_measurement_mode(3)
    as7262.set_illumination_led(0)

except KeyboardInterrupt:
    as7262.set_measurement_mode(3)
    as7262.set_illumination_led(0)

Also available at as7262-scan_and_match.txt 

To use this script first scan the items you wish to log. Select s from the menu. Enter a name for the item you are scanning. Then enter q when done.

Next run the program again but use m from the menu. Now when you scan an item it will compare the results with what is in the log and displays the name of the matched item if one is found. 

Reference:

https://github.com/pimoroni/as7262-python

https://www.colourphil.co.uk/xyz_colour_space.shtml

https://en.wikipedia.org/wiki/SRGB

https://videocide.com/glossary/color-space/

https://www.waveformlighting.com/color-matching/what-is-d50-for-graphic-arts-printing

https://python-colormath.readthedocs.io/en/latest/illuminants.html

 


Add comment