I2C 20x4 PCF8574 LCD output in fast loop: illegible

In a loop, where overcurrent on 5 modules must be detected fast (within a few tens of microseconds intervals), a 20x4 I2C display (with PCF8574 addon) must be refreshed every few seconds.
This is using F. Malpartida's LCD library.

However, when this code is omited from the measurement indexing loop,

  for (index = 0; index < 5; index++) {

then no output at all comes on the display.

    Serial.println(index);  // if this line is commentedthen no LCD output
    delay(50);  // if this line is commented then LCD output on last lines almost illegible 

If this last line is omitted, then the LCD displays the last lines virtually illegible.

Also, if the LCD display refresh rate ( slowTimer = 500; ) is increased above 500ms then there is no output on te LCD.

Basically, I need the current measurements to be performed fast, but the LCD updates may be made as slow as every few seconds.

What can be done to get

  1. fast current measurements
  2. LCD to display legibly
  3. LCD refresh rate may made be as slow as several seconds
#include<EEPROM.h>
#include <Wire.h>
#include <LCD.h> // will force using NewLiquidCrystal library
#include <LiquidCrystal_I2C.h>
#include <Auto485.h>
#include <CMRI.h>
#include <SPI.h>
#define CMRI_ADDR 0                  // select the CMRI node address
#define    DE_PIN 2                  // Arduino pin 2 -> MAX485 DE and RE pins
#define pot A7 // calibration pot attached to A7; value is read into calibrationValue
#define rstButton 8 // manual reset button attached to pin DIO8; value is read into rstButtonValue
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // I2C address 0x27 or 0x3F
bool rstButtonValue, ocAlarm, autoRstStatus, manualRstStatus, buttonState, lastButtonState = false; // reset button connected to DIO 8; ocAlarm = flag set when at least one booster is in overcurrent
bool eN1, eN2, eN3, eN4, eN5 = true; // digital outputs to enable booster
uint32_t lastDebounceTime = 0; // max value for analog read
uint32_t previousMillis, currentMillis, currentTotal, manualRstClear, slowTimerVar = 0; // delaying timer variables
float calibrationValue, currentNow, currentAvg, potReading, current1, current2, current3, current4, current5; // variables containing measured currents, and previous values
uint16_t analogReadInterval = 2; // interval in ms between subsequent reads from the same input
uint16_t readRepeat = 10; // number of consecutive repeat current measurements
uint16_t numberOfBooster = 5;
uint16_t index, repeat, i, j, k = 0; // index: currentMeasure array address pointer; repeat: current measurement variable
uint16_t automaticReset = 5; // number of automatic reset attempts
uint16_t autoRstTimer = 2000; // number of miliseconds between automatic reset attempts
uint16_t boosterNumber, x = 0; // array index from 0 to 4
uint16_t debounceDelay = 50; // manual reset button debounce delay
uint16_t manualRstClearDelay = 5000; // time after which manual reset flag is cleared; next short circuit will again be auto reset
uint16_t slowTimer = 500; // refresh time for LCD and reversing check for boosters
float currentMeasure[5] = {current1, current2, current3, current4, current5}; // array with measured currents
// bool stateRev[5] = {revState1, revState2, revState3, revState4, revState5}; // array with status of booster revering
bool enState[5] = {eN1, eN2, eN3, eN4, eN5}; // enable status
uint16_t enablePointer[5] = {3, 4, 5, 6, 7}; // digital output pins for enable
uint16_t analogReadPointer[5] = {0, 1, 2, 3, 6}; // analog pin numbers used for current reading
uint16_t reverserReadPointer[5] = {9, 10, 11, 12, 13}; // DIO output pins for booster reversing, commanded from C/MRI
float maxCurrent = 2.0; // this is the current for which the IBT-2 is set with current sense resistors to give 5V when this current is reached
float version = 1;
static char txt1[4];
static char txt2[4];
static char txt3[4];
static char txt4[4];
static char txt5[4];

void setup() {
  Serial.begin (115200);
  lcd.begin(20, 4);
  lcd.clear();
  lcd.backlight();//Power on the back light
  Serial.println("  Test started. Version v1");
  Serial.println();
  for (uint8_t i = 0; i < 5; i++) {
    pinMode(enablePointer[i], INPUT_PULLUP); // initialize pin as input wit pullu^p
    pinMode(enablePointer[i], OUTPUT); // then convert to output in HIGH state
  }
  for (i = 0; i < 5; i++) { // set enables states to true
    enState[i] = true;
  }
  for (uint8_t i = 0; i < 5; i++) {
    pinMode(reverserReadPointer[i], INPUT_PULLUP); // initialize pin as input wit pullu^p
    pinMode(reverserReadPointer[i], OUTPUT); // then convert to output in HIGH state
  }
  for (auto &i : analogReadPointer) {  //  auto range based for loops; this is identical to the previous 3 lines.
    pinMode(i, INPUT);  // currentMeasure are analog inputs; auto range based loop
  }
  pinMode(rstButton, INPUT_PULLUP);
  // pinMode(builtinLed, OUTPUT);
  lcd.setCursor(0, 0);
  lcd.print("   DCC Booster");
  lcd.setCursor(0, 1);
  lcd.print("  16/03/2022");
  lcd.setCursor(0, 2);
  lcd.print("  version ");
  lcd.print(version, 1);
  Serial.print("  16/3/2022 version ");
  Serial.println(version, 1);
  delay(500);
}

void loop() {
  if (millis - slowTimerVar > slowTimer) {  // first execution is immediately; execute LCD refresh, and booster reversing inputs check
    writeLCD();
    reverser();
    slowTimerVar = millis();
  }
  for (index = 0; index < 5; index++) {

    // Serial.print("execution of indexing (values 0 to 4), index value =  ");
    Serial.println(index);  // if this line is commentedthen no LCD output
    // Serial.println();
    delay(50);  // if this line is commented then LCD output on last lines almost illegible 

    
    currentMeasure[index] = actualMeasure(index); // read value of actualMeasure into array currentMeasure, pointer = index
    if (ocAlarm && !manualRstStatus) {
      autoRst(index);
    }
    if (ocAlarm && manualRstStatus) {
      manualRst(index);
    }
  }
  if (millis() - manualRstClear > manualRstClearDelay) {  // clear manual reset flag, from now again auto reset
    manualRstStatus = false;
  }
}
float actualMeasure(int i) { // i = value of index:
  // function to read measure from index = boosterNumber array address, and return value as float
  j = analogReadPointer[i]; // j = value at pointer address in this array
  repeat = 0;
  float currentMax = 0.0; // min value for analog read
  float currentMin = 1000000.0; // min value for analog read
  currentTotal = 0.0;
  while (repeat < readRepeat) {
    currentNow = analogRead(j); // here introduce MIN / MAX algorithm; readRepeat = 10, analogReadInterval = 2ms
    currentNow = analogRead(j); // second reading to get stabilised result
    if (currentNow < currentMin) currentMin = currentNow;
    if (currentNow >= currentMax) currentMax = currentNow;
    currentTotal += currentNow;
    repeat++;
    delayMicroseconds(10);
  }
  currentTotal -= (currentMin + currentMax);
  currentAvg = currentTotal / (readRepeat - 2);
  currentAvg = currentAvg / 1023 * maxCurrent; // this is when a voltage of 5V corresponds to maxCurrent A
  currentMeasure[i] = currentAvg;
  if (currentAvg > maxCurrent) {
    enState[i] = false; // disable output to booster i
    ocAlarm = true; // set alarm, at least one booster is in overcurrent
    digitalWrite (enablePointer[i], LOW); // set low eneable pin
  }
  return currentAvg;
}
uint16_t autoRst(int pointer) { // returns boolean status of autoreset
  for (k = 0; k < automaticReset; k++) {  // meerdere (= automaticReset) uitvoeren van pogingen tot automatische reset
    currentMillis = millis();  // why use currentMillis as intermediary, and not straight millis() ?
    if (currentMillis - previousMillis > autoRstTimer) {
      // reset relevant enable outputs to high
      enState[pointer] = true; // enable output to booster i
      ocAlarm = false; // reset alarm, at least one booster was in overcurrent, now restarted
      digitalWrite (enablePointer[i], HIGH); // set high enable pin

      // verify if current is ok now
      previousMillis = currentMillis;
    }
    // after automaticReset number of attempts set flag for manual reset, reset flag for autoreset
    manualRstStatus = true;
  }
}
uint16_t manualRst(int pointer) { // returns boolean status of manual reset
  // wait for button press to reset enable outputs
  // debounce: wait for status after button release
  // read the state of the switch into a local variable:
  // !!! RST_BTN goes from high to low when pressed !!! Adapt code below !!! ..done, 16/3/22
  rstButtonValue = digitalRead(rstButton);
  if (rstButtonValue == LOW && lastButtonState == HIGH) { // indien knop werd ingedrukt EN dit is opgaande flank, start debounce
    // reset the debouncing timer
    while (lastDebounceTime == 0) {
      lastDebounceTime = millis();
    }
    if ((millis() - lastDebounceTime) > debounceDelay) // uitvoeren na debounce
    {
      while (buttonState != lastButtonState) // uitvoeren zolang de knop nog ingedrukt is, en dus = 1.
        // Wat als de knop niet is/was ingedrukt: dan is buttonstate = lastButtonState en dus LOW, dus vervalt while
      {
        rstButtonValue = digitalRead (rstButton); // na debounce nogmaals inlezen op voorwaarde dat button nog steeds is ingedrukt
        // highState = HIGH;
      }
      lastButtonState = !rstButtonValue; // lastButtonState wordt 1 zodra knop gelost is; waat als buttonState niet hoog is geweest,
      // dus valse lezing? Dan geldt niet meer dat buttonState = HIGH dus if loop lijn 37 wordt niet meer uitgevoerd
    }
  }
  if (rstButtonValue == HIGH && lastButtonState == LOW) // enkel uitvoeren op neergaande flank EN nadat knop gelost werd EN na debounce
  {
    //hier de code die moet worden uitgevoerd als de reset knop gelost is
    manualRstClear = millis();
    lastButtonState = rstButtonValue; // alles terug naar begintoestand: 0
    lastDebounceTime = 0;
    enState[pointer] = true; // enable output to booster i
    ocAlarm = false; // reset alarm, at least one booster was in overcurrent, now restarted
    digitalWrite (enablePointer[i], HIGH); // set high enable pin
  }
}

void writeLCD() {
  dtostrf(currentMeasure[0], 3, 1, txt1);
  dtostrf(currentMeasure[1], 3, 1, txt2);
  dtostrf(currentMeasure[2], 3, 1, txt3);
  dtostrf(currentMeasure[3], 3, 1, txt4);
  dtostrf(currentMeasure[4], 3, 1, txt5);
  lcd.clear();
  lcd.backlight();//Power on the back light
  lcd.setCursor(0, 0);
  lcd.print(F(" B1  B2  B3  B4  B5"));
  lcd.setCursor (0, 1); // go to start of 2nd line
  // lcd.print("Current (A) :");
  // lcd.setCursor (0, 2); // go to start of 2nd line
  lcd.print(txt1);
  lcd.print(" ");
  lcd.print(txt2);
  lcd.print(" ");
  lcd.print(txt3);
  lcd.print(" ");
  lcd.print(txt4);
  lcd.print(" ");
  lcd.print(txt5);
  // lcd.print("A");
  lcd.setCursor (0, 2);
  lcd.print(F("Status :            "));
  lcd.setCursor (0, 3);
  lcd.print(" ");
  lcd.print(enState[0]);
  lcd.print("   ");
  lcd.print(enState[1]);
  lcd.print("   ");
  lcd.print(enState[2]);
  lcd.print("   ");
  lcd.print(enState[3]);
  lcd.print("   ");
  lcd.print(enState[4]);
  // delay(10);
}

void reverser() {
}

This should be easy using the BlinkWithoutDelay principle.

  • Use millis() and a period of the required length to do the timing.
  • When the period ends set a boolean to true.
  • Write to the LCD only when the boolean is true and
  • Have the code set the boolean to false at that time to prevent writing to the LCD until the period ends again

The limiting factor is the speed of the display. Several seconds sounds bad, though. Avoid using the clear() method and overwrite old text instead.
Attempt to separate the logic so the display activity does not block the critical parts of the code.

1 Like

That helps: only write those cells whose values may change. And the delay timer may be replaced by Serial.flush();

But this line still needs to be in the indexing for loop:

  for (index = 0; index < 5; index++) {
    Serial.println(index);  // if this line is commentedthen no LCD output
    Serial.flush();  // if this line is commented then no LCD output 

And increasing the slowTimer = 500; to higher values makes the LCD does not display any variable.

#include<EEPROM.h>
#include <Wire.h>
#include <LCD.h> // will force using NewLiquidCrystal library
#include <LiquidCrystal_I2C.h>
#include <Auto485.h>
#include <CMRI.h>
#include <SPI.h>
#define CMRI_ADDR 0                  // select the CMRI node address
#define    DE_PIN 2                  // Arduino pin 2 -> MAX485 DE and RE pins
#define pot A7 // calibration pot attached to A7; value is read into calibrationValue
#define rstButton 8 // manual reset button attached to pin DIO8; value is read into rstButtonValue
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // I2C address 0x27 or 0x3F
bool rstButtonValue, ocAlarm, autoRstStatus, manualRstStatus, buttonState, lastButtonState = false; // reset button connected to DIO 8; ocAlarm = flag set when at least one booster is in overcurrent
bool eN1, eN2, eN3, eN4, eN5 = true; // digital outputs to enable booster
uint32_t lastDebounceTime = 0; // max value for analog read
uint32_t previousMillis, currentMillis, currentTotal, manualRstClear, slowTimerVar = 0; // delaying timer variables
float calibrationValue, currentNow, currentAvg, potReading, current1, current2, current3, current4, current5; // variables containing measured currents, and previous values
uint16_t analogReadInterval = 2; // interval in ms between subsequent reads from the same input
uint16_t readRepeat = 10; // number of consecutive repeat current measurements
uint16_t numberOfBooster = 5;
uint16_t index, repeat, i, j, k = 0; // index: currentMeasure array address pointer; repeat: current measurement variable
uint16_t automaticReset = 5; // number of automatic reset attempts
uint16_t autoRstTimer = 2000; // number of miliseconds between automatic reset attempts
uint16_t boosterNumber, x = 0; // array index from 0 to 4
uint16_t debounceDelay = 50; // manual reset button debounce delay
uint16_t manualRstClearDelay = 5000; // time after which manual reset flag is cleared; next short circuit will again be auto reset
uint16_t slowTimer = 500; // refresh time for LCD and reversing check for boosters
float currentMeasure[5] = {current1, current2, current3, current4, current5}; // array with measured currents
// bool stateRev[5] = {revState1, revState2, revState3, revState4, revState5}; // array with status of booster revering
bool enState[5] = {eN1, eN2, eN3, eN4, eN5}; // enable status
uint16_t enablePointer[5] = {3, 4, 5, 6, 7}; // digital output pins for enable
uint16_t analogReadPointer[5] = {0, 1, 2, 3, 6}; // analog pin numbers used for current reading
uint16_t reverserReadPointer[5] = {9, 10, 11, 12, 13}; // DIO output pins for booster reversing, commanded from C/MRI
float maxCurrent = 2.0; // this is the current for which the IBT-2 is set with current sense resistors to give 5V when this current is reached
float version = 1;
static char txt1[4];
static char txt2[4];
static char txt3[4];
static char txt4[4];
static char txt5[4];

void setup() {
  Serial.begin (115200);
  lcd.begin(20, 4);
  lcd.clear();
  lcd.backlight();//Power on the back light
  Serial.println("  Test started. Version v1");
  Serial.println();
  for (uint8_t i = 0; i < 5; i++) {
    pinMode(enablePointer[i], INPUT_PULLUP); // initialize pin as input wit pullu^p
    pinMode(enablePointer[i], OUTPUT); // then convert to output in HIGH state
  }
  for (i = 0; i < 5; i++) { // set enables states to true
    enState[i] = true;
  }
  for (uint8_t i = 0; i < 5; i++) {
    pinMode(reverserReadPointer[i], INPUT_PULLUP); // initialize pin as input wit pullu^p
    pinMode(reverserReadPointer[i], OUTPUT); // then convert to output in HIGH state
  }
  for (auto &i : analogReadPointer) {  //  auto range based for loops; this is identical to the previous 3 lines.
    pinMode(i, INPUT);  // currentMeasure are analog inputs; auto range based loop
  }
  pinMode(rstButton, INPUT_PULLUP);
  // pinMode(builtinLed, OUTPUT);
  lcd.setCursor(0, 0);
  lcd.print("   DCC Booster");
  lcd.setCursor(0, 1);
  lcd.print("  16/03/2022");
  lcd.setCursor(0, 2);
  lcd.print("  version ");
  lcd.print(version, 1);
  Serial.print("  16/3/2022 version ");
  Serial.println(version, 1);
  delay(500);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F(" B1  B2  B3  B4  B5"));
  lcd.setCursor (0, 2);
  lcd.print(F("Status :"));
}

void loop() {
  if (millis - slowTimerVar > slowTimer) {  // first execution is immediately; execute LCD refresh, and booster reversing inputs check
    writeLCD();
    reverser();
    slowTimerVar = millis();
  }
  for (index = 0; index < 5; index++) {

    Serial.println(index);  // if this line is commentedthen no LCD output
    // delay(1);  // if this line is commented then LCD output on last lines almost illegible


    currentMeasure[index] = actualMeasure(index); // read value of actualMeasure into array currentMeasure, pointer = index
    if (ocAlarm && !manualRstStatus) {
      autoRst(index);
    }
    if (ocAlarm && manualRstStatus) {
      manualRst(index);
    }
  }
  if (millis() - manualRstClear > manualRstClearDelay) {  // clear manual reset flag, from now again auto reset
    manualRstStatus = false;
  }
}
float actualMeasure(int i) { // i = value of index:
  // function to read measure from index = boosterNumber array address, and return value as float
  j = analogReadPointer[i]; // j = value at pointer address in this array
  repeat = 0;
  float currentMax = 0.0; // min value for analog read
  float currentMin = 1000000.0; // min value for analog read
  currentTotal = 0.0;
  while (repeat < readRepeat) {
    currentNow = analogRead(j); // here introduce MIN / MAX algorithm; readRepeat = 10, analogReadInterval = 2ms
    currentNow = analogRead(j); // second reading to get stabilised result
    if (currentNow < currentMin) currentMin = currentNow;
    if (currentNow >= currentMax) currentMax = currentNow;
    currentTotal += currentNow;
    repeat++;
    delayMicroseconds(10);
  }
  currentTotal -= (currentMin + currentMax);
  currentAvg = currentTotal / (readRepeat - 2);
  currentAvg = currentAvg / 1023 * maxCurrent; // this is when a voltage of 5V corresponds to maxCurrent A
  currentMeasure[i] = currentAvg;
  if (currentAvg > maxCurrent) {
    enState[i] = false; // disable output to booster i
    ocAlarm = true; // set alarm, at least one booster is in overcurrent
    digitalWrite (enablePointer[i], LOW); // set low eneable pin
  }
  return currentAvg;
}
uint16_t autoRst(int pointer) { // returns boolean status of autoreset
  for (k = 0; k < automaticReset; k++) {  // meerdere (= automaticReset) uitvoeren van pogingen tot automatische reset
    currentMillis = millis();  // why use currentMillis as intermediary, and not straight millis() ?
    if (currentMillis - previousMillis > autoRstTimer) {
      // reset relevant enable outputs to high
      enState[pointer] = true; // enable output to booster i
      ocAlarm = false; // reset alarm, at least one booster was in overcurrent, now restarted
      digitalWrite (enablePointer[i], HIGH); // set high enable pin

      // verify if current is ok now
      previousMillis = currentMillis;
    }
    // after automaticReset number of attempts set flag for manual reset, reset flag for autoreset
    manualRstStatus = true;
  }
}
uint16_t manualRst(int pointer) { // returns boolean status of manual reset
  // wait for button press to reset enable outputs
  // debounce: wait for status after button release
  // read the state of the switch into a local variable:
  // !!! RST_BTN goes from high to low when pressed !!! Adapt code below !!! ..done, 16/3/22
  rstButtonValue = digitalRead(rstButton);
  if (rstButtonValue == LOW && lastButtonState == HIGH) { // indien knop werd ingedrukt EN dit is opgaande flank, start debounce
    // reset the debouncing timer
    while (lastDebounceTime == 0) {
      lastDebounceTime = millis();
    }
    if ((millis() - lastDebounceTime) > debounceDelay) // uitvoeren na debounce
    {
      while (buttonState != lastButtonState) // uitvoeren zolang de knop nog ingedrukt is, en dus = 1.
        // Wat als de knop niet is/was ingedrukt: dan is buttonstate = lastButtonState en dus LOW, dus vervalt while
      {
        rstButtonValue = digitalRead (rstButton); // na debounce nogmaals inlezen op voorwaarde dat button nog steeds is ingedrukt
        // highState = HIGH;
      }
      lastButtonState = !rstButtonValue; // lastButtonState wordt 1 zodra knop gelost is; waat als buttonState niet hoog is geweest,
      // dus valse lezing? Dan geldt niet meer dat buttonState = HIGH dus if loop lijn 37 wordt niet meer uitgevoerd
    }
  }
  if (rstButtonValue == HIGH && lastButtonState == LOW) // enkel uitvoeren op neergaande flank EN nadat knop gelost werd EN na debounce
  {
    //hier de code die moet worden uitgevoerd als de reset knop gelost is
    manualRstClear = millis();
    lastButtonState = rstButtonValue; // alles terug naar begintoestand: 0
    lastDebounceTime = 0;
    enState[pointer] = true; // enable output to booster i
    ocAlarm = false; // reset alarm, at least one booster was in overcurrent, now restarted
    digitalWrite (enablePointer[i], HIGH); // set high enable pin
  }
}

void writeLCD() {
  dtostrf(currentMeasure[0], 3, 1, txt1);
  dtostrf(currentMeasure[1], 3, 1, txt2);
  dtostrf(currentMeasure[2], 3, 1, txt3);
  dtostrf(currentMeasure[3], 3, 1, txt4);
  dtostrf(currentMeasure[4], 3, 1, txt5);
  // lcd.clear();
  // lcd.backlight();//Power on the back light
  // lcd.setCursor(0, 0);
  // lcd.print(F(" B1  B2  B3  B4  B5"));
  lcd.setCursor (1, 1);
  lcd.print(txt1);
  lcd.setCursor (5, 1);
  lcd.print(txt2);
  lcd.setCursor (9, 1);
  lcd.print(txt3);
  lcd.setCursor (13, 1);
  lcd.print(txt4);
  lcd.setCursor (17, 1);
  lcd.print(txt5);
  // lcd.setCursor (0, 2);
  // lcd.print(F("Status :"));
  lcd.setCursor (1, 3);
  lcd.print(enState[0]);
  lcd.setCursor (5, 3);
  lcd.print(enState[1]);
  lcd.setCursor (9, 3);
  lcd.print(enState[2]);
  lcd.setCursor (13, 3);
  lcd.print(enState[3]);
  lcd.setCursor (17, 3);
  lcd.print(enState[4]);
}

void reverser() {
}

With "several seconds" I mean there is no need to refresh the display faster than every several seconds.

See reply #2

It looks like you have the code to refresh the display at 500ms intervals.
If extending that period causes an empty display, it could be that logic which updates the variables which are to be displayed ( currentMeasure[ ] ) has simply cleaned them out in the meantime. Maybe always keep a copy of a valid value of currentMeasure[ ] for display purposes.

Edit
It may be that your problem is actually here:

static char txt1[4];
. . . 
dtostrf(currentMeasure[0], 3, 1, txt1);

That will be able to represent a value of no greater than 9.9 without overflowing.
What values are you expecting ?

This is what I could find for dtostrf():

char* dtostrf (double __val, signed char __width, unsigned char __prec, char * __s)
The dtostrf() function converts the double value passed in val into an ASCII representationthat will be stored under s. The caller is responsible for providing sufficient storage in s.

Conversion is done in the format '[-]d.ddd'. The minimum field width of the output string (including the possible '.' and the possible sign for negative values) is given in width, and prec determines the number of digits after the decimal sign. width is signed value, negative for left adjustment.

The dtostrf() function returns the pointer to the converted string s. 

@UKHeliBob the LCD refresh loop does use millis:

  if (millis - slowTimerVar > slowTimer) {  // first execution is immediately; execute LCD refresh, and booster reversing inputs check
    writeLCD();
    reverser();
    slowTimerVar = millis();
  }

If this means "any overcurrent that occurs at any time must be detected within a few tens of microseconds", your project as it is implemented is doomed. Any time you run any LCD code or even your utility functions, the current sensing inputs will be ignored.

2 Likes

After reading the first sentence of this topic, I had the same feeling as aarg.
Do you want to detect overcurrent all the time or only now and then ?

The fmalpartida LCD library: https://github.com/fmalpartida/New-LiquidCrystal.
Did you mention which Arduino board you use ?

Some Arduino boards can set analog channel(s) as a comparator.
It might be possible to do a analogRead() in a interrupt routine.
It is also possible to use a I2C LCD display with software I2C, so the display does not use any interrupts.
There are options and possibilities, but I don't know what is best for your project. Can you tell more about your project ? Please give us a broader view. Where does my "railroad" feeling comes from ?

Right. I discard the LCD, indeed a bottleneck.

16MHz Pro Mini.
Interrupts: ok if externally driven events, but here there is an internal need for constant and fast analog data readings, no external trigger. Anything that causes delays in these readings is out. I had hoped there might be a way to prevent I2C LCD writing from holding things up, not so.
Railroad feeling: well anything were DC power sources need to be monitored I guess? :smiley:

You haven't provided any specifications for the current monitoring. What is the exact maximum overcurrent detection delay? Also is the set point fixed? Also do you have custom power supply hardware? If so, you should build the protection into it.

I think a possible workaround could be that I get LCD updates once every 5 or 10 seconds.

Given an update to the I2C 20x4 LCD would take around 50ms for a full frame with a good library (ref: bperrybap LCD lib ) then it would take a bit more with the malpartida library, but less if only 20 cells out of 20x4 cells need an update.
Supposing it would still take 50ms for each update, if this is only once every 10.000ms I guess I would take the chance missing an overcurrent by 50ms given the occurence for overcurrents would be once an hour.

This answer is not presented in good faith. It's a reasonable and common question to ask what a project is about, when options are limited and requirements become stringent. Please just explain what this system is, and tell us more about the basic (i.e. not implementation) requirements.

FYI I am a model railroader, and I don't care who know... :wink:

If you can miss an overcurrent for 50ms every hour, why can't you always miss it for 50ms? It makes no sense. Surely you don't want to gamble

I am sorry if I offended anyone, that was not my intention, rather in jest; yes this is for a project using a David Bodnar idea with IBT-2 boosters. He did it with one, I made design with 5 boosters.
I usually do not boast about my hobbies or pass times, sorry for this misinterpretation.

Great, there are a few questions left to answer... telling us what the *** you are doing, is not boasting.

I guess this is statistics; firstly missing an overcurrent by 50ms event once in a session where the chance is 50/10000/(number of seconds between possible overcurrent events) really is a negligible amount.
Secondly residential circuit breakers for 30mA differential current trip between 50 and 120ms.

If you gave us more detailed information, we could explore reasonable and creative solutions like using the analog comparator with an interrupt as mentioned above. But it's impossible to recommend or suggest a good implementation with such sketchy replies. Frankly, I've lost patience and I'm out of here.