Added development sketches for Mics-6815 sensor
This commit is contained in:
parent
111d1c1d6e
commit
2be55da574
3 changed files with 295 additions and 2 deletions
249
ATmega-MiCS6814-I2C/ATmega-MiCS6814-I2C.ino
Normal file
249
ATmega-MiCS6814-I2C/ATmega-MiCS6814-I2C.ino
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* ATmega controller for MiCS-6814 gas sensor - I2C Adapter
|
||||
*
|
||||
* Adapted from Firmware for Groove Multichannel Gas Sensor
|
||||
* Using ATmega core from:
|
||||
* https://raw.githubusercontent.com/carlosefr/atmega/master/package_carlosefr_atmega_index.json
|
||||
*
|
||||
* Pin mapping:
|
||||
* Heater Enable (out): PB0 / Digital 8 (Inverted logic)
|
||||
* NH3 resistance (in): PC0 / Analog 0
|
||||
* CO resistance (in): PC1 / Analog 1
|
||||
* NO2 resistance (in): PC2 / Analog 2
|
||||
* SDA: PC4
|
||||
* SCL: PC5
|
||||
*/
|
||||
|
||||
#include <Wire.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
// Pin Constants
|
||||
#define HEATER_EN 8
|
||||
#define NH3_IN A0
|
||||
#define CO_IN A1
|
||||
#define NO2_IN A2
|
||||
|
||||
// I2C Communication (Taken from Groove sensor, hoping to use the same library on client side)
|
||||
#define DEFAULT_I2C_ADDR 0x04
|
||||
#define CMD_ADC_NH3 1 // NH3 channel
|
||||
#define CMD_ADC_CO 2 // CO channel
|
||||
#define CMD_ADC_NO2 3 // NO2 channel
|
||||
#define CMD_ADC_ALL 4 // All channels
|
||||
#define CMD_CHANGE_I2C 5 // I2C address change
|
||||
#define CMD_READ_EEPROM 6 // Read stored data from EEPROM as unsigned int
|
||||
#define CMD_SET_R0_ADC 7 // Set R0 ADC value
|
||||
#define CMD_GET_R0_ADC 8 // Get R0 ADC value
|
||||
#define CMD_GET_R0_ADC_DEFAULT 9 // Get factory R0 ADC value
|
||||
#define CMD_CONTROL_LED 10 // Control LED (no LED on my board, but Groove supports is, so ... meh!)
|
||||
#define CMD_CONTROL_PWR 11 // Heater control
|
||||
|
||||
// EEPROM Addresses (Match Groove addresses)
|
||||
#define EEPROM_INIT_DONE 0
|
||||
#define EEPROM_DEFAULT_ADC_NH3 2
|
||||
#define EEPROM_DEFAULT_ADC_CO 4
|
||||
#define EEPROM_DEFAULT_ADC_NO2 6
|
||||
#define EEPROM_ADC_NH3 8
|
||||
#define EEPROM_ADC_CO 10
|
||||
#define EEPROM_ADC_NO2 12
|
||||
#define EEPROM_I2C_ADDR 20
|
||||
|
||||
|
||||
// Runtime storage for data
|
||||
uint32_t timer; // Timer value of last update
|
||||
uint8_t adcData[6]; // Storage for the current resistance values
|
||||
uint8_t i2CCmd; // Current command passed from onReceive to onRequest
|
||||
uint8_t i2CData; // Command parameters
|
||||
uint8_t i2CResp[6]; // Buffer for I2C response
|
||||
|
||||
/**
|
||||
* EEPROM helper function to write a single uint16_t value
|
||||
* to two adjacient eeprom cells.
|
||||
* The upper half (MSB) will be written to the given address,
|
||||
* the lower half (LSB) will be written to the next address.
|
||||
*
|
||||
* @param addr
|
||||
* The eeprom addr to store the MSB in.
|
||||
* The next eeprom address will be used for the LSB.
|
||||
* @param value
|
||||
* The uint16_t value to store.
|
||||
*/
|
||||
void eeprom_write16(uint16_t addr, uint16_t value) {
|
||||
// Store MSB in address, shift value and mask
|
||||
EEPROM.write(addr, (value >> 8) & 0xFF);
|
||||
// Store LSB in next address
|
||||
EEPROM.write(addr + 1, value & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* EEPROM helper function to read a single uint16_t value
|
||||
* from two adjacient eeprom cells.
|
||||
*
|
||||
* @param addr
|
||||
* The eeprom addr to read the MSB from.
|
||||
* The the LSB will be read from the next eeprom address.
|
||||
* @return The stored uint16_t value.
|
||||
*/
|
||||
uint16_t eeprom_read16(uint16_t addr) {
|
||||
// Read two bytes separately
|
||||
uint8_t msb = EEPROM.read(addr);
|
||||
uint8_t lsb = EEPROM.read(addr + 1);
|
||||
|
||||
// Combine through shifting and bitwise-or
|
||||
return (msb << 8) | lsb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize EEPROM values if not already written
|
||||
*/
|
||||
void eeprom_init() {
|
||||
// Check if data has already been stored in EEPROM
|
||||
if (eeprom_read16(EEPROM_INIT_DONE)) {
|
||||
eeprom_write16(EEPROM_INIT_DONE, 1126);
|
||||
eeprom_write16(EEPROM_I2C_ADDR, DEFAULT_I2C_ADDR);
|
||||
eeprom_write16(EEPROM_DEFAULT_ADC_NH3, 123);
|
||||
eeprom_write16(EEPROM_DEFAULT_ADC_CO, 123);
|
||||
eeprom_write16(EEPROM_DEFAULT_ADC_NO2, 123);
|
||||
eeprom_write16(EEPROM_ADC_NH3, 123);
|
||||
eeprom_write16(EEPROM_ADC_CO, 123);
|
||||
eeprom_write16(EEPROM_ADC_NO2, 123);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the analog pin repeatedly and return the average value.
|
||||
*
|
||||
* @param pin
|
||||
* The (analog) pin to read from.
|
||||
* @return The average value read.
|
||||
*/
|
||||
uint16_t analogReadAvg(uint8_t pin) {
|
||||
// Analog read returns value between 0 and 1024, so at most 10 bits are needed.
|
||||
// If we read 64 times we can just store that in 16 bits of a uint16_t.
|
||||
|
||||
uint16_t sum = 0;
|
||||
for (int c = 0; c < 16; ++c) {
|
||||
sum += analogRead(pin);
|
||||
}
|
||||
|
||||
// Shift 6 bits right to calculate average (div by 64)
|
||||
return (sum >> 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the current values from the sensor
|
||||
* and stores the values in the global data.
|
||||
*/
|
||||
void updateValues() {
|
||||
uint16_t adcNH3 = analogReadAvg(NH3_IN);
|
||||
uint16_t adcCO = analogReadAvg(CO_IN);
|
||||
uint16_t adcNO2 = analogReadAvg(NO2_IN);
|
||||
|
||||
adcData[0] = adcNH3 >> 8; // MSB of NH3 value
|
||||
adcData[1] = adcNH3 & 0xFF; // LSB of NH3 value
|
||||
adcData[2] = adcCO >> 8; // MSB of CO value
|
||||
adcData[3] = adcCO & 0xFF; // LSB of CO value
|
||||
adcData[4] = adcNO2 >> 8; // MSB of NO2 value
|
||||
adcData[5] = adcNO2 & 0xFF; // LSB of NO2 value
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for data received from the master.
|
||||
*/
|
||||
void receiveCallback(uint16_t availData) {
|
||||
// Check possible package sizes
|
||||
if (availData == 1) {
|
||||
// Store command for the requestCallback
|
||||
i2CCmd = Wire.read();
|
||||
} else if (availData == 2) {
|
||||
// Command and one byte of data
|
||||
i2CCmd = Wire.read();
|
||||
i2CData = Wire.read();
|
||||
|
||||
// Process direct commands
|
||||
if (i2CCmd == CMD_CHANGE_I2C) {
|
||||
// Store new address in eeprom and restart Wire library with new address
|
||||
eeprom_write16(EEPROM_I2C_ADDR, i2CData);
|
||||
Wire.begin(i2CData);
|
||||
} else if (i2CCmd == CMD_CONTROL_PWR) {
|
||||
if (i2CData) {
|
||||
// Turn heater on
|
||||
digitalWrite(HEATER_EN, LOW);
|
||||
} else {
|
||||
// Turn heater off
|
||||
digitalWrite(HEATER_EN, HIGH);
|
||||
}
|
||||
}
|
||||
} else if (availData == 7) {
|
||||
// Lots of data, only happens for new calilbration data
|
||||
i2CCmd = Wire.read();
|
||||
uint16_t newADCData[3];
|
||||
|
||||
// Read all three channels
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
uint8_t msb = Wire.read();
|
||||
uint8_t lsb = Wire.read();
|
||||
newADCData[i] = (msb << 8) | lsb;
|
||||
}
|
||||
|
||||
// Check if command valid
|
||||
|
||||
if (i2CCmd == CMD_SET_R0_ADC) {
|
||||
eeprom_write16(EEPROM_ADC_NH3, newADCData[0]);
|
||||
eeprom_write16(EEPROM_ADC_CO, newADCData[1]);
|
||||
eeprom_write16(EEPROM_ADC_NO2, newADCData[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for sending data to the master.
|
||||
* Requires command to be set by receiveCallback.
|
||||
*/
|
||||
void requestCallback() {
|
||||
switch (i2CCmd) {
|
||||
// Resistor data
|
||||
case CMD_ADC_NH3:
|
||||
Wire.write(&adcData[0], 2);
|
||||
break;
|
||||
case CMD_ADC_CO:
|
||||
Wire.write(&adcData[2], 2);
|
||||
break;
|
||||
case CMD_ADC_NO2:
|
||||
Wire.write(&adcData[4], 2);
|
||||
break;
|
||||
case CMD_ADC_ALL:
|
||||
Wire.write(adcData, 6);
|
||||
break;
|
||||
|
||||
case CMD_READ_EEPROM:
|
||||
i2CResp[0] = EEPROM.read(i2CData);
|
||||
i2CResp[1] = EEPROM.read(i2CData + 1);
|
||||
Wire.write(i2CResp, 2);
|
||||
break;
|
||||
|
||||
case CMD_GET_R0_ADC:
|
||||
i2CResp[0] = EEPROM.read(EEPROM_ADC_NH3);
|
||||
i2CResp[1] = EEPROM.read(EEPROM_ADC_NH3 + 1);
|
||||
i2CResp[2] = EEPROM.read(EEPROM_ADC_CO);
|
||||
i2CResp[3] = EEPROM.read(EEPROM_ADC_CO + 1);
|
||||
i2CResp[4] = EEPROM.read(EEPROM_ADC_NO2);
|
||||
i2CResp[5] = EEPROM.read(EEPROM_ADC_NO2 + 1);
|
||||
Wire.write(i2CResp, 6);
|
||||
break;
|
||||
|
||||
case CMD_GET_R0_ADC_DEFAULT:
|
||||
i2CResp[0] = EEPROM.read(EEPROM_DEFAULT_ADC_NH3);
|
||||
i2CResp[1] = EEPROM.read(EEPROM_DEFAULT_ADC_NH3 + 1);
|
||||
i2CResp[2] = EEPROM.read(EEPROM_DEFAULT_ADC_CO);
|
||||
i2CResp[3] = EEPROM.read(EEPROM_DEFAULT_ADC_CO + 1);
|
||||
i2CResp[4] = EEPROM.read(EEPROM_DEFAULT_ADC_NO2);
|
||||
i2CResp[5] = EEPROM.read(EEPROM_DEFAULT_ADC_NO2 + 1);
|
||||
Wire.write(i2CResp, 6);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// Connect to sensor through wire lib directly
|
||||
Wire.begin(D3, D4);
|
||||
Wire.begin();
|
||||
|
||||
// Check version
|
||||
Wire.beginTransmission(I2C_ADDR);
|
||||
|
@ -58,6 +58,7 @@ void loop() {
|
|||
Wire.beginTransmission(I2C_ADDR);
|
||||
Wire.write(CMD_ADC_ALL);
|
||||
Wire.endTransmission();
|
||||
delayMicroseconds(50);
|
||||
uint8_t ret = Wire.requestFrom(I2C_ADDR, 6);
|
||||
if (ret == 6 && Wire.available() == 6) {
|
||||
uint16_t data[3];
|
||||
|
@ -72,7 +73,8 @@ void loop() {
|
|||
Serial.print("\t");
|
||||
Serial.println(data[2]);
|
||||
} else {
|
||||
Serial.println("Request error!");
|
||||
Serial.print("Request error! - Returned bytes: ");
|
||||
Serial.println(ret);
|
||||
}
|
||||
|
||||
delay(50);
|
||||
|
|
42
MiCS6814-I2C-Tests/MiCS6814-I2C-Tests.ino
Normal file
42
MiCS6814-I2C-Tests/MiCS6814-I2C-Tests.ino
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <MiCS6814-I2C.h>
|
||||
|
||||
MiCS6814 sensor;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
if (sensor.begin()) {
|
||||
sensor.powerOn();
|
||||
|
||||
Serial.println("Current base resistances:");
|
||||
Serial.print("NH3: ");
|
||||
Serial.print(sensor.getBaseResistance(CH_NH3));
|
||||
Serial.print("\tRED: ");
|
||||
Serial.print(sensor.getBaseResistance(CH_RED));
|
||||
Serial.print("\tOX: ");
|
||||
Serial.println(sensor.getBaseResistance(CH_OX));
|
||||
|
||||
Serial.println("---");
|
||||
|
||||
Serial.println("Starting calibration");
|
||||
sensor.calibrate();
|
||||
|
||||
Serial.println("Calibration done");
|
||||
Serial.println("New base resistances:");
|
||||
Serial.print("NH3: ");
|
||||
Serial.print(sensor.getBaseResistance(CH_NH3));
|
||||
Serial.print("\tRED: ");
|
||||
Serial.print(sensor.getBaseResistance(CH_RED));
|
||||
Serial.print("\tOX: ");
|
||||
Serial.println(sensor.getBaseResistance(CH_OX));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
Loading…
Reference in a new issue