I have a device that works on 3V battery (2 D size cells) now I want to debug it so that all the devices work correctly. My device consist of atmega 328p, running on 08 MHz Crystal which gets readings from ADXL345 for 2 seconds and then store it in 25Q64FVSIG flash memory and then RFM96 checks if server is available if yes then send the current timestamp, the data stored in flash and current battery otherwise goes to deep sleep mode. There are two 22 pF, one 100uF25V and one 104 pF (to power up radio) capacitors and 10k resistor. This device is enclose in a hard plastic box and then water packed so that not even air goes in or out of that box.
Now the main issue is that most of devices lose battery faster and die after a week or two ,but then one or two keeps on going for 2 to 3 months, yet same code is uploaded in all of them. I'm attaching reference picture of my device circuit and let me know if you need anything else.
I have tried to debug through checking the voltage and current drop across various components yet got the same values for every device as compare to the perfect one. I have not checked the capacitance of capacitors let me know if that can be crucial as well.
// Cow Health Monitoring Node
// Author: Muhammad Umar
// Date: 16th September 2019
// Version 1.0
#include <Arduino.h>
#include <LowPower.h>
#include <SPI.h>
#include <TimeLib.h>
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <Wire.h>
#include <ADXL345.h>
#include <SPIFlash.h>
#include <EEPROM.h>
#define DEBUGGING false
#define SLEEP false
#define FLASH_CS 8
#define SERVER_ADDRESS 1
#define SAFE_RSSI_VALUE -85
#define RADIO_PWR 14
#define CLIENT_ADDRESS 102
#define X_OFFSET 0
#define Y_OFFSET 0
#define Z_OFFSET 0
#define DATA_RATE 12.5
#define ACCEL_RANGE 2
ADXL345 adxl;
SPIFlash flash(FLASH_CS);
RH_RF95 driver;
RHReliableDatagram manager(driver, CLIENT_ADDRESS);
const int gatherValuesDuration = 2; // 4 collections @ 12.5 Hz - 1 run = 2 second values
const int sleepingStateDuration = 30; // sleep for 32 seconds 8*8
// const int sleepingStateDuration = 1;
// const int sleepingStateDuration = 2;
const long InternalReferenceVoltage = 1062;
// bool firstBoot = true;
int initialSleepDuration = 0;
bool getValues = true;
bool getTime = true;
bool sendValues;
bool radioAvailable = false;
int timesValuesCollected = 0;
long currentFlashLoc = 0;
long dumpCounter = 0;
int noOfReadingsInTransmission = 0;
/**
* On board devices setup
* Radio
* Flash
* Accelerometer
* **/
void radioSetup()
{
if (!manager.init())
{
#if DEBUGGING
Serial.println("Radio init failed");
#endif
}
// 13 for ubmilk farm
driver.setTxPower(RADIO_PWR, false);
manager.setRetries(2);
driver.sleep();
#if DEBUGGING
Serial.println("Radio Setup Complete");
#endif
}
void adxlSetup()
{
adxl.powerOn();
// look of activity movement on this axes - 1 == on; 0 == off
adxl.setActivityX(1);
adxl.setActivityY(1);
adxl.setActivityZ(1);
// setting all interrupts to take place on int pin 1
// I had issues with int pin 2, was unable to reset it
adxl.setInterruptMapping(ADXL345_INT_DATA_READY_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_SINGLE_TAP_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_DOUBLE_TAP_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_FREE_FALL_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_ACTIVITY_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_INACTIVITY_BIT, ADXL345_INT1_PIN);
adxl.setInterruptMapping(ADXL345_INT_WATERMARK_BIT, ADXL345_INT1_PIN);
// register interrupt actions - 1 == on; 0 == off
adxl.setInterrupt(ADXL345_INT_DATA_READY_BIT, 1);
adxl.setInterrupt(ADXL345_INT_SINGLE_TAP_BIT, 0);
adxl.setInterrupt(ADXL345_INT_DOUBLE_TAP_BIT, 0);
adxl.setInterrupt(ADXL345_INT_FREE_FALL_BIT, 0);
adxl.setInterrupt(ADXL345_INT_ACTIVITY_BIT, 0);
adxl.setInterrupt(ADXL345_INT_INACTIVITY_BIT, 0);
adxl.setInterrupt(ADXL345_INT_WATERMARK_BIT, 1);
// setting lowest sampling rate
adxl.setAxisOffset(X_OFFSET, Y_OFFSET, Z_OFFSET);
adxl.setRangeSetting(ACCEL_RANGE);
adxl.setRate(DATA_RATE);
// setting device into FIFO mode
adxl.setMode(ADXL345_MODE_FIFO);
// set watermark for Watermark interrupt
adxl.setWatermark(25);
adxl.setLowPower(0);
#if DEBUGGING
Serial.println("ADXL Setup Complete");
#endif
}
void flashSetup()
{
if (flash.initialize())
{
flash.chipErase();
while (!flash.busy())
{
// wait for flash to fully erase
}
}
else
{
#if DEBUGGING
Serial.println("Flash Setup Failed");
#endif
}
#if DEBUGGING
Serial.println("Flash Setup Complete");
#endif
}
// results are Vcc * 100
// So for example, 5V would be 500.
int getBandgap()
{
// REFS0 : Selects AVcc external reference
// MUX3 MUX2 MUX1 : Selects 1.1V (VBG)
ADMUX = bit(REFS0) | bit(MUX3) | bit(MUX2) | bit(MUX1);
ADCSRA |= bit(ADSC); // start conversion
while (ADCSRA & bit(ADSC))
{
} // wait for conversion to complete
int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10;
return results;
} // end of getBandgap
void updateTime()
{
uint8_t buf[11];
uint8_t len = sizeof(buf);
uint8_t from;
char target[10];
if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
{
strncpy(target, (char *)buf, 10);
target[10] = '\0';
#if DEBUGGING
Serial.print("Time Received: ");
Serial.println(target);
#endif
setTime(atol(target));
driver.sleep();
}
delay(50);
}
void updateTimeWithTimePacket()
{
// updating time from the server
char timePacket[8];
sprintf(timePacket, "%d,time\n", CLIENT_ADDRESS);
#if DEBUGGING
Serial.print("Requesting Time: ");
for (int i = 0; i < 7; i++)
{
Serial.print(timePacket[i]);
}
Serial.println();
#endif
manager.sendtoWait((uint8_t *)timePacket, strlen(timePacket), SERVER_ADDRESS);
updateTime();
getTime = false;
}
void sendData()
{
Serial.println("Yo Boy send the data");
int packetsCount = 0;
int noOfLines = 0;
char packetBuff[120] = "";
int charNo = strlen(packetBuff) - 1;
while (dumpCounter < currentFlashLoc)
{
char value = (char)flash.readByte(dumpCounter++);
charNo++;
packetBuff[charNo] = value;
if (value == '/')
{
noOfLines++;
if (noOfLines == 7)
{
packetsCount++;
packetBuff[charNo + 1] = '\n';
#ifdef DEBUGGING
Serial.print("Send Packet: ");
Serial.print(packetBuff);
#endif
manager.sendtoWait((uint8_t *)packetBuff, strlen(packetBuff), SERVER_ADDRESS);
memset(packetBuff, 0, sizeof(packetBuff));
int count = (packetsCount == 0) ? 1 : (log10(packetsCount) + 1);
sprintf(packetBuff, "%d?", packetsCount);
noOfLines = 0;
charNo = count;
while ((currentFlashLoc - dumpCounter) < 54)
{
// we are nearing the end of transmission
// handle the last packet
char value = (char)flash.readByte(dumpCounter++);
charNo++;
packetBuff[charNo] = value;
if ((dumpCounter - currentFlashLoc) == 0)
{
packetsCount++;
packetBuff[charNo + 1] = '\n';
#if DEBUGGING
Serial.print("LastPacket: ");
Serial.println(packetBuff);
#endif
manager.sendtoWait((uint8_t *)packetBuff, strlen(packetBuff), SERVER_ADDRESS);
memset(packetBuff, 0, sizeof(packetBuff)); // empty packetBuff
break;
}
}
}
}
}
char timeInfo[10] = "";
ultoa(now(), timeInfo, 10);
char eotPacket[21];
// sprintf(eotPacket, "eot %d\n", packetsCount);
// sprintf(eotPacket, "%d:eot %d\n", CLIENT_ADDRESS, packetsCount);
int batteryVoltage = getBandgap();
#if DEBUGGING
Serial.print("Battery Voltage: ");
Serial.println(batteryVoltage);
Serial.print("Current Flash Loc: ");
Serial.println(currentFlashLoc);
#endif
sprintf(eotPacket, "eot:%s packs:%d vals:%d batt:%d flash:%li\n", timeInfo, packetsCount, noOfReadingsInTransmission, batteryVoltage, currentFlashLoc);
#if DEBUGGING
Serial.print(eotPacket);
#endif
manager.sendtoWait((uint8_t *)eotPacket, strlen(eotPacket), SERVER_ADDRESS);
memset(eotPacket, '\0', sizeof(eotPacket));
noOfReadingsInTransmission = 0;
updateTime();
}
int lenHelper(unsigned x)
{
if (x >= 100)
return 3;
if (x >= 10)
return 2;
return 1;
}
int readingLen(int x)
{
return x < 0 ? lenHelper(-x) + 1 : lenHelper(x);
}
void getAccelValues()
{
int x[25], y[25], z[25];
byte fifoentries, intEvent;
fifoentries = adxl.getFifoEntries();
intEvent = adxl.getInterruptSource(); // reading interrupt status flags
if (adxl.triggered(intEvent, ADXL345_WATERMARK)) // if watermark interrupt occured
{
#if DEBUGGING
Serial.println("Watermark interrupt triggered. Fetching data now.");
#endif
if (fifoentries != 0)
{
// burst read will cause the fifo to empty
adxl.burstReadXYZ(&x[0], &y[0], &z[0], fifoentries);
#if DEBUGGING
Serial.print("FIFO Entries: ");
Serial.println(fifoentries);
#endif
for (int i = 0; i < 25; i++)
{
char buf[16] = "";
sprintf(buf, "%d,%d,%d/", x[i],y[i],z[i]);
#if DEBUGGING
Serial.print("Writing to Flash: ");
Serial.println(buf);
#endif
flash.writeBytes(currentFlashLoc, buf, strlen(buf));
currentFlashLoc += strlen(buf);
noOfReadingsInTransmission++;
}
}
}
}
int checkRSSI()
{
int availableServer = 1;
char freePacket[6] = "";
sprintf(freePacket, "free\n");
#if DEBUGGING
Serial.println("Checking RSSI");
#endif
manager.sendtoWait((uint8_t *)freePacket, strlen(freePacket), SERVER_ADDRESS);
uint8_t buf[5];
uint8_t len = sizeof(buf);
uint8_t from;
char target[3] = "";
if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
{
#if DEBUGGING
Serial.println((char *)buf);
#endif
strncpy(target, (char *)buf, 3);
target[3] = '\0';
if (strcmp(target, "yes") == 0)
{
#if DEBUGGING
Serial.println("You have permission to transmit");
#endif
radioAvailable = true;
}
}
delay(50);
return availableServer;
}
void sendDataRoutine()
{
checkRSSI();
if (radioAvailable)
{
sendData();
}
else{
#if DEBUGGING
Serial.println("no radio access bro");
#endif
}
radioAvailable = false;
sendValues = false;
driver.sleep();
}
void accelerometerRoutine()
{
/***
1. wake up the accelerometer
2. start running the fifo and taking measurements
3. put arduino to sleep for 2s following points will be carried out by getAccelValues()
4. burst read the fifo after watermark interrupt
5. save the readings in flash
*/
#if DEBUGGING
Serial.println("Run Accelerometer Routine");
#endif
adxl.setMode(ADXL345_MODE_BYPASS);
delay(80);
adxl.setMode(ADXL345_MODE_FIFO);
LowPower.powerDown(SLEEP_2S, ADC_ON, BOD_ON);
LowPower.powerDown(SLEEP_15MS, ADC_ON, BOD_ON);
// adjustTime(2.5);
adjustTime(2.15);
getAccelValues();
timesValuesCollected++;
}
void writeTime()
{
char timeInfo[11] = "";
ultoa(now(), timeInfo, 10);
#if DEBUGGING
Serial.print("Current Time: ");
Serial.println(timeInfo);
#endif
int writtenBytes = 0;
for (int i = 0; timeInfo[i] != '\0'; i++)
{
writtenBytes++;
}
flash.writeBytes(currentFlashLoc, timeInfo, writtenBytes);
currentFlashLoc += writtenBytes;
flash.writeByte(currentFlashLoc, '/');
currentFlashLoc += 1;
}
void setup()
{
// put your setup code here, to run once:
#if DEBUGGING
Serial.begin(115200);
Serial.println("Initializing...");
#endif
#if SLEEP
Serial.println("Going to Sleep Forever");
delay(1000);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
#endif
// turn off everything except the radio
// and get time from the rpi server
// maybe store this value in the flash?
// e.g. on the last 10 bytes of the flash
radioSetup();
flashSetup();
adxlSetup();
// getTime = true;
// setTime(1576412345);
}
void loop()
{
int eprom_value = EEPROM.read(0);
if (eprom_value == 1)
{
for (int i = 0; i < initialSleepDuration; i++)
{
#if DEBUGGING
Serial.println("Im in long sleep boi");
#endif
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
if (i == initialSleepDuration - 1)
{
EEPROM.write(0, 0);
}
}
}
if (currentFlashLoc > 7000000)
{
flash.chipErase();
while (!flash.busy())
{
// wait for flash to fully erase
}
currentFlashLoc = 0;
dumpCounter = 0;
}
if (getTime)
{
updateTimeWithTimePacket();
return;
}
if (getValues && timesValuesCollected != gatherValuesDuration)
{
// Serial.print("Run Accelerometer Routine");
if (timesValuesCollected == 0)
{
writeTime();
// adxl.measurementMode(1);
}
accelerometerRoutine();
return;
}
else if (timesValuesCollected == gatherValuesDuration)
{
getValues = false;
adxl.setStandbyMode(0);
sendValues = true;
}
if (sendValues)
{
#if DEBUGGING
Serial.println("Send Values");
#endif
sendDataRoutine();
for (int i = 0; i < sleepingStateDuration; i++)
{
#if DEBUGGING
Serial.println("Im sleeping for short time boi");
#endif
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
if (i % 2 == 0)
{
adjustTime(9);
}
else
{
adjustTime(8);
}
}
getValues = true;
adxl.setStandbyMode(1);
timesValuesCollected = 0;
}
}