Skip to main content

Overview

In this wiki, we are introducing how to add more Grove modules in to SenseCAP S2110 Sensor Builder and list all the supported ones.

Add Grove - ±5A DC/AC Current Sensor (ACS70331) to the builder and applying

1. Build new libraries using GitHub source code

The contents here are on the GitHub where we maintain the code.

  • Step 1: Add a sensorNew.hpp file to the src\sensor folder for the new sensor.

  • Step 2: Define the sensor class and implement the init() and sample() functions.

    The sensor class should inherits from the sensorClass class, and implement the init() and sample() functions. The init() function is used to initialize the sensor, and than returns a register offset value for Modbus communication. The sample() function is used to read the sensor data, returns true when the data is valid, and returns false when the data is invalid.

  • Step 3: Include the sensorNEW.hpp file and call it.

    Add line #include "sensorNew.hpp" to the src\sensor\sensorBuilder.hpp file. In the setup() function of the sensorBuilder.ino file, create the new sensor class object and call the SensorBuilder.addSensor() function with it as an argument.

Refer to the following code :

void setup()
{
Serial.begin(9600);
SensorBuilder.check_grove();

/* sensor list */
sensorUltrasonic *ultrasonic = new sensorUltrasonic();
SensorBuilder.addSensor(ultrasonic);

// add new sensor to sensor list
sensorNew *newSensor = new sensorNew();
SensorBuilder.addSensor(newSensor);

SensorBuilder.begin();
}
note

The Modbus register address for the new sensor will start from 0x0034, the register bit width for each measurement value is 32, so the register address offset between two adjacent measurement values is 2.

2. Knowledge of Modbus Register Table

Modbus register addresses 0x0000 to 0x0003 are reserved for storing module system information, where 0x0000 is the modbus address with a default value of 1 and a maximum value of 247, 0x0001 is the serial port baud rate with a default value of 96 (corresponding to 9600), and 0x0002 to 0x0003 are for software version.

Grove Sensor NameRegister NameRegister Address
(Hexadecimal)
Register Address
(Decimal)
Grove - CO2 & Temperature & Humidity Sensor (SCD41)Temperature0x000404
Humidity0x000606
CO20x000808
Grove - Light Sensor v1.2Light0x000A10
Grove - Flame SensorFlame0x000C12
Grove - Oxygen Sensor (MIX8410)Oxygen0x000E14
Grove - Sunlight sensor (SI1151)Light Intensity0x001016
Visible Light0x001218
UV0x001420
Grove Temperature and Barometer Sensor (BMP280)Barometric Temperature0x001622
Atmospheric Pressure0x001824
Height0x001A26
Grove - Temperature Humidity Pressure Gas Sensor(BME680)Temperature0x001C28
Atmospheric Pressure0x001E30
Humidity0x002032
Air Quality(VOC)0x002234
Grove - Gas Sensor V2(Multichannel)N020x002436
C2H50H0x002638
VOC0x002840
CO0x002A42
Grove - UV SensorUV Intensity0x002C44
Grove - Turbidity Sensor Meter V1.0Turbidity0x002E46
Grove - TDS SensorTDS0x003048
Grove - Ultrasonic RangerDistance0x003250

3. Knowledge of Hardware Connection

Connect the SIG (signal) pin of the sensor to one of the analog pins of the any microcontroller, supply 5V-3.3V to VCC and GND to Ground of the microcontroller.

The Grove sensor comes with a potentiometer mounted on it, that allows the user to fine tune with the gain, making adjustable to fit different input voltages. Its helps change sensor's sensitivity.

4. From steps above, we can have the library for Grove AC sensor:

Stick to the steps above, we have the library for applying Grove AC sensor.

#ifndef _SENSOR_AC_H
#define _SENSOR_AC_H

#include "sensorClass.hpp"

#define AC_ADC_PIN A2
#define ADC_BITS 12
#define ADC_COUNTS (1<<ADC_BITS)

class sensorAC : public sensorClass
{
public:
sensorAC(): sensorClass("AC"){};
~sensorAC(){};

uint16_t init(uint16_t reg, bool i2c_available);
bool connected();
bool sample();

enum
{
AC = 0,
MAX
};
private:
double voltCal = 523.56;
double phaseCal = 1.7;
unsigned int cycles = 20;
unsigned int timeout = 2000;
int SupplyVoltage = 3300;
int sampleV;
double lastFilteredV,filteredV;
double offsetV = ADC_COUNTS >> 1;
double phaseShiftedV;
double sqV,sumV;
int startV;
boolean lastVCross,checkVCross;
};

uint16_t sensorAC::init(uint16_t reg, bool i2c_available){
uint16_t t_reg = reg;

for (uint16_t i = 0; i < sensorAC::MAX; i++)
{
sensorClass::reg_t value;
value.addr = t_reg;
value.type = sensorClass::regType_t::REG_TYPE_S32_ABCD;
value.value.s32 = 0;
t_reg += sensorClass::valueLength(value.type);
m_valueVector.emplace_back(value);
}

_connected = i2c_available ? false : true;
//_connected = true;
return t_reg - reg;
}

bool sensorAC::sample()
{

GROVE_SWITCH_ADC;
pinMode(AC_ADC_PIN, INPUT);

unsigned int crossCount = 0;
unsigned int numberOfSamples = 0;

unsigned long start = millis();

while(1){
int startV = analogRead(AC_ADC_PIN);
if((startV<(ADC_COUNTS*0.51)) && (startV>(ADC_COUNTS*0.49)))
break;
if((millis()-start)>timeout)
break;
}

start = millis();

while((crossCount<cycles) && ((millis()-start)<timeout))
{
numberOfSamples++;
lastFilteredV = filteredV;

sampleV = analogRead(AC_ADC_PIN);
offsetV = offsetV + ((sampleV - offsetV)/ADC_COUNTS);
filteredV = sampleV - offsetV;

sqV = filteredV * filteredV;
sumV += sqV;

phaseShiftedV = lastFilteredV + phaseCal * (filteredV - lastFilteredV);

lastVCross = checkVCross;
if(sampleV>startV)
checkVCross = true;
else
checkVCross = false;
if(numberOfSamples == 1)
lastVCross = checkVCross;
if(lastVCross !=checkVCross)
crossCount++;
}

double V_RATIO = voltCal * ((SupplyVoltage/1000.0)/(ADC_COUNTS));
float value = V_RATIO * sqrt(sumV/numberOfSamples);
m_valueVector[AC].value.s32 = value * SCALE;
//Serial.println(value);
sumV = 0;

return true;
}

bool sensorAC::connected()
{
return _connected;
}

#endif

5. Using Arduino to program to test first

The program takes a few parameters, that need to be initialized before running the program. This ensure that the program is running correctly with the sensor and to get accurate values.

First flash the program into the microcontroller, then calibrate the parameters to fit the readings.

#define AC_ADC_PIN A2               //here pin A2 is used
#define ADC_BITS 12 //depends on microcontroller to microcontroller
#define Calibration_Value 523.56 //depends on the calibration result
#define Phase_Shift 1.7 //depends on the calibration result

void setup() {
Serial.begin(115200);
pinMode(AC_ADC_PIN, INPUT);
}

int ADC_COUNTS = (1<<ADC_BITS);
double voltCal = Calibration_Value;
double phaseCal = Phase_Shift;
unsigned int cycles = 10; //Number of AC Cycles you want to measure
unsigned int timeout = 500; //Timeout
int SupplyVoltage = 3300;
int sampleV;
double lastFilteredV,filteredV;
double offsetV = ADC_COUNTS >> 1;
double phaseShiftedV;
double sqV,sumV;
int startV;
boolean lastVCross,checkVCross;

void loop() {
unsigned int crossCount = 0;
unsigned int numberOfSamples = 0;

unsigned long start = millis();

while(1){
int startV = analogRead(AC_ADC_PIN);
if((startV<(ADC_COUNTS*0.51)) && (startV>(ADC_COUNTS*0.49)))
break;
if((millis()-start)>timeout)
break;
}

start = millis();

while((crossCount<cycles) && ((millis()-start)<timeout))
{
numberOfSamples++;
lastFilteredV = filteredV;

sampleV = analogRead(AC_ADC_PIN);
offsetV = offsetV + ((sampleV - offsetV)/ADC_COUNTS);
filteredV = sampleV - offsetV;

sqV = filteredV * filteredV;
sumV += sqV;

phaseShiftedV = lastFilteredV + phaseCal * (filteredV - lastFilteredV);

lastVCross = checkVCross;
if(sampleV>startV)
checkVCross = true;
else
checkVCross = false;
if(numberOfSamples == 1)
lastVCross = checkVCross;
if(lastVCross !=checkVCross)
crossCount++;
}

double V_RATIO = voltCal * ((SupplyVoltage/1000.0)/(ADC_COUNTS));
float value = V_RATIO * sqrt(sumV/numberOfSamples);
Serial.println(value);
sumV = 0;

}

6. Get Calibration value

Initially the Analog pin is set to the A2 pin, could be changed according to your requirement, using AC_ADC_PIN parameter. Calibration_Value and Phase_Shift value, need to be changed every time you change the voltage source as the AC voltage varies from country to country or sometimes even socket to socket.

The program outputs the sensor value onto the serial monitor. One can also open the serial plotter to view the voltage vs time graph.

  • Step 1: Take the multimeter and measure the AC voltage and note it down.
  • Step 2: Similarly note the voltage shown in the serial monitor.

Here in my case, the reading from the multimeter is 220V RMS voltage, while the sensor shows 718.87V on the serial monitor, in order to get an accurate calibration value, we need to do simple math, using the following formula.

 \dfrac{Mains Voltage}{x} = \dfrac{Sensor voltage}{Initail Calibration}

  • Step 3: Find the value of x and replace it with Calibration_Value in the program and flash the program to the microcontroller.

 x = \dfrac{mains voltage \times initial calibration}{Sensor voltage} You can change the other parameters like Phase_Shift, Number of AC cycles and timeout according to your configuration or keep it default.

Reference

The list of supported Grove modules for SenseCAP S2110 Sensor Builder

Currently, SenseCAP S2110 Sensor Builder supports the following Grove modules out-of-the-box to communicate with SenseCAP Data Logger and send the sensor data to the SenseCAP platform via LoRa.

✨ Contributor Project

Tech Support & Product Discussion

Thank you for choosing our products! We are here to provide you with different support to ensure that your experience with our products is as smooth as possible. We offer several communication channels to cater to different preferences and needs.

Loading Comments...