dev: add interface for opt4048 lux/colour sensor

This commit is contained in:
A.M. Rowsell 2026-07-04 15:41:48 -04:00
commit 93788beab4
Signed by: amr
GPG key ID: E0879EDBDB0CA7B1
3 changed files with 115 additions and 0 deletions

View file

@ -30,6 +30,7 @@ add_executable(ensaht
src/bmp.c src/bmp.c
src/aht.c src/aht.c
src/ens.c src/ens.c
src/opt4048.c
) )
# Add the standard include files to the build # Add the standard include files to the build

23
include/opt4048.h Normal file
View file

@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 A.M. Rowsell <amr@frzn.dev>
#ifndef OPT4048_H
#define OPT4048_H
#include "hardware/i2c.h"
#define OPT_ADDR 0x44
/* Structure to hold the calculated color and lux data */
typedef struct {
float x;
float y;
float z;
float lux;
} OPT4048_Data;
OPT4048_Data calculate_color_data(float ch0, float ch1, float ch2);
int initOPT(i2c_inst_t *i2c);
int getOPTData(i2c_inst_t *i2c, float *ch0, float *ch1, float *ch2);
#endif // OPT4048_H

91
src/opt4048.c Normal file
View file

@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 A.M. Rowsell <amr@frzn.dev>
#include "include/opt4048.h"
OPT4048_Data calculate_color_data(float ch0, float ch1, float ch2) {
OPT4048_Data result;
const float m0x = 2.34892992e-04f;
const float m1x = 4.07467441e-05f;
const float m2x = 9.28619404e-05f;
const float m0y = -1.89652390e-05f;
const float m1y = 1.98958202e-04f;
const float m2y = -1.69739553e-05f;
const float m0z = 1.20811684e-05f;
const float m1z = -1.58848115e-05f;
const float m2z = 6.74021520e-04f;
const float m1l = 2.15e-3f;
result.x = (ch0 * m0x) + (ch1 * m1x) + (ch2 * m2x);
result.y = (ch0 * m0y) + (ch1 * m1y) + (ch2 * m2y);
result.z = (ch0 * m0z) + (ch1 * m1z) + (ch2 * m2z);
result.lux = ch1 * m1l;
return result;
}
int initOPT(i2c_inst_t *i2c) {
int result = 0;
int bytesWritten = 0;
// all registers are 16 bits!
// Need to init regs as follows:
// write setup to CONFIG (0x0A/0x0B)
// this sets it to 100ms sample time, auto-range lux
static const uint8_t config[5] = {0x0A, 0b00110010, 0b00001000, 0b10000000,
0b00011101};
// we'll be doing single-shot measurements
bytesWritten = i2c_write_blocking(i2c, OPT_ADDR, config, 4, false);
result = (bytesWritten == 4) ? 0 : 1;
return result;
}
int getOPTData(i2c_inst_t *i2c, float *ch0, float *ch1, float *ch2) {
int result = 0;
int bytesWritten = 0, bytesRead = 0;
uint8_t lightData[16] = {0};
uint32_t mantissaCH0 = 0;
uint32_t mantissaCH1 = 0;
uint32_t mantissaCH2 = 0;
uint8_t exponentCH0 = 0;
uint8_t exponentCH1 = 0;
uint8_t exponentCH2 = 0;
static const uint8_t triggerMeas[2] = {0x0B, 0b00101000};
static const uint8_t dataReg = 0x00;
// force a one-shot measurement
bytesWritten = i2c_write_blocking(i2c, OPT_ADDR, triggerMeas, 2, false);
if (bytesWritten != 2) {
result = 1;
}
sleep_ms(410); // 4 channels @ 100ms each plus a little buffer time
bytesWritten = i2c_write_blocking(i2c, OPT_ADDR, &dataReg, 1, true);
if (bytesWritten != 1) {
result = 1;
}
bytesRead = i2c_read_blocking(i2c, OPT_ADDR, lightData, 16, false);
if (bytesRead != 16) {
result = 1;
}
// now take all the measurement data and convert them to the proper
// representation
mantissaCH0 = ((uint32_t)(lightData[0] & 0x0FUL) << 16) |
((uint32_t)lightData[1] << 8) | (uint32_t)lightData[2];
mantissaCH1 = ((uint32_t)(lightData[4] & 0x0FUL) << 16) |
((uint32_t)lightData[5] << 8) | (uint32_t)lightData[6];
mantissaCH2 = ((uint32_t)(lightData[8] & 0x0FUL) << 16) |
((uint32_t)lightData[9] << 8) | (uint32_t)lightData[10];
exponentCH0 = (lightData[0] & 0xF0) >> 4;
exponentCH1 = (lightData[4] & 0xF0) >> 4;
exponentCH2 = (lightData[8] & 0xF0) >> 4;
*ch0 = (float)(mantissaCH0 << exponentCH0);
*ch1 = (float)(mantissaCH1 << exponentCH1);
*ch2 = (float)(mantissaCH2 << exponentCH2);
return result;
}