The AMG8833 is a small 8x8 grid of thermopile sensors. The grid gives back values on the IR radiation readings for each point on the grid. Using this I’ve created a primitive thermal camera.
For this project I used the LEDMatrix Block and the Breadboard Block.
Configuring the Breadboard Block
To configure the Breadboard Block to connect to the AMG8833 sensor the pins are set up as below:
IO3 -GND | IO2 - Vcc | IO1 -I2cSDA | IO0 - I2cSCL |
Now that the pins are configured we can connect the sensor to the corresponding pins on the Breadboard Block.
Breadboard | AMG8833 Sensor |
IO3 GND | GND |
IO2 Vcc | 3.3V |
IO1 I2cSDA | SDA |
IO0 I2cSCL | SCL |
onReload + setting up constants
First the constants were set up for the IR array sensor. The onReload function was used to call the functions that would initialise the AMG8833 sensor, read the data and check the received data before updating the LED matrix.
const AMG8833_PCTL = 0x00;
const AMG8833_RST = 0x01;
const AMG8833_FPSC = 0x02;
const AMG8833_INTC = 0x03;
const AMG8833_PIXEL_OFFSET = 0x80;
const AMG88xx_PIXEL_TEMP_CONVERSION = .25;
/**
* Called every time you save your script (press the 'Apply' button)
*/
async function onReload(){
const BBblock = await Kitsi.waitForBlock(BlockTypes.Breadboard);
const LEDblock = await Kitsi.waitForBlock(BlockTypes.LEDMatrix);
await AMG8833_init(BBblock);
var i = 0;
while (i <100){
try {
var tempDataArr = await AMG8833_pixelRead(BBblock);
if (tempDataArr[32]==null|tempDataArr[0]==null){
//Data incorrectly read and returns null
}else {
//Data correctly read -> Display LEDs
await LEDupdate(LEDblock, tempDataArr);
}
}catch(e){
}
i++;
}
}
AMG8833_init
The IR sensor was initialised up as per the datasheet.
//Initialises the AMG8833 sensor
async function AMG8833_init(BBblock){
var pctl_normalMode = 0x00;
var mode_reset = 0x3F;
var FPS_10 = 0x00;
var initStruct = [0x00,0x00];
//ENTER NORMAL MODE
initStruct[0] = AMG8833_PCTL; //0x00
initStruct[1] = pctl_normalMode;// 0x00 reset
try{
await Kitsi.APICall('i2c_write', [0x69<<1, initStruct, 1] , BBblock);
}catch(e){
console.log("Error executing command (init1): %s", JSON.stringify(e));
}
//software reset
initStruct[0] = AMG8833_RST; //0x01
initStruct[1] = mode_reset;//0x3F reset
try{
await Kitsi.APICall('i2c_write', [0x69<<1, initStruct, 1], BBblock);
}catch(e){
console.log("Error executing command (init2): %s", JSON.stringify(e));
}
//disable interrupts
initStruct[0] = AMG8833_INTC; //0x03
initStruct[1] = 0x00;//reset
try{
await Kitsi.APICall('i2c_write',[ 0x69<<1, initStruct, 0], BBblock);
}catch(e){
console.log("Error executing command (init3): %s", JSON.stringify(e));
}
AMG8833_pixelRead
The AMG8833 has an upper and lower byte for each coordinate in the 8*8 array. The format of the two bytes are shown below from the datasheet.
Each of these sets of array values can be read from the sensor by sending the address of the point and then requesting to read from the I2C address. Once given an initial address to start the master can continue to request data which will pull the information out of the following address.
The first address of the IR matrix is 0x80, so that value is sent to initialise the position that we are trying to read from. From there we can continue to read in order of the addresses. The only issue is that the Hardware API i2c_request only allows for 127 bytes to be read at one time. Since there are 64 points in the 8*8 IR matrix and each has a high and low value it equates to 128 bytes needing to be read. Due to this limitation the readings were split into two loops.
On the first pass we read the first half of the data using the address 0x80 as the starting address and on the second pass we use the address 0xC2 since it is the starting address of the second half of the array values.
Before the temperature data can be saved to an array it must first be converted to the correct form. Firstly the data is separated over two bytes. So the upper byte is shifted and combined with the lower byte. Since the number is in two’s complement form it is fed into the sign() function which will return the value as a positive or negative integer. After this point, the conversion factor is applied to convert the sensor reading to a temperature.
Once the reading has been converted to the correct format it is saved into an array. This loops over the whole 128 values and the array is returned by the function.
//reads values from IR sensor
async function AMG8833_pixelRead(BBblock){
var initStruct = [0];
var shift = 0;
var row = 0;
var max=0;
var tempDataArr = new Array(64);
var addr = [0x80, 0xC2];
for(var j=0; j<2; j++){
initStruct[0] = addr[j];
try{
await Kitsi.APICall('i2c_write', [0x69<<1, initStruct, 1], BBblock);
}catch(e){
console.log("Error executing command (read1): %s", JSON.stringify(e));
}
for (var k=0; k<1; k++){
try{
//receive 8 readings (16 upper and lower readings)
var TempVals = await Kitsi.APICall('i2c_request', [0x69<<1, 64], BBblock);
if(TempVals.data){
const arr = new Uint8Array(TempVals.data);
//console.log("values%j", JSON.stringify(arr));
//console.log(TempVals.data);
for(var i=0; i<32; i++){
var pos = i << 1;
//format value
var recast = (arr[pos + 1] << 8) | (arr[pos]);
tempDataArr[i+(j*32)] = await sign(recast)*AMG88xx_PIXEL_TEMP_CONVERSION;
}
}
}catch(e){
console.log("Error executing command (read2): %s", JSON.stringify(e));
}
}
}
console.log(tempDataArr);
return tempDataArr;
}
Sign
Converts the two's complement 16 bit value returned from the IR array into a positive or negative value.
//Converts twos complement 16bit value to signed magnitude value
async function sign(value){
var abs = value & 0x7FF;
return (value & 0x800) ? abs- 0x77F - 1 : abs ;
}
LEDupdate
This function sorts through the temperature array given from the IR sensor and adds a LED to an array when the temperature is above a threshold value. The corresponding LEDs are then turned on to represent the hot spots.
//Checks temp values and threshold
async function LEDupdate(LEDblock, tempDataArr){
var LEDon = [];
for (var x=0; x<8; x++){
for (var y=0; y<8; y++){
if (tempDataArr[x*8+y] > 26){
LEDon.push(x*8+y);
}
}
}
try{
await Kitsi.APICall('setLights', [LEDon], LEDblock);
}catch(e){
}
}
Improvements
The LED matrix only allows for two states, either ON or OFF, which limits the representation of the sensor readings. Using an RGB LED matrix the temperature readings could be mapped to different colours, which could show hot and cold spots.