Brown Out with Data logger

We continue to have problems with brown outs on the arduinos, seemingly randomly. We are currently running 20 arduinos off of three usb power packs, which have "smart power regulation" which we think is causing the brown outs. After we distributed the arduinos between power packs and used different cords, the problem was fixed briefly.

I have been looking for “dumb” power supply, because all the usb packs I can find have voltage regulators that distribute the power, which we are thinking is the problem. It seems like the best way to address this problem is through coding the arduinos differently, although I don’t have a lot of experience handling this. I have found a few good forum posts:

What’s problem with brown out ?

This article seems to confirm that the problem we are having (see spreadsheet excerpt below) looks like brown outs, as the data keeps writing past when the peripheral unit is powered off. What I’m unsure of is whether our startup loop can re-start the peripheral unit, or if we need to write that into the code (also attached, our code: Experiment_Loop).

|-955453|0|-30.79|330998| 2095/7/14 23:11:55||
|296026|-28.89|-25.89|332000| 2095/7/14 23:11:56||
|173|-6.04|-17.32|332999| 2095/7/14 23:11:57||
|333999| 2095/7/14 23:11:58|||||
|334999| 2095/7/14 23:11:59|||||
|336000| 2095/7/14 23:12:0|||||


#include <Wire.h>
#include <EEPROM.h> //Needed to record user settings

#include "SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_NAU8702

NAU7802 myScale; //Create instance of the NAU7802 class

//EEPROM locations to store 4-byte variables
#define LOCATION_CALIBRATION_FACTOR 0 //Float, requires 4 bytes of EEPROM
#define LOCATION_ZERO_OFFSET 10 //Must be more than 4 away from previous spot. Long, requires 4 bytes of EEPROM

bool settingsDetected = false; //Used to prompt user to calibrate their scale

//Create an array to take average of weights. This helps smooth out jitter.
#define AVG_SIZE 4
float avgWeights[AVG_SIZE];
byte avgWeightSpot = 0;

#include <LiquidCrystal_I2C.h> //display

LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

#include "SD.h" //for data logger
#include <RTClib.h>
// A simple data logger for the Arduino analog pins
#define LOG_INTERVAL  1000 // mills between entries
#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()
RTC_PCF8523 rtc; // define the Real Time Clock object
// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;
 
// the logging file
File logfile;
 
void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  while(1);
} //still all for data logger


void setup()
{
  Serial.begin(9600);
  Serial.println("Qwiic Scale Example");

  Wire.begin();
  Wire.setClock(400000); //Qwiic Scale is capable of running at 400kHz if desired
  
  if (myScale.begin() == false){
    Serial.println("Scale not detected. Please check wiring. Freezing...");
    while (1);
  }
  
  Serial.println("Scale detected!");

  readSystemSettings(); //Load zeroOffset and calibrationFactor from EEPROM

  myScale.setSampleRate(NAU7802_SPS_320); //Increase to max sample rate
  myScale.calibrateAFE(); //Re-cal analog front end when we change gain, sample rate, or channel 

  Serial.print("Zero offset: ");
  Serial.println(myScale.getZeroOffset());
  Serial.print("Calibration factor: ");
  Serial.println(myScale.getCalibrationFactor());
  ////NEW STUFF
 // lcd.init();                      // initialize the lcd 
 // lcd.backlight();
  #if WAIT_TO_START //this is for datalogger
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START

// initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  // create a new file <-- this is beautiful but not functional for this project.
  //char filename[] = "LOGGER00.CSV";
  //for (uint8_t i = 0; i < 100; i++) {
    //filename[6] = i/10 + '0';
    //filename[7] = i%10 + '0';
    //if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      //logfile = SD.open(filename, FILE_WRITE); 
      //break;  // leave the loop!
    //}
  
 logfile = SD.open("m_oxy_.CSV", FILE_WRITE); 

 if (! logfile) {
  error("couldnt create file");
 }
  
 //Serial.print("Logging to: ");
 //Serial.println(filename); //end sd stuff
  
    Wire.begin();  
// if (!RTC.begin()) {
// logfile.println("RTC failed");
//#if ECHO_TO_SERIAL
  //  Serial.println("RTC failed");
//#endif  //ECHO_TO_SERIAL
//}
  logfile.println("reading,weight,avg,millis,time");    
  Serial.println("reading,weight,avg,millis,time"); //write header on SD card
  logfile.close();   ///close the file


  Wire.begin();
  Wire.setClock(400000); //Qwiic Scale is capable of running at 400kHz if desired

  if (myScale.begin() == false)
  {
    Serial.println("Scale not detected. Please check wiring. Freezing...");
    while (1);
  }

  Serial.println("Scale detected!");


  myScale.setSampleRate(NAU7802_SPS_320); //Increase to max sample rate
  myScale.calibrateAFE(); //Re-cal analog front end when we change gain, sample rate, or channel 

  Serial.print("Zero offset: ");
  Serial.println(myScale.getZeroOffset());
  Serial.print("Calibration factor: ");
  Serial.println(myScale.getCalibrationFactor());
  rtc.start();

}

void loop()
{
  logfile = SD.open("micea_oxy_.CSV", FILE_WRITE); 

   // when characters arrive over the serial port...
 // if (Serial.available()) {
    // wait a bit for the entire message to arrive
  //  delay(1);
    // clear the screen
  //  lcd.clear();
    // read all the available characters
 //   while (Serial.available() > 0) {
      // display each character to the LCD
   //   lcd.write(Serial.read());
    }
  }
  if (myScale.available() == true)
  {
    long currentReading = myScale.getReading();
    float currentWeight = myScale.getWeight();

    Serial.print("Reading: ");
    Serial.print(currentReading);
    logfile.print(currentReading);
    logfile.print(",");
    Serial.print("\tWeight: ");
    Serial.print(currentWeight, 2); //Print 2 decimal places
    logfile.print(currentWeight, 2);
    logfile.print(",");
    avgWeights[avgWeightSpot++] = currentWeight;
    if(avgWeightSpot == AVG_SIZE) avgWeightSpot = 0;

    float avgWeight = 0;
    for (int x = 0 ; x < AVG_SIZE ; x++)
      avgWeight += avgWeights[x];
    avgWeight /= AVG_SIZE;

    Serial.print("\tAvgWeight: ");
    Serial.print(avgWeight, 2); //Print 2 decimal places
    logfile.print(avgWeight,2);
    logfile.print(",");

    if(settingsDetected == false)
    {
      Serial.print("\tScale not calibrated. Press 'c'.");
    }

    Serial.println();
  }

  if (Serial.available())
  {
    byte incoming = Serial.read();

    if (incoming == 't') //Tare the scale
      myScale.calculateZeroOffset();
    else if (incoming == 'c') //Calibrate
    {
      calibrateScale();
    }
  }
  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");  
#endif

DateTime now = rtc.now();

  // fetch the time
  //DateTime now; //datalogger
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.println(now.second(), DEC);
  logfile.close();   ///close the file
#if ECHO_TO_SERIAL
    
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
#endif //ECHO_TO_SERIAL
}

//Gives user the ability to set a known weight on the scale and calculate a calibration factor
void calibrateScale(void)
{
  Serial.println();
  Serial.println();
  Serial.println(F("Scale calibration"));

  Serial.println(F("Setup scale with no weight on it. Press a key when ready."));
  while (Serial.available()) Serial.read(); //Clear anything in RX buffer
  while (Serial.available() == 0) delay(10); //Wait for user to press key

  myScale.calculateZeroOffset(64); //Zero or Tare the scale. Average over 64 readings.
  Serial.print(F("New zero offset: "));
  Serial.println(myScale.getZeroOffset());

  Serial.println(F("Place known weight on scale. Press a key when weight is in place and stable."));
  while (Serial.available()) Serial.read(); //Clear anything in RX buffer
  while (Serial.available() == 0) delay(10); //Wait for user to press key

  Serial.print(F("Please enter the weight, without units, currently sitting on the scale (for example '4.25'): "));
  while (Serial.available()) Serial.read(); //Clear anything in RX buffer
  while (Serial.available() == 0) delay(10); //Wait for user to press key

  //Read user input
  float weightOnScale = Serial.parseFloat();
  Serial.println();

  myScale.calculateCalibrationFactor(weightOnScale, 64); //Tell the library how much weight is currently on it
  Serial.print(F("New cal factor: "));
  Serial.println(myScale.getCalibrationFactor(), 2);

  Serial.print(F("New Scale Reading: "));
  Serial.println(myScale.getWeight(), 2);

  recordSystemSettings(); //Commit these values to EEPROM
}

//Record the current system settings to EEPROM
void recordSystemSettings(void)
{
  //Get various values from the library and commit them to NVM
  EEPROM.put(LOCATION_CALIBRATION_FACTOR, myScale.getCalibrationFactor());
  EEPROM.put(LOCATION_ZERO_OFFSET, myScale.getZeroOffset());
}

//Reads the current system settings from EEPROM
//If anything looks weird, reset setting to default value
void readSystemSettings(void)
{
  float settingCalibrationFactor; //Value used to convert the load cell reading to lbs or kg
  long settingZeroOffset; //Zero value that is found when scale is tared

  //Look up the calibration factor
  EEPROM.get(LOCATION_CALIBRATION_FACTOR, settingCalibrationFactor);
  if (settingCalibrationFactor == 0xFFFFFFFF)
  {
    settingCalibrationFactor = 0; //Default to 0
    EEPROM.put(LOCATION_CALIBRATION_FACTOR, settingCalibrationFactor);
  }

  //Look up the zero tare point
  EEPROM.get(LOCATION_ZERO_OFFSET, settingZeroOffset);
  if (settingZeroOffset == 0xFFFFFFFF)
  {
    settingZeroOffset = 1000L; //Default to 1000 so we don't get inf
    EEPROM.put(LOCATION_ZERO_OFFSET, settingZeroOffset);
  }

  //Pass these values to the library
 // myScale.setCalibrationFactor(settingCalibrationFactor);
  myScale.setCalibrationFactor(-12494);
  //myScale.setZeroOffset(settingZeroOffset);
  myScale.setZeroOffset(-75250);


  settingsDetected = true; //Assume for the moment that there are good cal values
  if (settingCalibrationFactor < 0.1 || settingZeroOffset == 1000)
    settingsDetected = false; //Defaults detected. Prompt user to cal scale.
}

“Keep the AVR RESET active (low) during periods of insufficient power supply voltage. This can be done by enabling the internal Brown-out Detector (BOD). If the detection level of the internal BOD does not match the needed detection level, an external low VCC reset Protection circuit can be used. If a reset occurs while a write operation is in progress, the write operation will be completed provided that the power supply voltage is sufficient.”

Based on this article: Is there Brown Out Detection for Arduino Uno? it seems the brown out detector is activated, but perhaps the threshold is too low to activate it in order for it to trip the restart?

“Now all is clear to me, I wrote this summary to have all the information in one place and to share with you.

I wanted to have low voltage (brown-out) detection on my Arduino Uno R3 to prevent it from unexpected behavior or even destroying my sketch when the 5V power supply drops below a certain level. I want to show how to correctly set the brown out detection. To set the brown-out detection a new bootloader with the setting must be burned to the Uno. I have used 1 Arduino Uno to burn a bootloader to another Arduino Uno. The other Uno is used as an in-system programmer(ISP). Here's a description how to do that http://arduino.cc/en/Tutorial/ArduinoISP.

In the Arduino IDE (mine is 1.0.1) boards.txt contains bootloader settings of many different Arduino boards. On my computer the file is located here D:\programs\arduino-1.0.1\hardware\arduino\boards.txt. In that file you need to find the Uno.


##############################################################
uno.name=Arduino Uno
uno.upload.protocol=arduino
uno.upload.maximum_size=32256
uno.upload.speed=115200
uno.bootloader.low_fuses=0xff
uno.bootloader.high_fuses=0xde
uno.bootloader.extended_fuses=0x05
uno.bootloader.path=optiboot
uno.bootloader.file=optiboot_atmega328.hex
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.core=arduino
uno.build.variant=standard
##############################################################

A change must be made to one of these lines to set the fuses of the Uno.

To find the fuse settings this fuse calculator is useful. AVR® Fuse Calculator – The Engbedded Blog. For Uno select ATmega328P. The second table shows that the brown-out detection (BOD) level is set with the extended fuse. Bits 0 to 2 are used to set the voltage level. Bits 3 to 7 are not used. Playing around with the calculator shows that 0x04 is 4.3V, 0x05 is 2.7V, 0x06 is 1.8V and 0x07 is disabled BOD. The datasheet of the ATmega328P shows the same BOD level coding.

This means that the default setting of 0x05 for the Uno that came with the IDE is 2.7V. So the Uno already has brown-out detection enabled. That's interesting, because the Arduino Uno runs on a 16MHz external crystal. The datasheet of the ATmega328P shows that at 2.7V the maximum frequency for safe operation is 10MHz. So at 16MHz the minimum voltage for safe operation must be set to 4.3V. So extended fuse must be set to 0x04.

You now learned that it useful to change the brown-out detection level for your Uno. Don't forget to save the boards.txt file, restart the IDE, burn the bootloader and then burn your sketch. Happy brown-out detecting!”

Also, it seems like the peripherals typically do not power back on with the BOD trip, but I don’t know if that includes the Amp and load cell which are connected through I2C. My reasoning comes from this:

  • “Does anybody know what happens with things like the ethernet shield when the BOD activates? On my current project the web servercode or the shield seems to stop working (IE comes up with a connection problem and wont display the pagefrom the SD card) even though there is a backup battery. Ironically the SD card keeps on recording data every 10 min, though it occasionally misses a beat. The only way to get communications going again seems to be a 'power off' reset!”
  • “When the BOD activates, as far as I know the processor enters a reset state. However this doesn't mean any attached peripherals reset. Nor does it mean the reset pin changes state (as far as I am aware)

So to effectively recover other peripherals you would have to (in setup) make them reset, if possible.”

This would suggest that that the code has to be specific enough in the restart loop to trigger the amp to restart, which I’m not sure if it is currently.

Any thoughts or direction you have about this problem would be helpful! I’m a bit out of my depth right now and unsure of the easiest way to fix this quickly. Let me know if you have any questions.

Can you post a schematic as you have it wired, not a frizzy picture showing all power and ground connections as you have them wired. Also links to technical information on each of the hardware devices will help. Can you also include some environmental information on where these are located and what the temperature extremes are?

Hi, yes. This is in a controlled lab environment. We are using the Adafruit Datalogger shield on a Quiic Sparkfun Redboard. The Redboard is connected to the Qwiic Scale via qwiic cable (I2C) connection, which is wired to a 100 g load cell. The whole apparatus is connected by power cable to a unit with usb ports, which is plugged into a regulated power supply.

Why brownout problems if you have regulated supplies? Schematic and links are needed. Also name your number 1 and 2 problems you list a log of different things. Include links to technical information on your power supplies. Also explain how voltage regulators distribute the power?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.