使用したカラーセンサー
事前調査の結果、カラーセンサーは、常に同一の測定条件でセンシングしないと、良い結果が得られないとわかったので、写真のような、センサーユニットの両端にLEDがあり、測定時には全体を覆ってしまうような構造のものを選定した。
こちらにTCS34725というカラーセンサーが乗っていた。
使用したマイコン
小型に作りたかったので、AtomS3Rを選定した。
制作事例があまり出てこず苦戦した…
とりあえず作ってみたが…
とりあえず、愚直にセンサーの値を取り込み、ディスプレイにその色を写すように、ちゃちゃっと作ってみたのだが、どうにも発色がおかしい…
センサーのSDA,SCLピンは、それぞれAtomS3Rの38, 39番ピンに接続している。
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include "M5AtomS3.h"
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_300MS, TCS34725_GAIN_16X); // 1, 4, 16, 60
static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) {
uint16_t _color;
_color = (uint16_t)(r & 0xF8) << 8;
_color |= (uint16_t)(g & 0xFC) << 3;
_color |= (uint16_t)(b & 0xF8) >> 3;
return _color;
}
void setup() {
// auto cfg = M5.config();
Wire.end();
//I2Cピン設定 I2C0を使用する SDA, SCL
Wire.begin(38, 39);
// AtomS3.begin(cfg);
AtomS3.begin();
Serial.begin(115200);
strip.begin();
if (!status) {
M5.Lcd.println(F("Could not find a valid BMP280 sensor, check wiring or "
"try a different address!"));
while (1) delay(10); // Stop code execution if the sensor is not found.
}
while(!tcs.begin()){
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.setCursor(0, 60);
M5.Lcd.print("Sensor not found");
delay(1000);
}
M5.Lcd.println("Found sensor");
}
void loop() {
// Typing any character into Serial Monitor will kick off a read / output cycle
AtomS3.update();
if (AtomS3.BtnA.wasPressed()) {
// raw values of r,g,b,c as read by TCS3472
uint16_t r, g, b, c;
// Get raw data from the TCS3472
tcs.getRawData(&r, &g, &b, &c);
// Print out values
Serial.print("R: "); Serial.print(r); Serial.print(" ");
Serial.print("G: "); Serial.print(g); Serial.print(" ");
Serial.print("B: "); Serial.print(b); Serial.print(" ");
Serial.println(" ");
uint16_t boxcolor = color16(r, g, b);
M5.Lcd.clear(boxcolor); // ディスプレイを指定した色にセット
}
}
キャリブレーションしてみる
参考になりそうな記事を発見し、RGB各センサーの値を補正してみる。
以下のようなキャリブレーション用のloop()関数を書き込み、真っ黒、つまり、(0,0,0)となるはずのものと、真っ白、つまり、(255,255,255)となるはずのもの、(反射の少ない布などがおすすめ)を実際に測定し、実際の値を計測してみた。
// Calib
String incomingString;
void loop(void) {
uint16_t r, g, b, c, colorTemp, lux;
unsigned int red, green, blue;
if (Serial.available() > 0){
incomingString = Serial.readString();
tcs.getRawData(&r, &g, &b, &c);
colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c);
lux = tcs.calculateLux(r, g, b);
Serial.print("Color Temp: "); Serial.print(colorTemp, DEC); Serial.print(" K - ");
Serial.print("Lux: "); Serial.print(lux, DEC); Serial.print(" - ");
Serial.print("R: "); Serial.print(r, DEC); Serial.print(" ");
Serial.print("G: "); Serial.print(g, DEC); Serial.print(" ");
Serial.print("B: "); Serial.print(b, DEC); Serial.print(" ");
Serial.print("C: "); Serial.print(c, DEC); Serial.print(" ");
Serial.println(" ");
}
}
多少誤差があるが、おおよそ以下のような結果になった
redCal.blackValue = 1500;
redCal.whiteValue = 65535;
greenCal.blackValue = 3000;
greenCal.whiteValue = 65535;
blueCal.blackValue = 3000;
blueCal.whiteValue = 65535;
全体的に黒に対して値が出ており、かつ、赤色のみ、黒検出時の値がちいさい。
これらに値を利用し、値をマッピングしなおした。
また、全体的にそのまま色を使うと暗い色になる印象があったため、色空間をRGBからYUVに変換し、彩度を上げて表示するようにしている。
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include "M5AtomS3.h"
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_300MS, TCS34725_GAIN_16X); // 1, 4, 16, 60
struct colorCalibration {
unsigned int blackValue;
unsigned int whiteValue;
};
colorCalibration redCal, greenCal, blueCal;
static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) {
uint16_t _color;
_color = (uint16_t)(r & 0xF8) << 8;
_color |= (uint16_t)(g & 0xFC) << 3;
_color |= (uint16_t)(b & 0xF8) >> 3;
return _color;
}
// array to represent the gamma function
int gammatable[256];
// Function to map TCS3472 values to 0 to 255
// Same as Arduino map() function - rewritten to make variables compatiable with inputs and outputs
int RGBmap(unsigned int x, unsigned int inlow, unsigned int inhigh, int outlow, int outhigh){
float flx = float(x);
float fla = float(outlow);
float flb = float(outhigh);
float flc = float(inlow);
float fld = float(inhigh);
float res = ((flx-flc)/(fld-flc))*(flb-fla) + fla;
return int(res);
}
void setup() {
// auto cfg = M5.config();
Wire.end();
//I2Cピン設定 I2C0を使用する SDA, SCL
Wire.begin(38, 39);
// AtomS3.begin(cfg);
AtomS3.begin();
Serial.begin(115200);
strip.begin();
if (!status) {
M5.Lcd.println(F("Could not find a valid BMP280 sensor, check wiring or "
"try a different address!"));
while (1) delay(10); // Stop code execution if the sensor is not found.
}
while(!tcs.begin()){
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.setCursor(0, 60);
M5.Lcd.print("Sensor not found");
delay(1000);
}
M5.Lcd.println("Found sensor");
redCal.blackValue = 1500;
redCal.whiteValue = 65535;
greenCal.blackValue = 3000;
greenCal.whiteValue = 65535;
blueCal.blackValue = 3000;
blueCal.whiteValue = 65535;
// Gamma function is Out = In^2.5
// Required to correct for human vision
// Store values in an array
// normalized for 0 to 255
for (int i=0; i<256; i++) {
float x = i;
x /= 255;
x = pow(x, 2.5);
x *= 255;
gammatable[i] = int(x);
}
}
void loop() {
// Typing any character into Serial Monitor will kick off a read / output cycle
AtomS3.update();
if (AtomS3.BtnA.wasPressed()) {
// raw values of r,g,b,c as read by TCS3472
uint16_t r, g, b, c;
// Variables used to hold RGB values between 0 and 255
int redValue;
int greenValue;
int blueValue;
int clearValue;
// Get raw data from the TCS3472
tcs.getRawData(&r, &g, &b, &c);
// Convert TCS3472 raw reading into value between 0 and 255 for analogwrite function
redValue = RGBmap(r, redCal.blackValue, redCal.whiteValue, 0, 255);
greenValue = RGBmap(g, greenCal.blackValue, greenCal.whiteValue, 0, 255);
blueValue = RGBmap(b, blueCal.blackValue, blueCal.whiteValue, 0, 255);
// Print out values
Serial.print("R: "); Serial.print(redValue); Serial.print(" ");
Serial.print("G: "); Serial.print(greenValue); Serial.print(" ");
Serial.print("B: "); Serial.print(blueValue); Serial.print(" ");
Serial.println(" ");
// uint16_t boxcolor = color16((int)redValue, (int)greenValue, (int)blueValue);
uint16_t boxcolor = color16(gammatable[redValue], gammatable[greenValue], gammatable[blueValue]);
M5.Lcd.clear(boxcolor); // ディスプレイを指定した色にセット
}
}
結果
おおよそ納得のいく色がセンシングできた。
ただ、取得した色が全体的に多少暗いイメージがあるので、ガンマ補正に加えて、色空間の変換を行った後の彩度の強調などを行えば、もっときれいになると思われる。
