Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin for CM1107 CO2 sensor #126

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 273 additions & 0 deletions _P177_CM1107.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
//#######################################################################################################
//######################## Plugin 177 CM1107 I2C CO2 Sensor ############################################
//#######################################################################################################
// development version
// by: V0JT4
/* Commands: CMGETABC - displays content of automatic calibration register */

#include "ESPEasy-Globals.h"
#define PLUGIN_177
#define PLUGIN_ID_177 177
#define PLUGIN_NAME_177 "Gases - CO2 CM1107"
#define PLUGIN_VALUENAME1_177 "CO2"

boolean Plugin_177_init = false;


#define CM1107_ADDR 0x31 // default address

#define CM1107_CMD_CO2 0x01
#define CM1107_CMD_ABC 0x10
#define CM1107_CMD_CAL 0x03
#define CM1107_CMD_SN 0x1F
#define CM1107_CMD_VER 0x1E

#define CM1107_STATUS_INIT 0x00 //0 1 swapped???
#define CM1107_STATUS_OK 0x01
#define CM1107_STATUS_ERR 0x02
#define CM1107_STATUS_OUT 0x03
#define CM1107_STATUS_NCAL 0x05

#define CM1107_CAL_ON 0x00
#define CM1107_CAL_OFF 0x02



boolean plugin_177_begin()
{
//Wire.begin(); called in ESPEasy framework
Plugin_177_init = true;
return(true);
}

boolean plugin_177_setABC(unsigned char abc)
// Set auto calibration period on CM1107
// Returns true (1) if successful, false (0) if there was an I2C error
{
// Write auto calibration configuration
Wire.beginTransmission(CM1107_ADDR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the functions in the existing I2C.ino file?
This makes it much harder to fix bugs or change I2C related code later.
Also lots of plugins tend to do just the same, so better use the existing functions then it is also clear what's being done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any function that would cover sensor's requirements, number of bytes to read and write at once.

Wire.write(CM1107_CMD_ABC);
Wire.write(100); // defined by data-sheet
if (abc)
{
Wire.write(CM1107_CAL_ON);
Wire.write(abc);
} else
{
Wire.write(CM1107_CAL_OFF);
Wire.write(7);
}
// ? make calibration concentration value configurable
Wire.write((uint8_t)(400 >> 8));
Wire.write((uint8_t)400);
Wire.write(100); // defined by data-sheet
return (Wire.endTransmission() == 0);
}
/* DEV getABC
uint8_t plugin_177_getABC()
{
I2C_write8(CM1107_ADDR, CM1107_CMD_ABC);
delay(500);
uint8_t abc;
Wire.requestFrom(CM1107_ADDR, (byte)8);

String log = F("CM1107: ABC:");
log += Wire.read(); // expected 0x10
log += F(" ");
log += Wire.read(); // expected 100
log += F(" ");
abc = Wire.read(); // expected 0 or 2
log += abc;
log += F(" ");
if (abc)
{ //expected read 1-15
log += Wire.read();
abc = 0;
} else
{
abc = Wire.read();
log += abc;
}
log += F(" ");
log += (Wire.read() << 8) | Wire.read();
log += F(" ");
log += Wire.read(); // expected 100
log += F(" ");
log += Wire.read(); // CS
addLog(LOG_LEVEL_INFO,log);
return abc;
}*/
String plugin_177_getABC()
{
I2C_write8(CM1107_ADDR, CM1107_CMD_ABC);
delay(500);
uint8_t abc; //replace with function parameter?
uint16_t co2;
uint8_t checksum;

Wire.requestFrom(CM1107_ADDR, (byte)8);
String log = F("CM1107: ABC: ");
checksum = Wire.read(); // expected CM1107_CMD_ABC
if (checksum != CM1107_CMD_ABC)
{
log += F("CMD_ERROR ");
}
checksum += Wire.read(); // expected 100
abc = Wire.read(); // expected 0 or 2
checksum += abc;
if (abc)
{ //expected read 1-15
abc = Wire.read();
checksum += abc;
log += F("OFF, period: ");
log += abc;
abc = 0;
} else
{
abc = Wire.read();
checksum += abc;
log += F("ON, period: ");
log += abc;
}
log += F(", ppm: ");
co2 = Wire.read(); // high byte ppm
checksum += co2;
co2 = (co2 << 8) | Wire.read(); // low byte ppm
checksum += (uint8_t)co2;
log += co2;
checksum += Wire.read(); // expected 100
checksum += Wire.read(); // CS
log += F(", checksum: ");
if (checksum)
{
log += F("ERR");
log += checksum;
} else
{
log += F("OK");
}
if(logLevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO,log);
}
return log;
}

uint16_t plugin_177_getCO2()
// Retrieve CO2 data in ppm
{
I2C_write8(CM1107_ADDR, CM1107_CMD_CO2);
uint16_t co2;
uint8_t status;
uint8_t checksum;

Wire.requestFrom(CM1107_ADDR, (byte)5);
checksum = Wire.read(); // CM1107_CMD_CO2
co2 = Wire.read(); // high byte ppm
checksum += (uint8_t)co2;
co2 = (co2 << 8) | Wire.read(); // low byte ppm
checksum += (uint8_t)co2;
status = Wire.read(); // status
checksum += status;
checksum += Wire.read(); //CS
if(logLevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("CM1107: CO2 ppm:");
log += co2;
log += F(", status: ");
log += status;
log += F(", checksum: ");
if (checksum)
{
log += F("ERR");
log += checksum;
} else
{
log += F("OK");
}
addLog(LOG_LEVEL_INFO,log);
}
return co2;
}


boolean Plugin_177(byte function, struct EventStruct *event, String& string)
{
boolean success = false;

switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_177;
Device[deviceCount].Type = DEVICE_TYPE_I2C;
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}

case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_177);
break;
}

case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_177));
break;
}

case PLUGIN_WEBFORM_LOAD:
{
addFormNumericBox(F("ABC period"), F("plugin_177_CM1107_abc"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
addUnit(F("days, 0=off"));
success = true;
break;
}

case PLUGIN_WEBFORM_SAVE:
{
unsigned char abc = getFormItemInt(F("plugin_177_CM1107_abc"));
if (abc > 15) abc = 15;
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = abc;
success = true;
break;
}
case PLUGIN_INIT:
{
plugin_177_begin();
success = plugin_177_setABC((uint8_t) Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
I2C_write8(CM1107_ADDR, CM1107_CMD_CO2);
break;
}
case PLUGIN_READ:
{
plugin_177_begin();
uint16_t co2 = plugin_177_getCO2();
UserVar[event->BaseVarIndex] = co2;
if (co2 > 5000 && logLevelActiveFor(LOG_LEVEL_INFO))
{
addLog(LOG_LEVEL_INFO,F("CM1107: Sensor saturated! > 5000 ppm"));
}
success = true;
break;
}
case PLUGIN_WRITE:
{
if (string.equalsIgnoreCase(F("CMGETABC")))
{
success = true;
SendStatus(event->Source, plugin_177_getABC());
I2C_write8(CM1107_ADDR, CM1107_CMD_CO2);
}
break;
}
}
return success;
}