obniz
Version:
obniz sdk for javascript
878 lines (820 loc) • 20.5 kB
JavaScript
class ArduCAMMini {
constructor() {
this.keys = [
'cs',
'mosi',
'miso',
'sclk',
'gnd',
'vcc',
'sda',
'scl',
'spi',
'i2c',
'spi_frequency',
'spi_drive',
];
this.requiredKeys = ['cs'];
this.ioKeys = this.keys;
this.displayName = 'Cam';
this.regs = {
ARDUCHIP_TEST1: 0x00,
ARDUCHIP_MODE: 0x02,
ARDUCHIP_FIFO: 0x04,
BURST_FIFO_READ: 0x3c,
ARDUCHIP_TRIG: 0x41,
FIFO_SIZE1: 0x42,
FIFO_SIZE2: 0x43,
FIFO_SIZE3: 0x44,
};
this.configs = {
OV2640_JPEG_INIT: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[], //[ 0xd3, 0x7f ],
[],
[],
[],
[],
[],
[],
[],
[],
//
[],
[], //[ 0xd3, 0x7f ],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_YUV422: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_JPEG: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_160x120_JPEG: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_176x144_JPEG: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_320x240_JPEG: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_352x288_JPEG: [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_640x480_JPEG: [
[],
[],
[], // Bit[6:4]: Resolution selection//
[], // HREFST[10:3]
[], // HREFEND[10:3]
[], // Bit[5:3]: HREFEND[2:0]; Bit[2:0]: HREFST[2:0]
[], // VSTRT[9:2]
[], // VEND[9:2]
[], // Bit[3:2]: VEND[1:0]; Bit[1:0]: VSTRT[1:0]
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_800x600_JPEG: [
[],
[],
[], // Bit[6:4]: Resolution selection
[], // HREFST[10:3]
[], // HREFEND[10:3]
[], // Bit[5:3]: HREFEND[2:0]; Bit[2:0]: HREFST[2:0]
[], // VSTRT[9:2]
[], // VEND[9:2]
[], // Bit[3:2]: VEND[1:0]; Bit[1:0]: VSTRT[1:0]
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_1024x768_JPEG: [
[],
[],
[], // Bit[6:4]: Resolution selection//0x02
[], // HREFST[10:3]
[], // HREFEND[10:3]
[], // Bit[5:3]: HREFEND[2:0]; Bit[2:0]: HREFST[2:0]
[], // VSTRT[9:2]
[], // VEND[9:2]
[], // Bit[3:2]: VEND[1:0]; Bit[1:0]: VSTRT[1:0]
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_1280x960_JPEG: [
[],
[],
[], // Bit[6:4]: Resolution selection//0x02
[], // HREFST[10:3]
[], // HREFEND[10:3]
[], // Bit[5:3]: HREFEND[2:0]; Bit[2:0]: HREFST[2:0]
[], // VSTRT[9:2]
[], // VEND[9:2]
[], // Bit[3:2]: VEND[1:0]; Bit[1:0]: VSTRT[1:0]
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
],
OV2640_1600x1200_JPEG: [
[],
[],
[], // Bit[6:4]: Resolution selection//0x02
[], // HREFST[10:3]
[], // HREFEND[10:3]
[], // Bit[5:3]: HREFEND[2:0]; Bit[2:0]: HREFST[2:0]
[], // VSTRT[9:2]
[], // VEND[9:2]
[], // Bit[3:2]: VEND[1:0]; Bit[1:0]: VSTRT[1:0]
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[], //bit2->1;bit[1:0]->1
[],
[],
[],
],
};
}
static info() {
return {
name: 'ArduCAMMini',
};
}
wired(obniz) {
this.obniz.setVccGnd(this.params.vcc, this.params.gnd, '5v');
this.io_cs = obniz.getIO(this.params.cs);
this.io_cs.output(true);
obniz.wait(100);
this.sensor_addr = 0x30; // i2c
this.params.mode = this.params.mode || 'master';
this.params.drive = this.params.spi_drive || '3v';
this.params.frequency = this.params.spi_frequency || 4 * 1000 * 1000;
this.params.clk = this.params.sclk;
this.spi = this.obniz.getSpiWithConfig(this.params);
this.params.sda = this.params.sda;
this.params.scl = this.params.scl;
this.params.clock = this.params.clock || 100 * 1000;
this.params.mode = 'master';
this.params.pull = '5v';
this.i2c = obniz.getI2CWithConfig(this.params);
}
spi_write(addr, byteData) {
let data = [];
data.push(addr);
data.push(byteData);
this.io_cs.output(false);
this.spi.write(data);
this.io_cs.output(true);
}
async spi_readWait(addr) {
let data = [];
data.push(addr);
data.push(0x00);
this.io_cs.output(false);
const recv = await this.spi.writeWait(data);
this.io_cs.output(true);
return recv[1];
}
i2c_byte_write(addr, byteData) {
this.i2c.write(this.sensor_addr, [addr, byteData]);
}
i2c_regs_write(regs) {
for (let i = 0; i < regs.length; i++) {
this.i2c.write(this.sensor_addr, regs[i]);
}
}
spi_write_reg(addr, byteData) {
this.spi_write(addr | 0x80, byteData);
}
async spi_read_regWait(addr) {
return await this.spi_readWait(addr & 0x7f);
}
async spi_pingpongWait() {
const testVal = 0x55;
this.spi_write_reg(this.regs.ARDUCHIP_TEST1, testVal);
const val = await this.spi_read_regWait(this.regs.ARDUCHIP_TEST1);
if (val !== testVal) {
throw new Error('spi bus fail');
}
}
setMode(mode) {
const modes = {
MCU2LCD: 0x00,
CAM2LCD: 0x01,
LCD2MCU: 0x02,
};
if (typeof modes[mode] !== 'number') {
throw new Error('unknown mode. options are ' + modes);
}
this.spi_write_reg(this.regs.ARDUCHIP_MODE, modes[mode]);
}
async getChipIdWait() {
this.i2c.write(this.sensor_addr, [0x0a]);
const val0 = await this.i2c.readWait(this.sensor_addr, 1);
this.i2c.write(this.sensor_addr, [0x0b]);
const val1 = await this.i2c.readWait(this.sensor_addr, 1);
return (val0[0] << 8) + val1[0];
}
init() {
this.i2c_byte_write(0xff, 0x01);
this.i2c_byte_write(0x12, 0x80);
this.obniz.wait(100);
this.i2c_regs_write(this.configs.OV2640_JPEG_INIT);
this.i2c_regs_write(this.configs.OV2640_YUV422);
this.i2c_regs_write(this.configs.OV2640_JPEG);
this.i2c_byte_write(0xff, 0x01);
this.i2c_byte_write(0x15, 0x00);
this.setSize('320x240');
}
async startupWait() {
await this.spi_pingpongWait();
this.setMode('MCU2LCD');
const chipid = await this.getChipIdWait();
if (chipid != 0x2642) {
throw new Error('unknown chip ' + chipid);
}
this.init();
}
async takeWait(size) {
if (typeof size === 'string' && this._size !== size) {
this.setSize(size);
this.obniz.wait(1000);
}
this.flushFIFO();
this.flushFIFO();
this.startCapture();
while (true) {
if (await this.isCaptureDoneWait()) {
break;
}
}
return await this.readFIFOWait();
}
setSize(string) {
if (this._size === string) {
return;
}
const map = {
'160x120': this.configs.OV2640_160x120_JPEG,
'176x144': this.configs.OV2640_176x144_JPEG,
'320x240': this.configs.OV2640_320x240_JPEG,
'352x288': this.configs.OV2640_352x288_JPEG,
'640x480': this.configs.OV2640_640x480_JPEG,
'800x600': this.configs.OV2640_800x600_JPEG,
'1024x768': this.configs.OV2640_1024x768_JPEG,
'1280x960': this.configs.OV2640_1280x960_JPEG,
'1600x1200': this.configs.OV2640_1600x1200_JPEG,
};
if (map[string]) {
this._size = string;
this.i2c_regs_write(map[string]);
} else {
throw new Error('unsupported size options are ' + Object.keys(map));
}
}
updateFIFO(data) {
// FIFO_CLEAR_MASK 0x01
// FIFO_START_MASK 0x02
// FIFO_RDPTR_RST_MASK 0x10
// FIFO_WRPTR_RST_MASK 0x20
this.spi_write_reg(this.regs.ARDUCHIP_FIFO, data);
}
flushFIFO() {
this.spi_write_reg(this.regs.ARDUCHIP_FIFO, 0x01);
}
async readFIFOLengthWait() {
const len1 = await this.spi_read_regWait(this.regs.FIFO_SIZE1);
const len2 = await this.spi_read_regWait(this.regs.FIFO_SIZE2);
const len3 = (await this.spi_read_regWait(this.regs.FIFO_SIZE3)) & 0x07;
return ((len3 << 16) | (len2 << 8) | len1) & 0x07ffff;
}
startCapture() {
this.spi_write_reg(this.regs.ARDUCHIP_FIFO, 0x02);
}
async isCaptureDoneWait() {
const CAP_DONE_MASK = 0x08;
const val = await this.spi_read_regWait(this.regs.ARDUCHIP_TRIG);
return val & CAP_DONE_MASK ? true : false;
}
async readFIFOWait() {
// get length of image data
let length = await this.readFIFOLengthWait();
// start bust
this.io_cs.output(false);
this.spi.write([this.regs.BURST_FIFO_READ]);
this.spi.write([0xff]); // dummy read
let buf = [];
while (buf.length < length) {
let mustRead = length - buf.length;
if (mustRead > 1024) {
mustRead = 1024;
}
let arr = new Array(mustRead);
arr.fill(0);
const sliced = await this.spi.writeWait(arr);
buf.push(...sliced);
}
// end burst
this.io_cs.output(true);
return buf;
}
arrayToBase64(array) {
return Buffer.from(array).toString('base64');
}
}
if (typeof module === 'object') {
module.exports = ArduCAMMini;
}