Tilt Sensor (Part 7)

Part 123456789

Calibrating the accelerometer on each boot may be boring! It might be a good idea to store the calibration parameters in some non-volatile memory space so that we could call them back on boot. The ATMEGA has a built in EEPROM memory space which will be perfect for this use.

First of all, we must include the EEPROM library on top of the sketch

#include

Then we will declare a custom variable that we will be using for reading and writing any type of data type under the form of bytes array:

typedef union combo_t {   
	double dValue;
	int16_t iValue;
	char bValue[]; // Little endian format (LSB first)
} combo; 
combo _cboValue;

Then we need to define the address of variables within the EEPROM space

const uint8_t MEM_LOC_OFFSETS = 0;
const uint8_t MEM_LOC_GAIN = 12;
const uint8_t MEM_LOC_DSP_MODE = 24;
const uint8_t MEM_LOC_TLT_DET_THRESHOLD = 28;
const uint8_t MEM_LOC_SAMPLES = 32;
const uint8_t MEM_LOC_PAUSE = 36;

Note:

  • Avoid memory overlaps by reserving enough space for each parameter

Then we need a couple of functions for reading and writing double and 16 bits integer data types

/* EEPROM read write functions */
void writeDoubleInEEPROM(uint16_t address, double value) {
	/* Write double value */
	_cboValue.dValue = value;
	/* Read mapped bytes */
	for (uint8_t i = 0; i < 4; i++) EEPROM.write((address + i), _cboValue.bValue[i]);			
}

double readDoubleFromEEPROM(uint16_t address) {
	/* Write mapped bytes */
	for (uint8_t i = 0; i < 4; i++) _cboValue.bValue[i] = EEPROM.read(address + i);			
	/* Read resulting double value */
	return(_cboValue.dValue);
}

void writeIntegerInEEPROM(uint16_t address, int16_t value) {
	/* Write integer value */
	_cboValue.iValue = value;
	/* Read mapped bytes */
	for (uint8_t i = 0; i < 2; i++) EEPROM.write((address + i), _cboValue.bValue[i]);			
}

int16_t readIntegerFromEEPROM(uint16_t address) {
	/* Write mapped bytes */
	for (uint8_t i = 0; i < 2; i++)	_cboValue.bValue[i] = EEPROM.read(address + i);			
	/* Read resulting integer value */
	return(_cboValue.iValue);
}

Finally, these are the functions which will read …

void readDefParameters() {
/* Read Default parameters */
	for (int i = 0; i < _channels; i++) {
		vOffset[i] = readDoubleFromEEPROM((MEM_LOC_OFFSETS + (i * sizeof(double))));
		vGain[i] = readDoubleFromEEPROM((MEM_LOC_GAIN + (i * sizeof(double))));
	}
	/* Tilt detection threshold */
	_tiltDetectThreshold = EEPROM.read(MEM_LOC_TLT_DET_THRESHOLD);
	_tiltDetectThreshold = constrain(_tiltDetectThreshold, 0, 90);
	/* Default display mode */
	_dispMode = EEPROM.read(MEM_LOC_DSP_MODE);
	_dispMode = constrain(_dispMode, 0, 8); /* Keep read value inside allowed range */
	/* Default pause */
	_pause = readIntegerFromEEPROM(MEM_LOC_PAUSE);
	_pause = constrain(_pause, 10, 5000); /* Keep read value inside allowed range */
	/* Default samples */
	_samples = readIntegerFromEEPROM(MEM_LOC_SAMPLES);
	_samples = constrain(_samples, 1, 128); /* Keep read value inside allowed range */
}

… and write calibration parameters

EEPROM.write(MEM_LOC_DSP_MODE, _dispMode);
EEPROM.write(MEM_LOC_TLT_DET_THRESHOLD, _tiltDetectThreshold);
writeIntegerInEEPROM(MEM_LOC_SAMPLES, _samples);
writeIntegerInEEPROM(MEM_LOC_PAUSE, _pause);
for (int i = 0; i < 3; i++) {
	writeDoubleInEEPROM((MEM_LOC_OFFSETS + (i * sizeof(double))), vOffset[i]);
	writeDoubleInEEPROM((MEM_LOC_GAIN + (i * sizeof(double))), vGain[i]);
}

Note:

  • The lines from above are located next to the various places in the code where specific calibration parameters are set.
  • It is probably a good idea to constrain the read data from EEPROM in order to avoid unpredictable operation due to ghost data read from EEPROM, which were written from previous application or code version

Next post on same subject

Leave a Reply

You must be logged in to post a comment.